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 : v4
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__ = "4.0.2"
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 
210  self.function = {}
211 
212 
234  self.inputInfo = {}
235 
236 
242  self.outputInfo = {}
243 
244 
246  self.designSurfaces = ["ALL_OPENFOAM_WALL_PATCHES"]
247 
248  # *********************************************************************************************
249  # ****************************** Intermediate Options *****************************************
250  # *********************************************************************************************
251 
252 
297  self.fvSource = {}
298 
299 
300  self.adjEqnSolMethod = "Krylov"
301 
302 
304  self.dynamicMesh = {
305  "active": False,
306  "mode": "rotation",
307  "center": [0.25, 0.0, 0.0],
308  "axis": "z",
309  "omega": 0.1,
310  }
311 
312 
318  "UMax": 1000.0,
319  "UMin": -1000.0,
320  "pMax": 500000.0,
321  "pMin": 20000.0,
322  "p_rghMax": 500000.0,
323  "p_rghMin": 20000.0,
324  "eMax": 500000.0,
325  "eMin": 100000.0,
326  "TMax": 1000.0,
327  "TMin": 100.0,
328  "hMax": 500000.0,
329  "hMin": 100000.0,
330  "DMax": 1e16,
331  "DMin": -1e16,
332  "rhoMax": 5.0,
333  "rhoMin": 0.2,
334  "nuTildaMax": 1e16,
335  "nuTildaMin": 1e-16,
336  "kMax": 1e16,
337  "kMin": 1e-16,
338  "omegaMax": 1e16,
339  "omegaMin": 1e-16,
340  "epsilonMax": 1e16,
341  "epsilonMin": 1e-16,
342  "ReThetatMax": 1e16,
343  "ReThetatMin": 1e-16,
344  "gammaIntMax": 1e16,
345  "gammaIntMin": 1e-16,
346  }
347 
348 
350  self.discipline = "aero"
351 
352 
355  "State": 1.0e-6,
356  }
357 
358 
361 
362 
366  "mode": "None",
367  "PCMatPrecomputeInterval": 100,
368  "PCMatUpdateInterval": 1,
369  "reduceIO": True,
370  "additionalOutput": ["None"],
371  "readZeroFields": True,
372  }
373 
374 
380  self.adjPCLag = 10000
381 
382 
389  self.useAD = {"mode": "reverse", "dvName": "None", "seedIndex": -9999}
390 
391 
396  self.useConstrainHbyA = True
397 
398 
406  "active": False,
407  # "model1": {
408  # "modelType": "neuralNetwork",
409  # "inputNames": ["PoD", "VoS", "PSoSS", "chiSA"],
410  # "outputName": "betaFINuTilda",
411  # "hiddenLayerNeurons": [20, 20],
412  # "inputShift": [0.0],
413  # "inputScale": [1.0],
414  # "outputShift": 1.0,
415  # "outputScale": 1.0,
416  # "outputUpperBound": 1e1,
417  # "outputLowerBound": -1e1,
418  # "activationFunction": "sigmoid", # other options are relu and tanh
419  # "printInputInfo": True,
420  # "defaultOutputValue": 1.0,
421  # },
422  # "model2": {
423  # "modelType": "radialBasisFunction",
424  # "inputNames": ["KoU2", "ReWall", "CoP", "TauoK"],
425  # "outputName": "betaFIOmega",
426  # "nRBFs": 50,
427  # "inputShift": [0.0],
428  # "inputScale": [1.0],
429  # "outputShift": 1.0,
430  # "outputScale": 1.0,
431  # "outputUpperBound": 1e1,
432  # "outputLowerBound": -1e1,
433  # "printInputInfo": True,
434  # "defaultOutputValue": 1.0,
435  # },
436  }
437 
438 
441  self.useMeanStates = False
442 
443  # *********************************************************************************************
444  # ************************************ Advance Options ****************************************
445  # *********************************************************************************************
446 
447 
450 
451 
452  self.printDAOptions = True
453 
454 
455  self.debug = False
456 
457 
461  self.writeJacobians = ["None"]
462 
463 
465  self.printInterval = 100
466 
467 
469 
470 
472  self.primalMinResTolDiff = 1.0e2
473 
474 
476  self.adjUseColoring = True
477 
478 
481  self.adjEqnOption = {
482  "globalPCIters": 0,
483  "asmOverlap": 1,
484  "localPCIters": 1,
485  "jacMatReOrdering": "rcm",
486  "pcFillLevel": 1,
487  "gmresMaxIters": 1000,
488  "gmresRestart": 1000,
489  "gmresRelTol": 1.0e-6,
490  "gmresAbsTol": 1.0e-14,
491  "gmresTolDiff": 1.0e2,
492  "useNonZeroInitGuess": False,
493  "useMGSO": False,
494  "printInfo": 1,
495  "fpMaxIters": 1000,
496  "fpRelTol": 1e-6,
497  "fpMinResTolDiff": 1.0e2,
498  "fpPCUpwind": False,
499  "dynAdjustTol": False,
500  }
501 
502 
504  "URes",
505  "pRes",
506  "p_rghRes",
507  "nuTildaRes",
508  "phiRes",
509  "TRes",
510  "DRes",
511  "kRes",
512  "omegaRes",
513  "epsilonRes",
514  ]
515 
516 
520  "pRes": 2,
521  "phiRes": 1,
522  "URes": 2,
523  "TRes": 2,
524  "nuTildaRes": 2,
525  "kRes": 2,
526  "epsilonRes": 2,
527  "omegaRes": 2,
528  "p_rghRes": 2,
529  "DRes": 2,
530  "gammaIntRes": 2,
531  "ReThetatRes": 2,
532  }
533 
534 
536  self.jacLowerBounds = {
537  "dRdW": 1.0e-30,
538  "dRdWPC": 1.0e-30,
539  }
540 
541 
543 
544 
548  "method": "scotch",
549  "simpleCoeffs": {"n": [2, 2, 1], "delta": 0.001},
550  "preservePatches": ["None"],
551  "singleProcessorFaceSets": ["None"],
552  "args": ["None"],
553  }
554 
555 
557  self.adjStateOrdering = "state"
558 
559 
561  "maxAspectRatio": 1000.0,
562  "maxNonOrth": 70.0,
563  "maxSkewness": 4.0,
564  "maxIncorrectlyOrientedFaces": 0,
565  }
566 
567 
572  self.writeSensMap = ["NONE"]
573 
574 
575  self.writeDeformedFFDs = False
576 
577 
579 
580 
581  self.writeAdjointFields = False
582 
583 
585 
586 
591  self.writeMinorIterations = False
592 
593 
595  self.primalMinIters = 1
596 
597 
598  self.tensorflow = {
599  "active": False,
600  # "model1": {
601  # "predictBatchSize": 1000
602  # }
603  }
604 
605 
606  self.wallDistanceMethod = "default"
607 
608 
617  self.unsteadyCompOutput = {}
618 
619 
620 class PYDAFOAM(object):
621  """
622  Main class for pyDAFoam
623 
624  Parameters
625  ----------
626 
627  comm : mpi4py communicator
628  An optional argument to pass in an external communicator.
629 
630  options : dictionary
631  The list of options to use with pyDAFoam.
632 
633  """
634 
635  def __init__(self, comm=None, options=None):
636  """
637  Initialize class members
638  """
639 
640  assert not os.getenv("WM_PROJECT") is None, "$WM_PROJECT not found. Please source OpenFOAM-v1812/etc/bashrc"
641 
642  self.version = __version__
643 
644  Info(" ")
645  Info("-------------------------------------------------------------------------------")
646  Info("| DAFoam v%s |" % self.version)
647  Info("-------------------------------------------------------------------------------")
648  Info(" ")
649 
650  # name
651  self.name = "PYDAFOAM"
652 
653  # register solver names and set their types
654  self._solverRegistry()
655 
656  # initialize options for adjoints
657  self._initializeOptions(options)
658 
659  # initialize comm for parallel communication
660  self._initializeComm(comm)
661 
662  # Initialize families
663  self.families = OrderedDict()
664 
665  # Default it to fault, after calling setSurfaceCoordinates, set it to true
666  self._updateGeomInfo = False
667 
668  # Use double data type: 'd'
669  self.dtype = "d"
670 
671  # run decomposePar for parallel runs
672  self.runDecomposePar()
673 
674  # initialize the pySolvers
676  self._initSolver()
677 
678  # set the primal boundary condition after initializing the solver
680 
681  # initialize the number of primal and adjoint calls
682  self.nSolvePrimals = 1
683  self.nSolveAdjoints = 1
684 
685  # flags for primal and adjoint failure
686  self.primalFail = 0
687  self.adjointFail = 0
688 
689  # initialize mesh information and read grids
690  self._readMeshInfo()
691 
692  # check if the combination of options is valid.
693  self._checkOptions()
694 
695  # get the reduced point connectivities for the base patches in the mesh
697 
698  # Add a couple of special families.
699  self.allSurfacesGroup = "allSurfaces"
701 
702  self.allWallsGroup = "allWalls"
703  self.addFamilyGroup(self.allWallsGroup, self.wallList)
704 
705  # Set the design surfaces group
706  self.designSurfacesGroup = "designSurfaces"
707  if "ALL_OPENFOAM_WALL_PATCHES" in self.getOption("designSurfaces"):
709  else:
710  self.addFamilyGroup(self.designSurfacesGroup, self.getOption("designSurfaces"))
711 
712  # By Default we don't have an external mesh object or a
713  # geometric manipulation object
714  self.mesh = None
715 
716  # preconditioner matrix
717  self.dRdWTPC = None
718 
719  # a KSP object which may be used outside of the pyDAFoam class
720  self.ksp = None
721 
722  # a flag used in deformDynamicMesh for runMode=runOnce
724 
725  if self.getOption("tensorflow")["active"]:
726  TensorFlowHelper.options = self.getOption("tensorflow")
727  TensorFlowHelper.initialize()
728  # pass this helper function to the C++ layer
729  self.solver.initTensorFlowFuncs(
730  TensorFlowHelper.predict, TensorFlowHelper.calcJacVecProd, TensorFlowHelper.setModelName
731  )
732  self.solverAD.initTensorFlowFuncs(
733  TensorFlowHelper.predict, TensorFlowHelper.calcJacVecProd, TensorFlowHelper.setModelName
734  )
735 
736  if self.getOption("printDAOptions"):
737  self.solver.printAllOptions()
738 
739  Info("pyDAFoam initialization done!")
740 
741  return
742 
743  def _solverRegistry(self):
744  """
745  Register solver names and set their types. For a new solver, first identify its
746  type and add their names to the following dict
747  """
748 
749  self.solverRegistry = {
750  "Incompressible": ["DASimpleFoam", "DAPimpleFoam", "DAPimpleDyMFoam"],
751  "Compressible": ["DARhoSimpleFoam", "DARhoSimpleCFoam", "DATurboFoam", "DARhoPimpleFoam"],
752  "Solid": ["DASolidDisplacementFoam", "DAHeatTransferFoam"],
753  }
754 
755  def __call__(self):
756  """
757  Solve the primal
758  """
759 
760  Info("Running Primal Solver %03d" % self.nSolvePrimals)
761 
763 
764  # self.primalFail: if the primal solution fails, assigns 1, otherwise 0
765  self.primalFail = 0
766  if self.getOption("useAD")["mode"] == "forward":
767  self.primalFail = self.solverAD.solvePrimal()
768  else:
769  self.primalFail = self.solver.solvePrimal()
770 
771  if self.getOption("writeMinorIterations"):
772  self.renameSolution(self.nSolvePrimals)
773 
774  self.nSolvePrimals += 1
775 
776  return
777 
778  def _getDefOptions(self):
779  """
780  Setup default options
781 
782  Returns
783  -------
784 
785  defOpts : dict
786  All the DAFoam options.
787  """
788 
789  # initialize the DAOPTION object
790  daOption = DAOPTION()
791 
792  defOpts = {}
793 
794  # assign all the attribute of daOptoin to defOpts
795  for key in vars(daOption):
796  value = getattr(daOption, key)
797  defOpts[key] = [type(value), value]
798 
799  return defOpts
800 
801  def _checkOptions(self):
802  """
803  Check if the combination of options are valid.
804  NOTE: we should add all possible checks here!
805  """
806 
807  if not self.getOption("useAD")["mode"] in ["reverse", "forward"]:
808  raise Error("useAD->mode only supports reverse, or forward!")
809 
810  if "NONE" not in self.getOption("writeSensMap"):
811  if not self.getOption("useAD")["mode"] in ["reverse"]:
812  raise Error("writeSensMap is only compatible with useAD->mode=reverse")
813 
814  if self.getOption("adjEqnSolMethod") == "fixedPoint":
815  # for the fixed-point adjoint, we should not normalize the states and residuals
816  if self.comm.rank == 0:
817  print("Fixed-point adjoint mode detected. Unset normalizeStates and normalizeResiduals...")
818 
819  # force the normalize states to be an empty dict
820  if len(self.getOption("normalizeStates")) > 0:
821  raise Error("Please do not set any normalizeStates for the fixed-point adjoint!")
822  # force the normalize residuals to be None; don't normalize any residuals
823  self.setOption("normalizeResiduals", ["None"])
824  # update this option to the C++ layer
825  self.updateDAOption()
826 
827  if self.getOption("discipline") not in ["aero", "thermal"]:
828  raise Error("discipline: %s not supported. Options are: aero or thermal" % self.getOption("discipline"))
829 
830  # check the patchNames from primalBC dict
831  primalBCDict = self.getOption("primalBC")
832  for bcKey in primalBCDict:
833  try:
834  patches = primalBCDict[bcKey]["patches"]
835  except Exception:
836  continue
837  for patchName in patches:
838  if patchName not in self.boundaries.keys():
839  raise Error(
840  "primalBC-%s-patches-%s is not valid. Please use a patchName from the boundaries list: %s"
841  % (bcKey, patchName, self.boundaries.keys())
842  )
843 
844  # check the patch names from function dict
845  functionDict = self.getOption("function")
846  for objKey in functionDict:
847  try:
848  patches = functionDict[objKey]["patches"]
849  except Exception:
850  continue
851  for patchName in patches:
852  if patchName not in self.boundaries.keys():
853  raise Error(
854  "function-%s-patches-%s is not valid. Please use a patchName from the boundaries list: %s"
855  % (objKey, patchName, self.boundaries.keys())
856  )
857 
858  # check other combinations...
859 
861  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
863  else:
865 
866  def writeAdjointFields(self, function, writeTime, psi):
867  """
868  Write the adjoint variables in OpenFOAM field format for post-processing
869  """
870 
871  if self.getOption("writeAdjointFields"):
872  if len(self.getOption("function").keys()) > 1:
873  raise Error("writeAdjointFields supports only one function, while multiple are defined!")
874  self.solver.writeAdjointFields(function, writeTime, psi)
875 
876  def evalFunctions(self, funcs):
877  """
878  Evaluate the desired functions given in iterable object,
879 
880  Examples
881  --------
882  >>> funcs = {}
883  >>> CFDsolver()
884  >>> CFDsolver.evalFunctions(funcs)
885  >>> # Result will look like:
886  >>> # {'CL':0.501, 'CD':0.02750}
887  """
888 
889  for funcName in list(self.getOption("function").keys()):
890  # call self.solver.getFunctionValue to get the functionValue from
891  # the DASolver
892  if self.getOption("useAD")["mode"] == "forward":
893  functionValue = self.solverAD.getTimeOpFuncVal(funcName)
894  else:
895  functionValue = self.solver.getTimeOpFuncVal(funcName)
896  funcs[funcName] = functionValue
897 
898  return
899 
900  def addFamilyGroup(self, groupName, families):
901  """
902  Add a custom grouping of families called groupName. The groupName
903  must be distinct from the existing families. All families must
904  in the 'families' list must be present in the mesh file.
905  Parameters
906  ----------
907  groupName : str
908  User-supplied custom name for the family groupings
909  families : list
910  List of string. Family names to combine into the family group
911  """
912 
913  # Do some error checking
914  if groupName in self.families:
915  raise Error(
916  "The specified groupName '%s' already exists in the mesh file or has already been added." % groupName
917  )
918 
919  # We can actually allow for nested groups. That is, an entry
920  # in families may already be a group added in a previous call.
921  indices = []
922  for fam in families:
923  if fam not in self.families:
924  raise Error(
925  "The specified family '%s' for group '%s', does "
926  "not exist in the mesh file or has "
927  "not already been added. The current list of "
928  "families (original and grouped) is: %s" % (fam, groupName, repr(self.families.keys()))
929  )
930 
931  indices.extend(self.families[fam])
932 
933  # It is very important that the list of families is sorted
934  # because in fortran we always use a binary search to check if
935  # a famID is in the list.
936  self.families[groupName] = sorted(np.unique(indices))
937 
938  def setMesh(self, mesh):
939  """
940  Set the mesh object to the aero_solver to do geometric deformations
941  Parameters
942  ----------
943  mesh : MBMesh or USMesh object
944  The mesh object for doing the warping
945  """
946 
947  # Store a reference to the mesh
948  self.mesh = mesh
949 
950  # Setup External Warping with volume indices
951  meshInd = self.getSolverMeshIndices()
952  self.mesh.setExternalMeshIndices(meshInd)
953 
954  # Set the surface the user has supplied:
955  conn, faceSizes = self.getSurfaceConnectivity(self.allWallsGroup)
956  pts = self.getSurfaceCoordinates(self.allWallsGroup)
957  self.mesh.setSurfaceDefinition(pts, conn, faceSizes)
958 
959  def getSurfaceConnectivity(self, groupName=None):
960  """
961  Return the connectivity of the coordinates at which the forces (or tractions) are
962  defined. This is the complement of getForces() which returns
963  the forces at the locations returned in this routine.
964 
965  Parameters
966  ----------
967  groupName : str
968  Group identifier to get only forces cooresponding to the
969  desired group. The group must be a family or a user-supplied
970  group of families. The default is None which corresponds to
971  all wall-type surfaces.
972  """
973 
974  if groupName is None:
975  groupName = self.allWallsGroup
976 
977  # loop over the families in this group and populate the connectivity
978  famInd = self.families[groupName]
979  conn = []
980  faceSizes = []
981 
982  pointOffset = 0
983  for Ind in famInd:
984  # select the face from the basic families
985  name = self.basicFamilies[Ind]
986 
987  # get the size of this
988  bc = self.boundaries[name]
989  nPts = len(bc["indicesRed"])
990 
991  # get the number of reduced faces associated with this boundary
992  nFace = len(bc["facesRed"])
993 
994  # check that this isn't an empty boundary
995  if nFace > 0:
996  # loop over the faces and add them to the connectivity and faceSizes array
997  for iFace in range(nFace):
998  face = copy.copy(bc["facesRed"][iFace])
999  for i in range(len(face)):
1000  face[i] += pointOffset
1001  conn.extend(face)
1002  faceSizes.append(len(face))
1003 
1004  pointOffset += nPts
1005 
1006  return conn, faceSizes
1007 
1008  def getTriangulatedMeshSurface(self, groupName=None, **kwargs):
1009  """
1010  This function returns a trianguled verision of the surface
1011  mesh on all processors. The intent is to use this for doing
1012  constraints in DVConstraints.
1013  Returns
1014  -------
1015  surf : list
1016  List of points and vectors describing the surface. This may
1017  be passed directly to DVConstraint setSurface() function.
1018  """
1019 
1020  if groupName is None:
1021  groupName = self.allWallsGroup
1022 
1023  # Obtain the points and connectivity for the specified
1024  # groupName
1025  pts = self.comm.allgather(self.getSurfaceCoordinates(groupName, **kwargs))
1026  conn, faceSizes = self.getSurfaceConnectivity(groupName)
1027  conn = np.array(conn).flatten()
1028  conn = self.comm.allgather(conn)
1029  faceSizes = self.comm.allgather(faceSizes)
1030 
1031  # Triangle info...point and two vectors
1032  p0 = []
1033  v1 = []
1034  v2 = []
1035 
1036  # loop over the faces
1037  for iProc in range(len(faceSizes)):
1038 
1039  connCounter = 0
1040  for iFace in range(len(faceSizes[iProc])):
1041  # Get the number of nodes on this face
1042  faceSize = faceSizes[iProc][iFace]
1043  faceNodes = conn[iProc][connCounter : connCounter + faceSize]
1044 
1045  # Start by getting the centerpoint
1046  ptSum = [0, 0, 0]
1047  for i in range(faceSize):
1048  # idx = ptCounter+i
1049  idx = faceNodes[i]
1050  ptSum += pts[iProc][idx]
1051 
1052  avgPt = ptSum / faceSize
1053 
1054  # Now go around the face and add a triangle for each adjacent pair
1055  # of points. This assumes an ordered connectivity from the
1056  # meshwarping
1057  for i in range(faceSize):
1058  idx = faceNodes[i]
1059  p0.append(avgPt)
1060  v1.append(pts[iProc][idx] - avgPt)
1061  if i < (faceSize - 1):
1062  idxp1 = faceNodes[i + 1]
1063  v2.append(pts[iProc][idxp1] - avgPt)
1064  else:
1065  # wrap back to the first point for the last element
1066  idx0 = faceNodes[0]
1067  v2.append(pts[iProc][idx0] - avgPt)
1068 
1069  # Now increment the connectivity
1070  connCounter += faceSize
1071 
1072  return [p0, v1, v2]
1073 
1074  def printFamilyList(self):
1075  """
1076  Print a nicely formatted dictionary of the family names
1077  """
1078  Info(self.families)
1079 
1080  def _initializeOptions(self, options):
1081  """
1082  Initialize the options passed into pyDAFoam
1083 
1084  Parameters
1085  ----------
1086 
1087  options : dictionary
1088  The list of options to use with pyDAFoam.
1089  """
1090 
1091  # If 'options' is None raise an error
1092  if options is None:
1093  raise Error("The 'options' keyword argument must be passed pyDAFoam.")
1094 
1095  # set immutable options that users should not change during the optimization
1097 
1098  # Load all the option information:
1100 
1101  # we need to adjust the default p primalValueBounds for incompressible solvers
1102  if options["solverName"] in self.solverRegistry["Incompressible"]:
1103  self.defaultOptions["primalVarBounds"][1]["pMin"] = -50000.0
1104  self.defaultOptions["primalVarBounds"][1]["pMax"] = 50000.0
1105  self.defaultOptions["primalVarBounds"][1]["p_rghMin"] = -50000.0
1106  self.defaultOptions["primalVarBounds"][1]["p_rghMax"] = 50000.0
1107 
1108  # Set options based on defaultOptions
1109  # we basically overwrite defaultOptions with the given options
1110  # first assign self.defaultOptions to self.options
1111  self.options = OrderedDict()
1112  for key in self.defaultOptions:
1113  if len(self.defaultOptions[key]) != 2:
1114  raise Error(
1115  "key %s has wrong format! \
1116  Example: {'iters' : [int, 1]}"
1117  % key
1118  )
1119  self.options[key] = self.defaultOptions[key]
1120  # now set options to self.options
1121  for key in options:
1122  self._initOption(key, options[key])
1123 
1124  return
1125 
1126  def _initializeComm(self, comm):
1127  """
1128  Initialize MPI COMM and setup parallel flags
1129  """
1130 
1131  # Set the MPI Communicators and associated info
1132  if comm is None:
1133  comm = MPI.COMM_WORLD
1134  self.comm = comm
1135 
1136  # Check whether we are running in parallel
1137  nProc = self.comm.size
1138  self.parallel = False
1139  if nProc > 1:
1140  self.parallel = True
1141 
1142  # Save the rank and number of processors
1143  self.rank = self.comm.rank
1144  self.nProcs = self.comm.size
1145 
1146  # Setup the parallel flag for OpenFOAM executives
1147  self.parallelFlag = ""
1148  if self.parallel:
1149  self.parallelFlag = "-parallel"
1150 
1151  return
1152 
1154  """
1155  Deform the dynamic mesh and save to them to the disk
1156  """
1157 
1158  if not self.getOption("dynamicMesh")["active"]:
1159  return
1160 
1161  Info("Deforming dynamic mesh")
1162 
1163  # if we do not have the volCoord as the input, we need to run this only once
1164  # otherwise, we need to deform the dynamic mesh for each primal solve
1165  if self.solver.hasVolCoordInput() == 0:
1166  # if the mesh has been deformed, return
1167  if self.dynamicMeshDeformed == 1:
1168  return
1169 
1170  mode = self.getOption("dynamicMesh")["mode"]
1171 
1172  deltaT = self.solver.getDeltaT()
1173 
1174  endTime = self.solver.getEndTime()
1175  endTimeIndex = round(endTime / deltaT)
1176  nLocalPoints = self.solver.getNLocalPoints()
1177 
1178  if mode == "rotation":
1179  center = self.getOption("dynamicMesh")["center"]
1180  axis = self.getOption("dynamicMesh")["axis"]
1181  omega = self.getOption("dynamicMesh")["omega"]
1182 
1183  # always get the initial mesh from OF layer
1184  points0 = np.zeros(nLocalPoints * 3)
1185  self.solver.getOFMeshPoints(points0)
1186  # NOTE: we also write the mesh point for t = 0
1187  self.solver.writeMeshPoints(points0, 0.0)
1188 
1189  # do a for loop to incrementally deform the mesh by a deltaT
1190  points = np.reshape(points0, (-1, 3))
1191  for i in range(1, endTimeIndex + 1):
1192  t = i * deltaT
1193  dTheta = omega * deltaT
1194  dCosTheta = np.cos(dTheta)
1195  dSinTheta = np.sin(dTheta)
1196 
1197  for pointI in range(nLocalPoints):
1198 
1199  if axis == "z":
1200  xTemp = points[pointI][0] - center[0]
1201  yTemp = points[pointI][1] - center[1]
1202 
1203  points[pointI][0] = dCosTheta * xTemp - dSinTheta * yTemp + center[0]
1204  points[pointI][1] = dSinTheta * xTemp + dCosTheta * yTemp + center[1]
1205  else:
1206  raise Error("axis not valid! Options are: z")
1207 
1208  pointsWrite = points.flatten()
1209  self.solver.writeMeshPoints(pointsWrite, t)
1210  else:
1211  raise Error("mode not valid! Options are: rotation")
1212 
1213  # reset the time
1214  self.solver.setTime(0.0, 0)
1215  self.dynamicMeshDeformed = 1
1216 
1217  def readDynamicMeshPoints(self, timeVal, deltaT, timeIndex, ddtSchemeOrder):
1218  """
1219  Read the dynamic mesh points saved in the folders 0.001, 0.002
1220  NOTE: if the backward scheme is used we need to read the mesh
1221  for 3 time levels to get the correct V0, V00 etc
1222  NOTE: setting the proper time index is critical because the fvMesh
1223  will use timeIndex to calculate meshPhi, V0 etc
1224  """
1225  if timeVal < deltaT:
1226  raise Error("timeVal not valid")
1227 
1228  if ddtSchemeOrder == 1:
1229  # no special treatment
1230  pass
1231  elif ddtSchemeOrder == 2:
1232  # need to read timeVal - 2*deltaT
1233  time_2 = max(timeVal - 2 * deltaT, 0.0)
1234  # NOTE: the index can go to negative, just to force the fvMesh to update V0, V00 etc
1235  index_2 = timeIndex - 2
1236  self.solver.setTime(time_2, index_2)
1237  self.solver.readMeshPoints(time_2)
1238  self.solverAD.setTime(time_2, index_2)
1239  self.solverAD.readMeshPoints(time_2)
1240  else:
1241  raise Error("ddtSchemeOrder not supported")
1242 
1243  # read timeVal - deltaT points
1244  time_1 = max(timeVal - deltaT, 0.0)
1245  index_1 = timeIndex - 1
1246  self.solver.setTime(time_1, index_1)
1247  self.solver.readMeshPoints(time_1)
1248  self.solverAD.setTime(time_1, index_1)
1249  self.solverAD.readMeshPoints(time_1)
1250  # read timeVal points
1251  self.solver.setTime(timeVal, timeIndex)
1252  self.solver.readMeshPoints(timeVal)
1253  self.solverAD.setTime(timeVal, timeIndex)
1254  self.solverAD.readMeshPoints(timeVal)
1255 
1256  def readStateVars(self, timeVal, deltaT):
1257  """
1258  Read the state variables in to OpenFOAM's state fields
1259  """
1260 
1261  # read current time
1262  self.solver.readStateVars(timeVal, 0)
1263  self.solverAD.readStateVars(timeVal, 0)
1264 
1265  # read old time
1266  t0 = timeVal - deltaT
1267  self.solver.readStateVars(t0, 1)
1268  self.solverAD.readStateVars(t0, 1)
1269 
1270  # read old old time
1271  t00 = timeVal - 2 * deltaT
1272  self.solver.readStateVars(t00, 2)
1273  self.solverAD.readStateVars(t00, 2)
1274 
1275  # assign the state from OF field to wVec so that the wVec
1276  # is update to date for unsteady adjoint
1277  # self.solver.ofField2StateVec(self.wVec)
1278 
1279  def set_solver_input(self, inputs, DVGeo=None):
1280  """
1281  Set solver input. If it is forward mode, we also set the seeds
1282  """
1283  inputDict = self.getOption("inputInfo")
1284 
1285  for inputName in list(inputDict.keys()):
1286  # this input is attached to solver comp
1287  if "solver" in inputDict[inputName]["components"]:
1288  inputType = inputDict[inputName]["type"]
1289  input = inputs[inputName]
1290  inputSize = len(input)
1291  seeds = np.zeros(inputSize)
1292  if self.getOption("useAD")["mode"] == "forward":
1293  if inputType == "volCoord":
1294  if self.getOption("useAD")["dvName"] not in list(inputDict.keys()):
1295  seeds = self.calcFFD2XvSeeds(DVGeo)
1296  else:
1297  if inputName == self.getOption("useAD")["dvName"]:
1298  seedIndex = self.getOption("useAD")["seedIndex"]
1299  seeds[seedIndex] = 1.0
1300 
1301  # here we need to update the solver input for both solver and solverAD
1302  self.solver.setSolverInput(inputName, inputType, inputSize, input, seeds)
1303  self.solverAD.setSolverInput(inputName, inputType, inputSize, input, seeds)
1304 
1305  def calcFFD2XvSeeds(self, DVGeo=None):
1306  # Calculate the FFD2XvSeed array:
1307  # Given a FFD seed xDvDot, run pyGeo and IDWarp and propagate the seed to Xv seed xVDot:
1308  # xSDot = \\frac{dX_{S}}{dX_{DV}}\\xDvDot
1309  # xVDot = \\frac{dX_{V}}{dX_{S}}\\xSDot
1310 
1311  # Then, we assign this vector to FFD2XvSeed in the mphys_dafoam
1312  # This will be used in forward mode AD runs
1313 
1314  discipline = self.getOption("discipline")
1315 
1316  if DVGeo is None:
1317  raise Error("calcFFD2XvSeeds is call but no DVGeo object found! Call add_dvgeo in the run script!")
1318 
1319  if self.mesh is None:
1320  raise Error("calcFFD2XvSeeds is call but no mesh object found!")
1321 
1322  dvName = self.getOption("useAD")["dvName"]
1323  seedIndex = self.getOption("useAD")["seedIndex"]
1324  # create xDVDot vec and initialize it with zeros
1325  xDV = DVGeo.getValues()
1326 
1327  # create a copy of xDV and set the seed to 1.0
1328  # the dv and index depends on dvName and seedIndex
1329  xDvDot = {}
1330  for key in list(xDV.keys()):
1331  xDvDot[key] = np.zeros_like(xDV[key])
1332  xDvDot[dvName][seedIndex] = 1.0
1333 
1334  # get the original surf coords
1335  xs0 = self.getSurfaceCoordinates(self.allSurfacesGroup)
1336  xSDot0 = np.zeros_like(xs0)
1337  xSDot0 = self.mapVector(xSDot0, self.allSurfacesGroup, self.designSurfacesGroup)
1338 
1339  # get xSDot
1340  xSDot = DVGeo.totalSensitivityProd(xDvDot, ptSetName="x_%s0" % discipline).reshape(xSDot0.shape)
1341  # get xVDot
1342  xVDot = self.mesh.warpDerivFwd(xSDot)
1343 
1344  return xVDot
1345 
1346  def _initSolver(self):
1347  """
1348  Initialize the solvers. This needs to be called before calling any runs
1349  """
1350 
1351  if self.solverInitialized == 1:
1352  raise Error("pyDAFoam: self._initSolver has been called! One shouldn't initialize solvers twice!")
1353 
1354  solverName = self.getOption("solverName")
1355  solverArg = solverName + " -python " + self.parallelFlag
1356 
1357  from .libs.pyDASolvers import pyDASolvers
1358 
1359  self.solver = pyDASolvers(solverArg.encode(), self.options)
1360 
1361  if self.getOption("useAD")["mode"] == "forward":
1362 
1363  from .libs.ADF.pyDASolvers import pyDASolvers as pyDASolversAD
1364 
1365  self.solverAD = pyDASolversAD(solverArg.encode(), self.options)
1366 
1367  elif self.getOption("useAD")["mode"] == "reverse":
1368 
1369  from .libs.ADR.pyDASolvers import pyDASolvers as pyDASolversAD
1370 
1371  self.solverAD = pyDASolversAD(solverArg.encode(), self.options)
1372 
1373  self.solver.initSolver()
1374  self.solverAD.initSolver()
1375 
1376  Info("Init solver done! ElapsedClockTime %f s" % self.solver.getElapsedClockTime())
1377  Info("Init solver done! ElapsedCpuTime %f s" % self.solver.getElapsedCpuTime())
1378 
1379  self.solverInitialized = 1
1380 
1381  return
1382 
1383  def runDecomposePar(self):
1384  """
1385  Run decomposePar to parallel run
1386  """
1387 
1388  # don't run it if it is a serial case
1389  if self.comm.size == 1:
1390  return
1391 
1392  # write the decomposeParDict file with the correct numberOfSubdomains number
1393  self._writeDecomposeParDict()
1394 
1395  command = ["decomposePar"]
1396  args = self.getOption("decomposeParDict")["args"]
1397 
1398  for arg in args:
1399  if arg != "None":
1400  command.append(arg)
1401 
1402  if self.comm.rank == 0:
1403  status = subprocess.call(command, stdout=sys.stdout, stderr=subprocess.STDOUT, shell=False)
1404  if status != 0:
1405  # raise Error('pyDAFoam: status %d: Unable to run decomposePar'%status)
1406  print("\nUnable to run decomposePar, the domain has been already decomposed?\n", flush=True)
1407  self.comm.Barrier()
1408 
1409  return
1410 
1412  """
1413  Delete the previous primal solution time folder
1414  """
1415 
1416  solTime = self.solver.getPrevPrimalSolTime()
1417 
1418  rootDir = os.getcwd()
1419  if self.parallel:
1420  checkPath = os.path.join(rootDir, "processor%d/%g" % (self.comm.rank, solTime))
1421  else:
1422  checkPath = os.path.join(rootDir, "%g" % solTime)
1423 
1424  if os.path.isdir(checkPath):
1425  try:
1426  shutil.rmtree(checkPath)
1427  except Exception:
1428  raise Error("Can not delete %s" % checkPath)
1429 
1430  Info("Previous solution time %g found and deleted." % solTime)
1431  else:
1432  Info("Previous solution time %g not found and nothing deleted." % solTime)
1433 
1434  return
1435 
1436  def renameSolution(self, solIndex):
1437  """
1438  Rename the primal solution folder to specific format for post-processing. The renamed time has the
1439  format like 0.0001, 0.0002, etc. One can load these intermediate shapes and fields and
1440  plot them in paraview.
1441  The way it is implemented is that we sort the solution folder and consider the largest time folder
1442  as the solution folder and rename it
1443 
1444  Parameters
1445  ----------
1446  solIndex: int
1447  The major interation index
1448  """
1449 
1450  rootDir = os.getcwd()
1451  if self.parallel:
1452  checkPath = os.path.join(rootDir, "processor%d" % self.comm.rank)
1453  else:
1454  checkPath = rootDir
1455 
1456  latestTime = self.solver.getLatestTime()
1457 
1458  if latestTime < 1.0:
1459  Info("Latest solution time %g less than 1, not renamed." % latestTime)
1460  renamed = False
1461  return latestTime, renamed
1462 
1463  distTime = "%g" % (solIndex / 1e4)
1464  targetTime = "%g" % latestTime
1465 
1466  src = os.path.join(checkPath, targetTime)
1467  dst = os.path.join(checkPath, distTime)
1468 
1469  Info("Moving time %s to %s" % (targetTime, distTime))
1470 
1471  if os.path.isdir(dst):
1472  raise Error("%s already exists, moving failed!" % dst)
1473  else:
1474  try:
1475  shutil.move(src, dst)
1476  except Exception:
1477  raise Error("Can not move %s to %s" % (src, dst))
1478 
1479  renamed = True
1480  return distTime, renamed
1481 
1482  def _readMeshInfo(self):
1483  """
1484  Initialize mesh information and read mesh information
1485  """
1486 
1487  dirName = os.getcwd()
1488 
1489  self.fileNames, self.xv0, self.faces, self.boundaries, self.owners, self.neighbours = self._readOFGrid(dirName)
1490  self.xv = copy.copy(self.xv0)
1491 
1492  return
1493 
1494  def setSurfaceCoordinates(self, coordinates, groupName=None):
1495  """
1496  Set the updated surface coordinates for a particular group.
1497  Parameters
1498  ----------
1499  coordinates : numpy array
1500  Numpy array of size Nx3, where N is the number of coordinates on this processor.
1501  This array must have the same shape as the array obtained with getSurfaceCoordinates()
1502  groupName : str
1503  Name of family or group of families for which to return coordinates for.
1504  """
1505  if self.mesh is None:
1506  return
1507 
1508  if groupName is None:
1509  groupName = self.allWallsGroup
1510 
1511  self._updateGeomInfo = True
1512  if self.mesh is None:
1513  raise Error("Cannot set new surface coordinate locations without a mesh" "warping object present.")
1514 
1515  # First get the surface coordinates of the meshFamily in case
1516  # the groupName is a subset, those values will remain unchanged.
1517 
1518  meshSurfCoords = self.getSurfaceCoordinates(self.allWallsGroup)
1519  meshSurfCoords = self.mapVector(coordinates, groupName, self.allWallsGroup, meshSurfCoords)
1520 
1521  self.mesh.setSurfaceCoordinates(meshSurfCoords)
1522 
1523  def getSurfaceCoordinates(self, groupName=None):
1524  """
1525  Return the coordinates for the surfaces defined by groupName.
1526 
1527  Parameters
1528  ----------
1529  groupName : str
1530  Group identifier to get only coordinates cooresponding to
1531  the desired group. The group must be a family or a
1532  user-supplied group of families. The default is None which
1533  corresponds to all wall-type surfaces.
1534 
1535  Output
1536  ------
1537  xs: numpy array of size nPoints * 3 for surface points
1538  """
1539 
1540  if groupName is None:
1541  groupName = self.allWallsGroup
1542 
1543  # Get the required size
1544  npts, ncell = self._getSurfaceSize(groupName)
1545  xs = np.zeros((npts, 3), self.dtype)
1546 
1547  # loop over the families in this group and populate the surface
1548  famInd = self.families[groupName]
1549  counter = 0
1550  for Ind in famInd:
1551  name = self.basicFamilies[Ind]
1552  bc = self.boundaries[name]
1553  for ptInd in bc["indicesRed"]:
1554  xs[counter, :] = self.xv[ptInd]
1555  counter += 1
1556 
1557  return xs
1558 
1559  def _getSurfaceSize(self, groupName):
1560  """
1561  Internal routine to return the size of a particular surface. This
1562  does *NOT* set the actual family group
1563  """
1564  if groupName is None:
1565  groupName = self.allSurfacesGroup
1566 
1567  if groupName not in self.families:
1568  raise Error(
1569  "'%s' is not a family in the OpenFoam Case or has not been added"
1570  " as a combination of families" % groupName
1571  )
1572 
1573  # loop over the basic surfaces in the family group and sum up the number of
1574  # faces and nodes
1575 
1576  famInd = self.families[groupName]
1577  nPts = 0
1578  nCells = 0
1579  for Ind in famInd:
1580  name = self.basicFamilies[Ind]
1581  bc = self.boundaries[name]
1582  nCells += len(bc["facesRed"])
1583  nPts += len(bc["indicesRed"])
1584 
1585  return nPts, nCells
1586 
1587  def setPrimalBoundaryConditions(self, printInfo=1, printInfoAD=0):
1588  """
1589  Assign the boundary condition defined in primalBC to the OF fields
1590  """
1591  self.solver.setPrimalBoundaryConditions(printInfo)
1592  self.solverAD.setPrimalBoundaryConditions(printInfoAD)
1593 
1594  def _computeBasicFamilyInfo(self):
1595  """
1596  Loop over the boundary data and compute necessary family
1597  information for the basic patches
1598 
1599  """
1600  # get the list of basic families
1601  self.basicFamilies = sorted(self.boundaries.keys())
1602 
1603  # save and return a list of the wall boundaries
1604  self.wallList = []
1605  counter = 0
1606  # for each boundary, figure out the unique list of volume node indices it uses
1607  for name in self.basicFamilies:
1608  # setup the basic families dictionary
1609  self.families[name] = [counter]
1610  counter += 1
1611 
1612  # Create a handle for this boundary
1613  bc = self.boundaries[name]
1614 
1615  # get the number of faces associated with this boundary
1616  nFace = len(bc["faces"])
1617 
1618  # create the point index list
1619  indices = []
1620 
1621  # check that this isn't an empty boundary
1622  if nFace > 0:
1623  for iFace in bc["faces"]:
1624  # get the node information for the current face
1625  face = self.faces[iFace]
1626  indices.extend(face)
1627 
1628  # Get the unique point entries for this boundary
1629  indices = np.unique(indices)
1630 
1631  # now create the reverse dictionary to connect the reduced set with the original
1632  inverseInd = {}
1633  for i in range(len(indices)):
1634  inverseInd[indices[i]] = i
1635 
1636  # Now loop back over the faces and store the connectivity in terms of the reduces index set
1637  # Here facesRed store the boundary face reduced-point-index
1638  # For example,
1639  # 'indicesRed': [0, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80] <- unique point index for this boundary
1640  # 'facesRed': [0, 8, 9, 1], [1, 9, 10, 2] <- Here 0 means the 0th point index (0) in indicesRed, and 8 means the 8th
1641  # point index (64) in incidexRed. So [0 8 9 1] corresponds to the face [0 64 72 8] in the original point index system.
1642  # NOTE: using the reduce face indexing will faciliate the connectivity calls
1643  facesRed = []
1644  for iFace in bc["faces"]:
1645  # get the node information for the current face
1646  face = self.faces[iFace]
1647  nNodes = len(face)
1648  # Generate the reduced connectivity.
1649  faceReduced = []
1650  for j in range(nNodes):
1651  indOrig = face[j]
1652  indRed = inverseInd[indOrig]
1653  faceReduced.append(indRed)
1654  facesRed.append(faceReduced)
1655 
1656  # Check that the length of faces and facesRed are equal
1657  if not (len(bc["faces"]) == len(facesRed)):
1658  raise Error("Connectivity for faces on reduced index set is not the same length as original.")
1659 
1660  # put the reduced faces and index list in the boundary dict
1661  bc["facesRed"] = facesRed
1662  bc["indicesRed"] = list(indices)
1663 
1664  # now check for walls
1665  if bc["type"] == "wall" or bc["type"] == "slip" or bc["type"] == "cyclic":
1666  self.wallList.append(name)
1667 
1668  return
1669 
1671  """
1672  Get the list of indices to pass to the mesh object for the
1673  volume mesh mapping
1674  """
1675 
1676  # Setup External Warping
1677  nCoords = len(self.xv0.flatten())
1678 
1679  nCoords = self.comm.allgather(nCoords)
1680  offset = 0
1681  for i in range(self.comm.rank):
1682  offset += nCoords[i]
1683 
1684  meshInd = np.arange(nCoords[self.comm.rank]) + offset
1685 
1686  return meshInd
1687 
1688  def mapVector(self, vec1, groupName1, groupName2, vec2=None):
1689  """This is the main workhorse routine of everything that deals with
1690  families in pyDAFoam. The purpose of this routine is to convert a
1691  vector 'vec1' (of size Nx3) that was evaluated with
1692  'groupName1' and expand or contract it (and adjust the
1693  ordering) to produce 'vec2' evaluated on groupName2.
1694 
1695  A little ascii art might help. Consider the following "mesh"
1696  . Family 'fam1' has 9 points, 'fam2' has 10 pts and 'fam3' has
1697  5 points. Consider that we have also also added two
1698  additional groups: 'f12' containing 'fam1' and 'fma2' and a
1699  group 'f23' that contains families 'fam2' and 'fam3'. The vector
1700  we want to map is 'vec1'. It is length 9+10. All the 'x's are
1701  significant values.
1702 
1703  The call: mapVector(vec1, 'f12', 'f23')
1704 
1705  will produce the "returned vec" array, containing the
1706  significant values from 'fam2', where the two groups overlap,
1707  and the new values from 'fam3' set to zero. The values from
1708  fam1 are lost. The returned vec has size 15.
1709 
1710  fam1 fam2 fam3
1711  |---------+----------+------|
1712 
1713  |xxxxxxxxx xxxxxxxxxx| <- vec1
1714  |xxxxxxxxxx 000000| <- returned vec (vec2)
1715 
1716  Parameters
1717  ----------
1718  vec1 : Numpy array
1719  Array of size Nx3 that will be mapped to a different family set.
1720 
1721  groupName1 : str
1722  The family group where the vector vec1 is currently defined
1723 
1724  groupName2 : str
1725  The family group where we want to the vector to mapped into
1726 
1727  vec2 : Numpy array or None
1728  Array containing existing values in the output vector we want to keep.
1729  If this vector is not given, the values will be filled with zeros.
1730 
1731  Returns
1732  -------
1733  vec2 : Numpy array
1734  The input vector mapped to the families defined in groupName2.
1735  """
1736  if groupName1 not in self.families or groupName2 not in self.families:
1737  raise Error(
1738  "'%s' or '%s' is not a family in the mesh file or has not been added"
1739  " as a combination of families" % (groupName1, groupName2)
1740  )
1741 
1742  # Shortcut:
1743  if groupName1 == groupName2:
1744  return vec1
1745 
1746  if vec2 is None:
1747  npts, ncell = self._getSurfaceSize(groupName2)
1748  vec2 = np.zeros((npts, 3), self.dtype)
1749 
1750  famList1 = self.families[groupName1]
1751  famList2 = self.families[groupName2]
1752 
1753  """
1754  This functionality is predicated on the surfaces being traversed in the
1755  same order every time. Loop over the allfamilies list, keeping track of sizes
1756  as we go and if the family is in both famLists, copy the values from vec1 to vec2.
1757 
1758  """
1759 
1760  vec1counter = 0
1761  vec2counter = 0
1762 
1763  for ind in self.families[self.allSurfacesGroup]:
1764  npts, ncell = self._getSurfaceSize(self.basicFamilies[ind])
1765 
1766  if ind in famList1 and ind in famList2:
1767  vec2[vec2counter : npts + vec2counter] = vec1[vec1counter : npts + vec1counter]
1768 
1769  if ind in famList1:
1770  vec1counter += npts
1771 
1772  if ind in famList2:
1773  vec2counter += npts
1774 
1775  return vec2
1776 
1777  # base case files
1778  def _readOFGrid(self, caseDir):
1779  """
1780  Read in the mesh information we need to run the case using pyofm
1781 
1782  Parameters
1783  ----------
1784  caseDir : str
1785  The directory containing the openFOAM Mesh files
1786  """
1787 
1788  Info("Reading OpenFOAM mesh information...")
1789 
1790  from pyofm import PYOFM
1791 
1792  # Initialize pyOFM
1793  ofm = PYOFM(comm=self.comm)
1794 
1795  # generate the file names
1796  fileNames = ofm.getFileNames(caseDir, comm=self.comm)
1797 
1798  # Read in the volume points
1799  x0 = ofm.readVolumeMeshPoints()
1800 
1801  # Read the face info for the mesh
1802  faces = ofm.readFaceInfo()
1803 
1804  # Read the boundary info
1805  boundaries = ofm.readBoundaryInfo(faces)
1806 
1807  # Read the cell info for the mesh
1808  owners, neighbours = ofm.readCellInfo()
1809 
1810  return fileNames, x0, faces, boundaries, owners, neighbours
1811 
1812  def setOption(self, name, value):
1813  """
1814  Set a value to options.
1815  NOTE: calling this function will only change the values in self.options
1816  It will NOT change values for allOptions_ in DAOption. To make the options changes
1817  from pyDAFoam to DASolvers, call self.updateDAOption()
1818 
1819  Parameters
1820  ----------
1821  name : str
1822  Name of option to set. Not case sensitive
1823  value : varies
1824  Value to set. Type is checked for consistency.
1825 
1826  Examples
1827  --------
1828  If self.options reads:
1829  self.options =
1830  {
1831  'solverName': [str, 'DASimpleFoam'],
1832  'flowEndTime': [float, 1.0]
1833  }
1834  Then, calling self.options('solverName', 'DARhoSimpleFoam') will give:
1835  self.options =
1836  {
1837  'solverName': [str, 'DARhoSimpleFoam'],
1838  'flowEndTime': [float, 1.0]
1839  }
1840 
1841 
1842  NOTE: if 'value' is of dict type, we will set all the subKey values in
1843  'value' dict to self.options, instead of overiding it. This works for
1844  only THREE levels of subDicts
1845 
1846  For example, if self.options reads
1847  self.options =
1848  {
1849  'function': [dict, {
1850  'name': 'CD',
1851  'direction': [1.0, 0.0, 0.0],
1852  'scale': 1.0}]
1853  }
1854 
1855  Then, calling self.setOption('function', {'name': 'CL'}) will give:
1856 
1857  self.options =
1858  {
1859  'function': [dict, {
1860  'name': 'CL',
1861  'direction': [1.0, 0.0, 0.0],
1862  'scale': 1.0}]
1863  }
1864 
1865  INSTEAD OF
1866 
1867  self.options =
1868  {
1869  'function': [dict, {'name': 'CL'}]
1870  }
1871  """
1872 
1873  try:
1874  self.defaultOptions[name]
1875  except KeyError:
1876  Error("Option '%-30s' is not a valid %s option." % (name, self.name))
1877 
1878  # Make sure we are not trying to change an immutable option if
1879  # we are not allowed to.
1880  if name in self.imOptions:
1881  raise Error("Option '%-35s' cannot be modified after the solver " "is created." % name)
1882 
1883  # Now we know the option exists, lets check if the type is ok:
1884  if isinstance(value, self.defaultOptions[name][0]):
1885  # the type matches, now we need to check if the 'value' is of dict type, if yes, we only
1886  # replace the subKey values of 'value', instead of overiding all the subKey values
1887  # NOTE. we only check 3 levels of subKeys
1888  if isinstance(value, dict):
1889  for subKey1 in value:
1890  # check if this subKey is still a dict.
1891  if isinstance(value[subKey1], dict):
1892  for subKey2 in value[subKey1]:
1893  # check if this subKey is still a dict.
1894  if isinstance(value[subKey1][subKey2], dict):
1895  for subKey3 in value[subKey1][subKey2]:
1896  self.options[name][1][subKey1][subKey2][subKey3] = value[subKey1][subKey2][subKey3]
1897  else:
1898  self.options[name][1][subKey1][subKey2] = value[subKey1][subKey2]
1899  else:
1900  # no need to set self.options[name][0] since it has the right type
1901  self.options[name][1][subKey1] = value[subKey1]
1902  else:
1903  # It is not dict, just set
1904  # no need to set self.options[name][0] since it has the right type
1905  self.options[name][1] = value
1906  else:
1907  raise Error(
1908  "Datatype for Option %-35s was not valid \n "
1909  "Expected data type is %-47s \n "
1910  "Received data type is %-47s" % (name, self.defaultOptions[name][0], type(value))
1911  )
1912 
1913  def _initOption(self, name, value):
1914  """
1915  Set a value to options. This function will be used only for initializing the options internally.
1916  Do NOT call this function from the run script!
1917 
1918  Parameters
1919  ----------
1920  name : str
1921  Name of option to set. Not case sensitive
1922  value : varies
1923  Value to set. Type is checked for consistency.
1924  """
1925 
1926  try:
1927  self.defaultOptions[name]
1928  except KeyError:
1929  Error("Option '%-30s' is not a valid %s option." % (name, self.name))
1930 
1931  # Make sure we are not trying to change an immutable option if
1932  # we are not allowed to.
1933  if name in self.imOptions:
1934  raise Error("Option '%-35s' cannot be modified after the solver " "is created." % name)
1935 
1936  # Now we know the option exists, lets check if the type is ok:
1937  if isinstance(value, self.defaultOptions[name][0]):
1938  # the type matches, now we need to check if the 'value' is of dict type, if yes, we only
1939  # replace the subKey values of 'value', instead of overiding all the subKey values
1940  if isinstance(value, dict):
1941  for subKey in value:
1942  # no need to set self.options[name][0] since it has the right type
1943  self.options[name][1][subKey] = value[subKey]
1944  else:
1945  # It is not dict, just set
1946  # no need to set self.options[name][0] since it has the right type
1947  self.options[name][1] = value
1948  else:
1949  raise Error(
1950  "Datatype for Option %-35s was not valid \n "
1951  "Expected data type is %-47s \n "
1952  "Received data type is %-47s" % (name, self.defaultOptions[name][0], type(value))
1953  )
1954 
1955  def getNRegressionParameters(self, modelName):
1956  """
1957  Get the number of regression model parameters
1958  """
1959  nParameters = self.solver.getNRegressionParameters(modelName.encode())
1960  return nParameters
1961 
1962  def getOption(self, name):
1963  """
1964  Get a value from options
1965 
1966  Parameters
1967  ----------
1968  name : str
1969  Name of option to get. Not case sensitive
1970 
1971  Returns
1972  -------
1973  value : varies
1974  Return the value of the option.
1975  """
1976 
1977  if name in self.defaultOptions:
1978  return self.options[name][1]
1979  else:
1980  raise Error("%s is not a valid option name." % name)
1981 
1982  def updateDAOption(self):
1983  """
1984  Update the allOptions_ in DAOption based on the latest self.options in
1985  pyDAFoam. This will pass the changes of self.options from pyDAFoam
1986  to DASolvers. NOTE: need to call this function after calling
1987  self.initSolver
1988  """
1989 
1990  if self.solverInitialized == 0:
1991  raise Error("self._initSolver not called!")
1992 
1993  self.solver.updateDAOption(self.options)
1994 
1995  if self.getOption("useAD")["mode"] in ["forward", "reverse"]:
1996  self.solverAD.updateDAOption(self.options)
1997 
1999  """
2000  Get number of local adjoint states
2001  """
2002  return self.solver.getNLocalAdjointStates()
2003 
2004  def getNLocalPoints(self):
2005  """
2006  Get number of local points
2007  """
2008  return self.solver.getNLocalPoints()
2009 
2010  def getStates(self):
2011  """
2012  Return the adjoint state array owns by this processor
2013  """
2014 
2015  nLocalStateSize = self.solver.getNLocalAdjointStates()
2016  states = np.zeros(nLocalStateSize, self.dtype)
2017  self.solver.getOFFields(states)
2018 
2019  return states
2020 
2021  def setStates(self, states):
2022  """
2023  Set the state to the OpenFOAM field
2024  """
2025 
2026  self.solver.updateOFFields(states)
2027  self.solverAD.updateOFFields(states)
2028 
2029  return
2030 
2031  def setVolCoords(self, vol_coords):
2032  """
2033  Set the vol_coords to the OpenFOAM field
2034  """
2035 
2036  self.solver.updateOFMesh(vol_coords)
2037  self.solverAD.updateOFMesh(vol_coords)
2038 
2039  return
2040 
2041  def arrayVal2Vec(self, array1, vec):
2042  """
2043  Assign the values from array1 to vec
2044  """
2045 
2046  size = len(array1)
2047 
2048  Istart, Iend = vec.getOwnershipRange()
2049 
2050  if (Iend - Istart) != size:
2051  raise Error("array and vec's sizes are not consistent")
2052 
2053  for i in range(Istart, Iend):
2054  iRel = i - Istart
2055  vec[i] = array1[iRel]
2056 
2057  vec.assemblyBegin()
2058  vec.assemblyEnd()
2059 
2060  def vecVal2Array(self, vec, array1):
2061  """
2062  Assign the values from vec to array1
2063  """
2064 
2065  size = len(array1)
2066 
2067  Istart, Iend = vec.getOwnershipRange()
2068 
2069  if (Iend - Istart) != size:
2070  raise Error("array and vec's sizes are not consistent")
2071 
2072  for i in range(Istart, Iend):
2073  iRel = i - Istart
2074  array1[iRel] = vec[i]
2075 
2076  def vec2Array(self, vec):
2077  """
2078  Convert a Petsc vector to numpy array
2079  """
2080 
2081  Istart, Iend = vec.getOwnershipRange()
2082  size = Iend - Istart
2083  array1 = np.zeros(size, self.dtype)
2084  for i in range(Istart, Iend):
2085  iRel = i - Istart
2086  array1[iRel] = vec[i]
2087  return array1
2088 
2089  def array2Vec(self, array1):
2090  """
2091  Convert a numpy array to Petsc vector
2092  """
2093  size = len(array1)
2094 
2095  vec = PETSc.Vec().create(PETSc.COMM_WORLD)
2096  vec.setSizes((size, PETSc.DECIDE), bsize=1)
2097  vec.setFromOptions()
2098  vec.zeroEntries()
2099 
2100  Istart, Iend = vec.getOwnershipRange()
2101  for i in range(Istart, Iend):
2102  iRel = i - Istart
2103  vec[i] = array1[iRel]
2104 
2105  vec.assemblyBegin()
2106  vec.assemblyEnd()
2107 
2108  return vec
2109 
2110  def _getImmutableOptions(self):
2111  """
2112  We define the list of options that *cannot* be changed after the
2113  object is created. pyDAFoam will raise an error if a user tries to
2114  change these. The strings for these options are placed in a set
2115  """
2116 
2117  return ()
2118 
2119  def _writeDecomposeParDict(self):
2120  """
2121  Write system/decomposeParDict
2122  """
2123  if self.comm.rank == 0:
2124  # Open the options file for writing
2125 
2126  workingDirectory = os.getcwd()
2127  sysDir = "system"
2128  varDir = os.path.join(workingDirectory, sysDir)
2129  fileName = "decomposeParDict"
2130  fileLoc = os.path.join(varDir, fileName)
2131  f = open(fileLoc, "w")
2132  # write header
2133  self._writeOpenFoamHeader(f, "dictionary", sysDir, fileName)
2134  # write content
2135  decomDict = self.getOption("decomposeParDict")
2136  n = decomDict["simpleCoeffs"]["n"]
2137  f.write("numberOfSubdomains %d;\n" % self.nProcs)
2138  f.write("\n")
2139  f.write("method %s;\n" % decomDict["method"])
2140  f.write("\n")
2141  f.write("simpleCoeffs \n")
2142  f.write("{ \n")
2143  f.write(" n (%d %d %d);\n" % (n[0], n[1], n[2]))
2144  f.write(" delta %g;\n" % decomDict["simpleCoeffs"]["delta"])
2145  f.write("} \n")
2146  f.write("\n")
2147  f.write("distributed false;\n")
2148  f.write("\n")
2149  f.write("roots();\n")
2150  if len(decomDict["preservePatches"]) == 1 and decomDict["preservePatches"][0] == "None":
2151  pass
2152  else:
2153  f.write("\n")
2154  f.write("preservePatches (")
2155  for pPatch in decomDict["preservePatches"]:
2156  f.write("%s " % pPatch)
2157  f.write(");\n")
2158  if decomDict["singleProcessorFaceSets"][0] != "None":
2159  f.write("singleProcessorFaceSets (")
2160  for pPatch in decomDict["singleProcessorFaceSets"]:
2161  f.write(" (%s -1) " % pPatch)
2162  f.write(");\n")
2163  f.write("\n")
2164  f.write("// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n")
2165 
2166  f.close()
2167  self.comm.Barrier()
2168 
2169  def _writeOpenFoamHeader(self, f, className, location, objectName):
2170  """
2171  Write OpenFOAM header file
2172  """
2173 
2174  f.write("/*--------------------------------*- C++ -*---------------------------------*\ \n")
2175  f.write("| ======== | | \n")
2176  f.write("| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | \n")
2177  f.write("| \\ / O peration | Version: v1812 | \n")
2178  f.write("| \\ / A nd | Web: www.OpenFOAM.com | \n")
2179  f.write("| \\/ M anipulation | | \n")
2180  f.write("\*--------------------------------------------------------------------------*/ \n")
2181  f.write("FoamFile\n")
2182  f.write("{\n")
2183  f.write(" version 2.0;\n")
2184  f.write(" format ascii;\n")
2185  f.write(" class %s;\n" % className)
2186  f.write(' location "%s";\n' % location)
2187  f.write(" object %s;\n" % objectName)
2188  f.write("}\n")
2189  f.write("// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //\n")
2190  f.write("\n")
2191 
2192 
2193 class Error(Exception):
2194  """
2195  Format the error message in a box to make it clear this
2196  was a expliclty raised exception.
2197  """
2198 
2199  def __init__(self, message):
2200  msg = "\n+" + "-" * 78 + "+" + "\n" + "| pyDAFoam Error: "
2201  i = 19
2202  for word in message.split():
2203  if len(word) + i + 1 > 78: # Finish line and start new one
2204  msg += " " * (78 - i) + "|\n| " + word + " "
2205  i = 1 + len(word) + 1
2206  else:
2207  msg += word + " "
2208  i += len(word) + 1
2209  msg += " " * (78 - i) + "|\n" + "+" + "-" * 78 + "+" + "\n"
2210  print(msg, flush=True)
2211  Exception.__init__(self)
2212 
2213  return
2214 
2215 
2216 class Info(object):
2217  """
2218  Print information and flush to screen for parallel cases
2219  """
2220 
2221  def __init__(self, message):
2222  if MPI.COMM_WORLD.rank == 0:
2223  print(message, flush=True)
2224  MPI.COMM_WORLD.Barrier()
2225 
2226 
2228  """
2229  TensorFlow helper class.
2230  NOTE: this is a static class because the callback function
2231  does not accept non-static class members (seg fault)
2232  """
2233 
2234  options = {}
2235 
2236  model = {}
2237 
2238  modelName = None
2239 
2240  predictBatchSize = {}
2241 
2242  nInputs = {}
2243 
2244  @staticmethod
2245  def initialize():
2246  """
2247  Initialize parameters and load models
2248  """
2249  Info("Initializing the TensorFlowHelper")
2250  for key in list(TensorFlowHelper.options.keys()):
2251  if key != "active":
2252  modelName = key
2253  TensorFlowHelper.predictBatchSize[modelName] = TensorFlowHelper.options[modelName]["predictBatchSize"]
2254  TensorFlowHelper.nInputs[modelName] = TensorFlowHelper.options[modelName]["nInputs"]
2255  TensorFlowHelper.model[modelName] = tf.keras.models.load_model(modelName)
2256 
2257  @staticmethod
2258  def setModelName(modelName):
2259  """
2260  Set the model name from the C++ to Python layer
2261  """
2262  TensorFlowHelper.modelName = modelName.decode()
2263 
2264  @staticmethod
2265  def predict(inputs, n, outputs, m):
2266  """
2267  Calculate the outputs based on the inputs using the saved model
2268  """
2269 
2270  modelName = TensorFlowHelper.modelName
2271  nInputs = TensorFlowHelper.nInputs[modelName]
2272 
2273  inputs_tf = np.reshape(inputs, (-1, nInputs))
2274  batchSize = TensorFlowHelper.predictBatchSize[modelName]
2275  outputs_tf = TensorFlowHelper.model[modelName].predict(inputs_tf, verbose=False, batch_size=batchSize)
2276 
2277  for i in range(m):
2278  outputs[i] = outputs_tf[i, 0]
2279 
2280  @staticmethod
2281  def calcJacVecProd(inputs, inputs_b, n, outputs, outputs_b, m):
2282  """
2283  Calculate the gradients of the outputs wrt the inputs
2284  """
2285 
2286  modelName = TensorFlowHelper.modelName
2287  nInputs = TensorFlowHelper.nInputs[modelName]
2288 
2289  inputs_tf = np.reshape(inputs, (-1, nInputs))
2290  inputs_tf_var = tf.Variable(inputs_tf, dtype=tf.float32)
2291 
2292  with tf.GradientTape() as tape:
2293  outputs_tf = TensorFlowHelper.model[modelName](inputs_tf_var)
2294 
2295  gradients_tf = tape.gradient(outputs_tf, inputs_tf_var)
2296 
2297  for i in range(gradients_tf.shape[0]):
2298  for j in range(gradients_tf.shape[1]):
2299  idx = i * gradients_tf.shape[1] + j
2300  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.Info.__init__
def __init__(self, message)
Definition: pyDAFoam.py:2221
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:2076
dafoam.pyDAFoam.DAOPTION.discipline
discipline
The discipline name.
Definition: pyDAFoam.py:350
dafoam.pyDAFoam.PYDAFOAM._initializeOptions
def _initializeOptions(self, options)
Definition: pyDAFoam.py:1080
dafoam.pyDAFoam.PYDAFOAM._getImmutableOptions
def _getImmutableOptions(self)
Definition: pyDAFoam.py:2110
dafoam.pyDAFoam.DAOPTION.adjPartDerivFDStep
adjPartDerivFDStep
The step size for finite-difference computation of partial derivatives.
Definition: pyDAFoam.py:354
dafoam.pyDAFoam.PYDAFOAM.setPrimalBoundaryConditions
def setPrimalBoundaryConditions(self, printInfo=1, printInfoAD=0)
Definition: pyDAFoam.py:1587
dafoam.pyDAFoam.PYDAFOAM.solver
solver
Definition: pyDAFoam.py:1359
dafoam.pyDAFoam.DAOPTION.unsteadyAdjoint
unsteadyAdjoint
Options for unsteady adjoint.
Definition: pyDAFoam.py:365
dafoam.pyDAFoam.PYDAFOAM.getSurfaceCoordinates
def getSurfaceCoordinates(self, groupName=None)
Definition: pyDAFoam.py:1523
dafoam.pyDAFoam.PYDAFOAM.getSolverMeshIndices
def getSolverMeshIndices(self)
Definition: pyDAFoam.py:1670
dafoam.pyDAFoam.Info
Definition: pyDAFoam.py:2216
dafoam.pyDAFoam.PYDAFOAM.arrayVal2Vec
def arrayVal2Vec(self, array1, vec)
Definition: pyDAFoam.py:2041
dafoam.pyDAFoam.DAOPTION.inputInfo
inputInfo
General input information.
Definition: pyDAFoam.py:234
dafoam.pyDAFoam.PYDAFOAM.readDynamicMeshPoints
def readDynamicMeshPoints(self, timeVal, deltaT, timeIndex, ddtSchemeOrder)
Definition: pyDAFoam.py:1217
dafoam.pyDAFoam.PYDAFOAM
Definition: pyDAFoam.py:620
dafoam.pyDAFoam.PYDAFOAM._readOFGrid
def _readOFGrid(self, caseDir)
Definition: pyDAFoam.py:1778
dafoam.pyDAFoam.DAOPTION
Definition: pyDAFoam.py:34
dafoam.pyDAFoam.PYDAFOAM.families
families
Definition: pyDAFoam.py:663
dafoam.pyDAFoam.DAOPTION.writeDeformedConstraints
writeDeformedConstraints
Whether to write deformed constraints to disk during optimization, i.e., DVCon.writeTecplot.
Definition: pyDAFoam.py:578
dafoam.pyDAFoam.PYDAFOAM.__init__
def __init__(self, comm=None, options=None)
Definition: pyDAFoam.py:635
dafoam.pyDAFoam.DAOPTION.writeAdjointFields
writeAdjointFields
Whether to write adjoint variables in OpenFOAM field format for post-processing.
Definition: pyDAFoam.py:581
dafoam.pyDAFoam.DAOPTION.writeMinorIterations
writeMinorIterations
Whether to write the primal solutions for minor iterations (i.e., line search).
Definition: pyDAFoam.py:591
dafoam.pyDAFoam.DAOPTION.dynamicMesh
dynamicMesh
whether the dynamic mesh is activated.
Definition: pyDAFoam.py:304
dafoam.pyDAFoam.PYDAFOAM.solverAD
solverAD
Definition: pyDAFoam.py:1365
dafoam.pyDAFoam.PYDAFOAM.parallel
parallel
Definition: pyDAFoam.py:1138
dafoam.pyDAFoam.DAOPTION.primalMinResTolDiff
primalMinResTolDiff
Users can adjust primalMinResTolDiff to tweak how much difference between primalMinResTol and the act...
Definition: pyDAFoam.py:472
dafoam.pyDAFoam.PYDAFOAM.getSurfaceConnectivity
def getSurfaceConnectivity(self, groupName=None)
Definition: pyDAFoam.py:959
dafoam.pyDAFoam.PYDAFOAM.allSurfacesGroup
allSurfacesGroup
Definition: pyDAFoam.py:699
dafoam.pyDAFoam.PYDAFOAM._updateGeomInfo
_updateGeomInfo
Definition: pyDAFoam.py:666
dafoam.pyDAFoam.DAOPTION.transonicPCOption
transonicPCOption
Which options to use to improve the adjoint equation convergence of transonic conditions This is used...
Definition: pyDAFoam.py:360
dafoam.pyDAFoam.PYDAFOAM.getOption
def getOption(self, name)
Definition: pyDAFoam.py:1962
dafoam.pyDAFoam.TensorFlowHelper.calcJacVecProd
def calcJacVecProd(inputs, inputs_b, n, outputs, outputs_b, m)
Definition: pyDAFoam.py:2281
dafoam.pyDAFoam.PYDAFOAM._writeOpenFoamHeader
def _writeOpenFoamHeader(self, f, className, location, objectName)
Definition: pyDAFoam.py:2169
dafoam.pyDAFoam.DAOPTION.normalizeResiduals
normalizeResiduals
Normalization for residuals.
Definition: pyDAFoam.py:503
dafoam.pyDAFoam.PYDAFOAM.mapVector
def mapVector(self, vec1, groupName1, groupName2, vec2=None)
Definition: pyDAFoam.py:1688
dafoam.pyDAFoam.DAOPTION.adjEqnSolMethod
adjEqnSolMethod
The adjoint equation solution method.
Definition: pyDAFoam.py:300
dafoam.pyDAFoam.PYDAFOAM.neighbours
neighbours
Definition: pyDAFoam.py:1489
dafoam.pyDAFoam.PYDAFOAM.evalFunctions
def evalFunctions(self, funcs)
Definition: pyDAFoam.py:876
dafoam.pyDAFoam.DAOPTION.maxCorrectBCCalls
maxCorrectBCCalls
The max number of correctBoundaryConditions calls in the updateOFField function.
Definition: pyDAFoam.py:584
dafoam.pyDAFoam.DAOPTION.debug
debug
Whether running the optimization in the debug mode, which prints extra information.
Definition: pyDAFoam.py:455
dafoam.pyDAFoam.PYDAFOAM.designSurfacesGroup
designSurfacesGroup
Definition: pyDAFoam.py:706
dafoam.pyDAFoam.DAOPTION.primalMinIters
primalMinIters
number of minimal primal iterations.
Definition: pyDAFoam.py:595
dafoam.pyDAFoam.DAOPTION.primalVarBounds
primalVarBounds
The variable upper and lower bounds for primal solution.
Definition: pyDAFoam.py:317
dafoam.pyDAFoam.DAOPTION.decomposeParDict
decomposeParDict
decomposeParDict option.
Definition: pyDAFoam.py:547
dafoam.pyDAFoam.PYDAFOAM.adjointFail
adjointFail
Definition: pyDAFoam.py:687
dafoam.pyDAFoam.PYDAFOAM.__call__
def __call__(self)
Definition: pyDAFoam.py:755
dafoam.pyDAFoam.DAOPTION.solveLinearFunctionName
solveLinearFunctionName
The current objective function name.
Definition: pyDAFoam.py:449
dafoam.pyDAFoam.DAOPTION.adjEqnOption
adjEqnOption
The Petsc options for solving the adjoint linear equation.
Definition: pyDAFoam.py:481
dafoam.pyDAFoam.PYDAFOAM.deletePrevPrimalSolTime
def deletePrevPrimalSolTime(self)
Definition: pyDAFoam.py:1411
dafoam.pyDAFoam.DAOPTION.printIntervalUnsteady
printIntervalUnsteady
The print interval of unsteady primal solvers, e.g., for DAPisoFoam.
Definition: pyDAFoam.py:468
dafoam.pyDAFoam.PYDAFOAM.options
options
Definition: pyDAFoam.py:1111
dafoam.pyDAFoam.DAOPTION.regressionModel
regressionModel
parameters for regression models we support defining multiple regression models.
Definition: pyDAFoam.py:405
dafoam.pyDAFoam.PYDAFOAM._getSurfaceSize
def _getSurfaceSize(self, groupName)
Definition: pyDAFoam.py:1559
dafoam.pyDAFoam.PYDAFOAM.setMesh
def setMesh(self, mesh)
Definition: pyDAFoam.py:938
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:572
dafoam.pyDAFoam.PYDAFOAM.solverRegistry
solverRegistry
Definition: pyDAFoam.py:749
dafoam.pyDAFoam.DAOPTION.outputInfo
outputInfo
General input information.
Definition: pyDAFoam.py:242
setTime
runTime setTime(0.0, 0)
dafoam.pyDAFoam.PYDAFOAM.version
version
Definition: pyDAFoam.py:642
dafoam.pyDAFoam.Error.__init__
def __init__(self, message)
Definition: pyDAFoam.py:2199
dafoam.pyDAFoam.DAOPTION.normalizeStates
normalizeStates
State normalization for dRdWT computation.
Definition: pyDAFoam.py:95
dafoam.pyDAFoam.DAOPTION.tensorflow
tensorflow
tensorflow related functions
Definition: pyDAFoam.py:598
dafoam.pyDAFoam.PYDAFOAM.getNLocalAdjointStates
def getNLocalAdjointStates(self)
Definition: pyDAFoam.py:1998
dafoam.pyDAFoam.PYDAFOAM.imOptions
imOptions
Definition: pyDAFoam.py:1096
dafoam.pyDAFoam.DAOPTION.unsteadyCompOutput
unsteadyCompOutput
the component output for the unsteady solvers.
Definition: pyDAFoam.py:610
dafoam.pyDAFoam.DAOPTION.adjPCLag
adjPCLag
The interval of recomputing the pre-conditioner matrix dRdWTPC for solveAdjoint By default,...
Definition: pyDAFoam.py:380
dafoam.pyDAFoam.PYDAFOAM.getStates
def getStates(self)
Definition: pyDAFoam.py:2010
dafoam.pyDAFoam.PYDAFOAM.runDecomposePar
def runDecomposePar(self)
Definition: pyDAFoam.py:1383
dafoam.pyDAFoam.DAOPTION.wallDistanceMethod
wallDistanceMethod
Whether to use OpenFOAMs snGrad() function or to manually compute distance for wall interfaces.
Definition: pyDAFoam.py:606
dafoam.pyDAFoam.PYDAFOAM.ksp
ksp
Definition: pyDAFoam.py:720
dafoam.pyDAFoam.DAOPTION.primalMinResTol
primalMinResTol
The convergence tolerance for the primal solver.
Definition: pyDAFoam.py:75
dafoam.pyDAFoam.DAOPTION.useMeanStates
useMeanStates
whether to use step-averaged state variables.
Definition: pyDAFoam.py:441
dafoam.pyDAFoam.PYDAFOAM.dRdWTPC
dRdWTPC
Definition: pyDAFoam.py:717
dafoam.pyDAFoam.PYDAFOAM.name
name
Definition: pyDAFoam.py:651
dafoam.pyDAFoam.PYDAFOAM._solverRegistry
def _solverRegistry(self)
Definition: pyDAFoam.py:743
dafoam.pyDAFoam.PYDAFOAM.vecVal2Array
def vecVal2Array(self, vec, array1)
Definition: pyDAFoam.py:2060
dafoam.pyDAFoam.Error
Definition: pyDAFoam.py:2193
dafoam.pyDAFoam.TensorFlowHelper.setModelName
def setModelName(modelName)
Definition: pyDAFoam.py:2258
dafoam.pyDAFoam.PYDAFOAM.renameSolution
def renameSolution(self, solIndex)
Definition: pyDAFoam.py:1436
dafoam.pyDAFoam.PYDAFOAM.setSurfaceCoordinates
def setSurfaceCoordinates(self, coordinates, groupName=None)
Definition: pyDAFoam.py:1494
dafoam.pyDAFoam.PYDAFOAM.nSolvePrimals
nSolvePrimals
Definition: pyDAFoam.py:682
dafoam.pyDAFoam.DAOPTION.maxTractionBCIters
maxTractionBCIters
The maximal iterations of tractionDisplacement boundary conditions.
Definition: pyDAFoam.py:542
dafoam.pyDAFoam.PYDAFOAM._readMeshInfo
def _readMeshInfo(self)
Definition: pyDAFoam.py:1482
dafoam.pyDAFoam.DAOPTION.printDAOptions
printDAOptions
Whether to print all DAOption defined in the C++ layer to screen before optimization.
Definition: pyDAFoam.py:452
dafoam.pyDAFoam.TensorFlowHelper.predict
def predict(inputs, n, outputs, m)
Definition: pyDAFoam.py:2265
dafoam.pyDAFoam.DAOPTION.adjUseColoring
adjUseColoring
Whether to use graph coloring to accelerate partial derivative computation.
Definition: pyDAFoam.py:476
dafoam.pyDAFoam.PYDAFOAM.xv
xv
Definition: pyDAFoam.py:1490
dafoam.pyDAFoam.PYDAFOAM._initOption
def _initOption(self, name, value)
Definition: pyDAFoam.py:1913
dafoam.pyDAFoam.PYDAFOAM.setVolCoords
def setVolCoords(self, vol_coords)
Definition: pyDAFoam.py:2031
dafoam.pyDAFoam.PYDAFOAM.basicFamilies
basicFamilies
Definition: pyDAFoam.py:1601
dafoam.pyDAFoam.DAOPTION.designSurfaces
designSurfaces
List of patch names for the design surface.
Definition: pyDAFoam.py:246
dafoam.pyDAFoam.PYDAFOAM.readStateVars
def readStateVars(self, timeVal, deltaT)
Definition: pyDAFoam.py:1256
dafoam.pyDAFoam.PYDAFOAM.calcPrimalResidualStatistics
def calcPrimalResidualStatistics(self, mode)
Definition: pyDAFoam.py:860
dafoam.pyDAFoam.PYDAFOAM.comm
comm
Definition: pyDAFoam.py:1134
dafoam.pyDAFoam.PYDAFOAM.set_solver_input
def set_solver_input(self, inputs, DVGeo=None)
Definition: pyDAFoam.py:1279
dafoam.pyDAFoam.DAOPTION.maxResConLv4JacPCMat
maxResConLv4JacPCMat
The maximal connectivity level for the dRdWTPC matrix.
Definition: pyDAFoam.py:519
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:297
dafoam.pyDAFoam.PYDAFOAM.defaultOptions
defaultOptions
Definition: pyDAFoam.py:1099
dafoam.pyDAFoam.DAOPTION.checkMeshThreshold
checkMeshThreshold
The threshold for check mesh call.
Definition: pyDAFoam.py:560
dafoam.pyDAFoam.DAOPTION.useAD
useAD
Whether to use AD: Mode options: forward, reverse, or fd.
Definition: pyDAFoam.py:389
dafoam.pyDAFoam.PYDAFOAM.wallList
wallList
Definition: pyDAFoam.py:1604
dafoam.pyDAFoam.DAOPTION.writeDeformedFFDs
writeDeformedFFDs
Whether to write deformed FFDs to the disk during optimization, i.e., DVGeo.writeTecplot.
Definition: pyDAFoam.py:575
dafoam.pyDAFoam.PYDAFOAM.getTriangulatedMeshSurface
def getTriangulatedMeshSurface(self, groupName=None, **kwargs)
Definition: pyDAFoam.py:1008
dafoam.pyDAFoam.PYDAFOAM._computeBasicFamilyInfo
def _computeBasicFamilyInfo(self)
Definition: pyDAFoam.py:1594
dafoam.pyDAFoam.PYDAFOAM._initSolver
def _initSolver(self)
Definition: pyDAFoam.py:1346
dafoam.pyDAFoam.PYDAFOAM.setOption
def setOption(self, name, value)
Definition: pyDAFoam.py:1812
dafoam.pyDAFoam.PYDAFOAM.deformDynamicMesh
def deformDynamicMesh(self)
Definition: pyDAFoam.py:1153
dafoam.pyDAFoam.DAOPTION.adjStateOrdering
adjStateOrdering
The ordering of state variable.
Definition: pyDAFoam.py:557
dafoam.pyDAFoam.PYDAFOAM.parallelFlag
parallelFlag
Definition: pyDAFoam.py:1147
dafoam.pyDAFoam.PYDAFOAM.calcFFD2XvSeeds
def calcFFD2XvSeeds(self, DVGeo=None)
Definition: pyDAFoam.py:1305
dafoam.pyDAFoam.DAOPTION.__init__
def __init__(self)
Example "unsteadyCompOutput": {"output1": ["function1", "function2"], "output2": ["function3"]}.
Definition: pyDAFoam.py:54
dafoam.pyDAFoam.PYDAFOAM.dtype
dtype
Definition: pyDAFoam.py:669
dafoam.pyDAFoam.PYDAFOAM._getDefOptions
def _getDefOptions(self)
Definition: pyDAFoam.py:778
dafoam.pyDAFoam.PYDAFOAM.nSolveAdjoints
nSolveAdjoints
Definition: pyDAFoam.py:683
dafoam.pyDAFoam.PYDAFOAM.dynamicMeshDeformed
dynamicMeshDeformed
Definition: pyDAFoam.py:723
dafoam.pyDAFoam.PYDAFOAM.setStates
def setStates(self, states)
Definition: pyDAFoam.py:2021
dafoam.pyDAFoam.PYDAFOAM._checkOptions
def _checkOptions(self)
Definition: pyDAFoam.py:801
dafoam.pyDAFoam.PYDAFOAM._writeDecomposeParDict
def _writeDecomposeParDict(self)
Definition: pyDAFoam.py:2119
dafoam.pyDAFoam.TensorFlowHelper.initialize
def initialize()
Definition: pyDAFoam.py:2245
dafoam.pyDAFoam.PYDAFOAM.getNLocalPoints
def getNLocalPoints(self)
Definition: pyDAFoam.py:2004
dafoam.pyDAFoam.PYDAFOAM.allWallsGroup
allWallsGroup
Definition: pyDAFoam.py:702
dafoam.pyDAFoam.DAOPTION.function
function
Information on objective and constraint functions.
Definition: pyDAFoam.py:210
dafoam.pyDAFoam.DAOPTION.writeJacobians
writeJacobians
Whether to write Jacobian matrices to file for debugging Example: writeJacobians = ["dRdWT",...
Definition: pyDAFoam.py:461
dafoam.pyDAFoam.PYDAFOAM.solverInitialized
solverInitialized
Definition: pyDAFoam.py:675
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:465
dafoam.pyDAFoam.PYDAFOAM.addFamilyGroup
def addFamilyGroup(self, groupName, families)
Definition: pyDAFoam.py:900
dafoam.pyDAFoam.PYDAFOAM.mesh
mesh
Definition: pyDAFoam.py:714
dafoam.pyDAFoam.PYDAFOAM.writeAdjointFields
def writeAdjointFields(self, function, writeTime, psi)
Definition: pyDAFoam.py:866
dafoam.pyDAFoam.TensorFlowHelper
Definition: pyDAFoam.py:2227
dafoam.pyDAFoam.PYDAFOAM.rank
rank
Definition: pyDAFoam.py:1143
dafoam.pyDAFoam.PYDAFOAM.array2Vec
def array2Vec(self, array1)
Definition: pyDAFoam.py:2089
dafoam.pyDAFoam.DAOPTION.useConstrainHbyA
useConstrainHbyA
whether to use the constrainHbyA in the pEqn.
Definition: pyDAFoam.py:396
dafoam.pyDAFoam.PYDAFOAM.updateDAOption
def updateDAOption(self)
Definition: pyDAFoam.py:1982
dafoam.pyDAFoam.PYDAFOAM.getNRegressionParameters
def getNRegressionParameters(self, modelName)
Definition: pyDAFoam.py:1955
dafoam.pyDAFoam.PYDAFOAM.nProcs
nProcs
Definition: pyDAFoam.py:1144
dafoam.pyDAFoam.PYDAFOAM.primalFail
primalFail
Definition: pyDAFoam.py:686
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:536
dafoam.pyDAFoam.PYDAFOAM._initializeComm
def _initializeComm(self, comm)
Definition: pyDAFoam.py:1126
dafoam.pyDAFoam.PYDAFOAM.printFamilyList
def printFamilyList(self)
Definition: pyDAFoam.py:1074