mphys_dafoam.py
Go to the documentation of this file.
1 import sys
2 from openmdao.api import Group, ImplicitComponent, ExplicitComponent, AnalysisError
3 import openmdao.api as om
4 from dafoam import PYDAFOAM
5 from idwarp import USMesh
6 from mphys.builder import Builder
7 import petsc4py
8 from petsc4py import PETSc
9 import numpy as np
10 from mpi4py import MPI
11 from mphys import MaskedConverter, UnmaskedConverter, MaskedVariableDescription
12 from mphys.utils.directory_utils import cd
13 
14 petsc4py.init(sys.argv)
15 
16 
17 class DAFoamBuilder(Builder):
18  """
19  DAFoam builder called from runScript.py
20  """
21 
22  def __init__(
23  self,
24  options, # DAFoam options
25  mesh_options=None, # IDWarp options
26  scenario="aerodynamic", # scenario type to configure the groups
27  prop_coupling=None,
28  run_directory="", # the directory to run this case in, default is the current directory
29  ):
30 
31  # options dictionary for DAFoam
32  self.options = options
33 
34  # mesh warping option. If no design variables are mesh related,
35  # e.g., topology optimization, set it to None.
36  self.mesh_options = mesh_options
37  # flag to determine if the mesh warping component is added
38  # in the nonlinear solver loop (e.g. for aerostructural)
39  # or as a preprocessing step like the surface mesh coordinates
40  # (e.g. for aeropropulsive). This will avoid doing extra work
41  # for mesh deformation when the volume mesh does not change
42  # during nonlinear iterations
43  self.warp_in_solver = False
44  # flag for aerostructural coupling variables
45  self.struct_coupling = False
46  # thermal coupling defaults to false
47  self.thermal_coupling = False
48 
49  # the directory to run this case in, default is the current directory
50  self.run_directory = run_directory
51 
52  # flag for aero-propulsive coupling variables
53  self.prop_coupling = prop_coupling
54 
55  # depending on the scenario we are building for, we adjust a few internal parameters:
56  if scenario.lower() == "aerodynamic":
57  # default
58  pass
59  elif scenario.lower() == "aerostructural":
60  # volume mesh warping needs to be inside the coupling loop for aerostructural
61  self.warp_in_solver = True
62  self.struct_coupling = True
63  elif scenario.lower() == "aerothermal":
64  # volume mesh warping needs to be inside the coupling loop for aerothermal
65  self.thermal_coupling = True
66  else:
67  raise AnalysisError(
68  "scenario %s not valid! Options: aerodynamic, aerostructural, and aerothermal" % scenario
69  )
70 
71  # api level method for all builders
72  def initialize(self, comm):
73 
74  self.comm = comm
75 
76  with cd(self.run_directory):
77  # initialize the PYDAFOAM class, defined in pyDAFoam.py
78  self.DASolver = PYDAFOAM(options=self.options, comm=comm)
79  if self.mesh_options is not None:
80  # always set the mesh
81  mesh = USMesh(options=self.mesh_options, comm=comm)
82  self.DASolver.setMesh(mesh) # add the design surface family group
83  self.DASolver.printFamilyList()
84 
85  def get_solver(self):
86  # this method is only used by the RLT transfer scheme
87  return self.DASolver
88 
89  # api level method for all builders
90  def get_coupling_group_subsystem(self, scenario_name=None):
91  dafoam_group = DAFoamGroup(
92  solver=self.DASolver,
93  use_warper=self.warp_in_solver,
94  struct_coupling=self.struct_coupling,
95  prop_coupling=self.prop_coupling,
96  thermal_coupling=self.thermal_coupling,
97  run_directory=self.run_directory,
98  )
99  return dafoam_group
100 
101  def get_mesh_coordinate_subsystem(self, scenario_name=None):
102 
103  # just return the component that outputs the surface mesh.
104  return DAFoamMesh(solver=self.DASolver)
105 
106  def get_pre_coupling_subsystem(self, scenario_name=None):
107 
108  if self.mesh_options is None:
109  return None
110  else:
111  return DAFoamPrecouplingGroup(
112  solver=self.DASolver, warp_in_solver=self.warp_in_solver, thermal_coupling=self.thermal_coupling
113  )
114 
115  def get_post_coupling_subsystem(self, scenario_name=None):
116  return DAFoamPostcouplingGroup(solver=self.DASolver)
117 
118  def get_number_of_nodes(self, groupName=None):
119  # Get number of aerodynamic nodes
120  if groupName is None:
121  groupName = self.DASolver.couplingSurfacesGroup
122  nodes = int(self.DASolver.getSurfaceCoordinates(groupName=groupName).size / 3)
123 
124  # Add fictitious nodes to root proc, if they are used
125  if self.comm.rank == 0:
126  aerostructDict = self.DASolver.getOption("couplingInfo")["aerostructural"]
127  fvSourceDict = self.DASolver.getOption("fvSource")
128  if aerostructDict["active"] and aerostructDict["propMovement"]:
129  if "fvSource" in aerostructDict.keys():
130  # Iterate through Actuator Disks
131  for fvSource, parameters in aerostructDict["fvSource"].items():
132  # Check if Actuator Disk Exists
133  if fvSource not in fvSourceDict:
134  raise RuntimeWarning("Actuator disk {} not found when adding masked nodes".format(fvSource))
135 
136  # Count Nodes
137  nodes += 1 + parameters["nNodes"]
138  return nodes
139 
140 
141 class DAFoamGroup(Group):
142  """
143  DAFoam solver group
144  """
145 
146  def initialize(self):
147  self.options.declare("solver", recordable=False)
148  self.options.declare("struct_coupling", default=False)
149  self.options.declare("use_warper", default=True)
150  self.options.declare("prop_coupling", default=None)
151  self.options.declare("thermal_coupling", default=False)
152  self.options.declare("run_directory", default="")
153 
154  def setup(self):
155 
156  self.DASolver = self.options["solver"]
157  self.struct_coupling = self.options["struct_coupling"]
158  self.use_warper = self.options["use_warper"]
159  self.prop_coupling = self.options["prop_coupling"]
160  self.thermal_coupling = self.options["thermal_coupling"]
161  self.run_directory = self.options["run_directory"]
162  self.discipline = self.DASolver.getOption("discipline")
163 
164  if self.prop_coupling is not None:
165  if self.prop_coupling not in ["Prop", "Wing"]:
166  raise AnalysisError("prop_coupling can be either Wing or Prop, while %s is given!" % self.prop_coupling)
167 
168  aerostructDict = self.DASolver.getOption("couplingInfo")["aerostructural"]
169  if self.use_warper:
170  # Setup node masking
171  self.mphys_set_masking()
172 
173  # Add propeller movement, if enabled
174  if aerostructDict["active"] and aerostructDict["propMovement"]:
175  prop_movement = DAFoamActuator(solver=self.DASolver)
176  self.add_subsystem("prop_movement", prop_movement, promotes_inputs=["*"], promotes_outputs=["*"])
177 
178  # if we dont have geo_disp, we also need to promote the x_a as x_a0 from the deformer component
179  self.add_subsystem(
180  "deformer",
181  DAFoamWarper(
182  solver=self.DASolver,
183  ),
184  promotes_inputs=[("x_%s" % self.discipline, "x_%s_masked" % self.discipline)],
185  promotes_outputs=["%s_vol_coords" % self.discipline],
186  )
187  elif aerostructDict["active"] and aerostructDict["propMovement"]:
188  raise RuntimeError(
189  "Propeller movement not possible when the warper is outside of the solver. Check for a valid scenario."
190  )
191 
192  if self.prop_coupling is not None:
193  if self.prop_coupling == "Wing":
194  self.add_subsystem(
195  "source",
196  DAFoamFvSource(solver=self.DASolver),
197  promotes_inputs=["*"],
198  promotes_outputs=["*"],
199  )
200 
201  # add the solver implicit component
202  self.add_subsystem(
203  "solver",
204  DAFoamSolver(solver=self.DASolver, prop_coupling=self.prop_coupling, run_directory=self.run_directory),
205  promotes_inputs=["*"],
206  promotes_outputs=["%s_states" % self.discipline],
207  )
208 
209  if self.prop_coupling is not None:
210  if self.prop_coupling == "Prop":
211  self.add_subsystem(
212  "profile",
213  DAFoamPropForce(solver=self.DASolver),
214  promotes_inputs=["*"],
215  promotes_outputs=["*"],
216  )
217 
218  if self.struct_coupling:
219  self.add_subsystem(
220  "force",
221  DAFoamForces(solver=self.DASolver),
222  promotes_inputs=["%s_vol_coords" % self.discipline, "%s_states" % self.discipline],
223  promotes_outputs=[("f_aero", "f_aero_masked")],
224  )
225 
226  if self.thermal_coupling:
227 
228  self.add_subsystem(
229  "get_%s" % self.discipline,
230  DAFoamThermal(solver=self.DASolver),
231  promotes_inputs=["*"],
232  promotes_outputs=["*"],
233  )
234 
235  # Setup unmasking
236  self.mphys_set_unmasking(forces=self.struct_coupling)
237 
239  aerostructDict = self.DASolver.getOption("couplingInfo")["aerostructural"]
240  fvSourceDict = self.DASolver.getOption("fvSource")
241 
242  # Check if Actuator Disk Definitions Exist, only add to Root Proc
243  nodes_prop = 0
244  if self.comm.rank == 0:
245  if aerostructDict["active"] and aerostructDict["propMovement"]:
246  if "fvSource" in aerostructDict.keys():
247  # Iterate through Actuator Disks
248  for fvSource, parameters in aerostructDict["fvSource"].items():
249  # Check if Actuator Disk Exists
250  if fvSource not in fvSourceDict:
251  raise RuntimeWarning("Actuator disk %s not found when adding masked nodes" % fvSource)
252 
253  # Count Nodes
254  nodes_prop += 1 + parameters["nNodes"]
255 
256  # Compute number of aerodynamic nodes
257  nodes_aero = int(self.DASolver.getSurfaceCoordinates(groupName=self.DASolver.designSurfacesGroup).size / 3)
258 
259  # Sum nodes and return all values
260  nodes_total = nodes_aero + nodes_prop
261  return nodes_total, nodes_aero, nodes_prop
262 
263  def mphys_set_masking(self):
264  # Retrieve number of nodes in each category
265  nodes_total, nodes_aero, nodes_prop = self.mphys_compute_nodes()
266 
267  aerostructDict = self.DASolver.getOption("couplingInfo")["aerostructural"]
268 
269  mask = []
270  output = []
271  promotes_inputs = []
272  promotes_outputs = []
273 
274  # Mesh Coordinate Mask
275  mask.append(np.zeros([(nodes_total) * 3], dtype=bool))
276  mask[0][:] = True
277 
278  if nodes_prop > 0:
279  mask[0][3 * nodes_aero :] = False
280 
281  output.append(
282  MaskedVariableDescription("x_%s_masked" % self.discipline, shape=(nodes_aero) * 3, tags=["mphys_coupling"])
283  )
284 
285  promotes_outputs.append("x_%s_masked" % self.discipline)
286 
287  # Add Propeller Masks
288  if aerostructDict["active"] and aerostructDict["propMovement"]:
289  if "fvSource" in aerostructDict.keys():
290  i_fvSource = 0
291  i_start = 3 * nodes_aero
292  for fvSource, parameters in aerostructDict["fvSource"].items():
293  mask.append(np.zeros([(nodes_total) * 3], dtype=bool))
294  mask[i_fvSource + 1][:] = False
295 
296  if self.comm.rank == 0:
297  mask[i_fvSource + 1][i_start : i_start + 3 * (1 + parameters["nNodes"])] = True
298  i_start += 3 * (1 + parameters["nNodes"])
299 
300  output.append(
301  MaskedVariableDescription(
302  "x_prop_%s" % fvSource, shape=((1 + parameters["nNodes"])) * 3, tags=["mphys_coupling"]
303  )
304  )
305  else:
306  output.append(
307  MaskedVariableDescription("x_prop_%s" % fvSource, shape=(0), tags=["mphys_coupling"])
308  )
309 
310  promotes_outputs.append("x_prop_%s" % fvSource)
311 
312  i_fvSource += 1
313 
314  # Define Mask
315  input = MaskedVariableDescription("x_%s" % self.discipline, shape=(nodes_total) * 3, tags=["mphys_coupling"])
316  promotes_inputs.append("x_%s" % self.discipline)
317  masker = MaskedConverter(input=input, output=output, mask=mask, distributed=True, init_output=0.0)
318  self.add_subsystem("masker", masker, promotes_inputs=promotes_inputs, promotes_outputs=promotes_outputs)
319 
320  def mphys_set_unmasking(self, forces=False):
321  # Retrieve number of nodes in each category
322  nodes_total, nodes_aero, nodes_prop = self.mphys_compute_nodes()
323 
324  # If forces are active, generate mask
325  if forces:
326  aerostructDict = self.DASolver.getOption("couplingInfo")["aerostructural"]
327 
328  mask = []
329  input = []
330  promotes_inputs = []
331  promotes_outputs = []
332 
333  # Mesh Coordinate Mask
334  mask.append(np.zeros([(nodes_total) * 3], dtype=bool))
335  mask[0][:] = True
336  if nodes_prop > 0:
337  mask[0][3 * nodes_aero :] = False
338  input.append(MaskedVariableDescription("f_aero_masked", shape=(nodes_aero) * 3, tags=["mphys_coupling"]))
339  promotes_inputs.append("f_aero_masked")
340 
341  if aerostructDict["active"] and aerostructDict["propMovement"]:
342  if "fvSource" in aerostructDict.keys():
343  # Add Propeller Masks
344  i_fvSource = 0
345  i_start = 3 * nodes_aero
346  for fvSource, parameters in aerostructDict["fvSource"].items():
347  mask.append(np.zeros([(nodes_total) * 3], dtype=bool))
348  mask[i_fvSource + 1][:] = False
349 
350  if self.comm.rank == 0:
351  mask[i_fvSource + 1][i_start : i_start + 3 * (1 + parameters["nNodes"])] = True
352  i_start += 3 * (1 + parameters["nNodes"])
353 
354  input.append(
355  MaskedVariableDescription(
356  "f_prop_%s" % fvSource,
357  shape=((1 + parameters["nNodes"])) * 3,
358  tags=["mphys_coordinates"],
359  )
360  )
361  else:
362  input.append(
363  MaskedVariableDescription("f_prop_%s" % fvSource, shape=(0), tags=["mphys_coupling"])
364  )
365  promotes_inputs.append("f_prop_%s" % fvSource)
366 
367  i_fvSource += 1
368 
369  # Define Mask
370  output = MaskedVariableDescription("f_aero", shape=(nodes_total) * 3, tags=["mphys_coupling"])
371  promotes_outputs.append("f_aero")
372  unmasker = UnmaskedConverter(input=input, output=output, mask=mask, distributed=True, default_values=0.0)
373  self.add_subsystem(
374  "force_unmasker", unmasker, promotes_inputs=promotes_inputs, promotes_outputs=promotes_outputs
375  )
376 
377  def mphys_set_options(self, optionDict):
378  # here optionDict should be a dictionary that has a consistent format
379  # with the daOptions defined in the run script
380  self.solver.set_options(optionDict)
381 
382 
384  """
385  Pre-coupling group that configures any components that happen before the solver and post-processor.
386  """
387 
388  def initialize(self):
389  self.options.declare("solver", default=None, recordable=False)
390  self.options.declare("warp_in_solver", default=None, recordable=False)
391  self.options.declare("thermal_coupling", default=None, recordable=False)
392 
393  def setup(self):
394  self.DASolver = self.options["solver"]
395  self.warp_in_solver = self.options["warp_in_solver"]
396  self.thermal_coupling = self.options["thermal_coupling"]
397  self.discipline = self.DASolver.getOption("discipline")
398 
399  aerostructDict = self.DASolver.getOption("couplingInfo")["aerostructural"]
400 
401  # Return the warper only if it is not in the solver
402  if not self.warp_in_solver:
403  if aerostructDict["active"] and aerostructDict["propMovement"]:
404  raise RuntimeError(
405  "Propeller movement not possible when the warper is outside of the solver. Check for a valid scenario."
406  )
407 
408  self.add_subsystem(
409  "warper",
410  DAFoamWarper(solver=self.DASolver),
411  promotes_inputs=["x_%s" % self.discipline],
412  promotes_outputs=["%s_vol_coords" % self.discipline],
413  )
414 
415  # If the warper is in the solver, add other pre-coupling groups if desired
416  else:
417  fvSourceDict = self.DASolver.getOption("fvSource")
418  nodes_prop = 0
419 
420  # Add propeller nodes and subsystem if needed
421  if aerostructDict["active"] and aerostructDict["propMovement"]:
422  self.add_subsystem(
423  "prop_nodes", DAFoamPropNodes(solver=self.DASolver), promotes_inputs=["*"], promotes_outputs=["*"]
424  )
425 
426  # Only add to Root Proc
427  if self.comm.rank == 0:
428  if "fvSource" in aerostructDict.keys():
429  # Iterate through Actuator Disks
430  for fvSource, parameters in aerostructDict["fvSource"].items():
431  # Check if Actuator Disk Exists
432  if fvSource not in fvSourceDict:
433  raise RuntimeWarning("Actuator disk %s not found when adding masked nodes" % fvSource)
434 
435  # Count Nodes
436  nodes_prop += 1 + parameters["nNodes"]
437 
438  nodes_aero = int(self.DASolver.getSurfaceCoordinates(groupName=self.DASolver.designSurfacesGroup).size / 3)
439  nodes_total = nodes_aero + nodes_prop
440 
441  mask = []
442  input = []
443  promotes_inputs = []
444 
445  # Mesh Coordinate Mask
446  mask.append(np.zeros([(nodes_total) * 3], dtype=bool))
447  mask[0][:] = True
448  if nodes_prop > 0:
449  mask[0][3 * nodes_aero :] = False
450  input.append(
451  MaskedVariableDescription(
452  "x_%s0_masked" % self.discipline, shape=(nodes_aero) * 3, tags=["mphys_coordinates"]
453  )
454  )
455  promotes_inputs.append("x_%s0_masked" % self.discipline)
456 
457  # Add propeller movement nodes mask if needed
458  if aerostructDict["active"] and aerostructDict["propMovement"]:
459  # Add Propeller Masks
460  if "fvSource" in aerostructDict.keys():
461  i_fvSource = 0
462  i_start = 3 * nodes_aero
463  for fvSource, parameters in aerostructDict["fvSource"].items():
464  mask.append(np.zeros([(nodes_total) * 3], dtype=bool))
465  mask[i_fvSource + 1][:] = False
466 
467  if self.comm.rank == 0:
468  mask[i_fvSource + 1][i_start : i_start + 3 * (1 + parameters["nNodes"])] = True
469  i_start += 3 * (1 + parameters["nNodes"])
470 
471  input.append(
472  MaskedVariableDescription(
473  "x_prop0_nodes_%s" % fvSource,
474  shape=((1 + parameters["nNodes"])) * 3,
475  tags=["mphys_coordinates"],
476  )
477  )
478  else:
479  input.append(
480  MaskedVariableDescription(
481  "x_prop0_nodes_%s" % fvSource, shape=(0), tags=["mphys_coordinates"]
482  )
483  )
484  promotes_inputs.append("x_prop0_nodes_%s" % fvSource)
485 
486  i_fvSource += 1
487 
488  output = MaskedVariableDescription(
489  "x_%s0" % self.discipline, shape=(nodes_total) * 3, tags=["mphys_coordinates"]
490  )
491 
492  unmasker = UnmaskedConverter(input=input, output=output, mask=mask, distributed=True, default_values=0.0)
493  self.add_subsystem(
494  "unmasker", unmasker, promotes_inputs=promotes_inputs, promotes_outputs=["x_%s0" % self.discipline]
495  )
496 
497  if self.thermal_coupling:
498  self.add_subsystem(
499  "%s_xs" % self.discipline,
500  DAFoamFaceCoords(solver=self.DASolver, groupName=self.DASolver.couplingSurfacesGroup),
501  promotes_inputs=["*"],
502  promotes_outputs=["*"],
503  )
504 
505 
507  """
508  Post-coupling group that configures any components that happen in the post-processor.
509  """
510 
511  def initialize(self):
512  self.options.declare("solver", default=None, recordable=False)
513 
514  def setup(self):
515  self.DASolver = self.options["solver"]
516 
517  # Add Functionals
518  self.add_subsystem("functionals", DAFoamFunctions(solver=self.DASolver), promotes=["*"])
519 
520  # Add Acoustics Data
521  couplingInfo = self.DASolver.getOption("couplingInfo")
522  if couplingInfo["aeroacoustic"]["active"]:
523  for groupName in couplingInfo["aeroacoustic"]["couplingSurfaceGroups"]:
524  self.add_subsystem(
525  groupName, DAFoamAcoustics(solver=self.DASolver, groupName=groupName), promotes_inputs=["*"]
526  )
527 
528  def mphys_add_funcs(self):
529  self.functionals.mphys_add_funcs()
530 
531  def add_dv_func(self, dvName, dv_func):
532  self.functionals.add_dv_func(dvName, dv_func)
533 
534  def mphys_set_options(self, options):
535  self.functionals.mphys_set_options(options)
536 
537 
538 class DAFoamSolver(ImplicitComponent):
539  """
540  OpenMDAO component that wraps the DAFoam flow and adjoint solvers
541  """
542 
543  def initialize(self):
544  self.options.declare("solver", recordable=False)
545  self.options.declare("prop_coupling", recordable=False)
546  self.options.declare("run_directory", default="")
547 
548  def setup(self):
549  # NOTE: the setup function will be called everytime a new scenario is created.
550 
551  self.prop_coupling = self.options["prop_coupling"]
552 
553  self.DASolver = self.options["solver"]
554  DASolver = self.DASolver
555 
556  self.run_directory = self.options["run_directory"]
557 
558  self.discipline = self.DASolver.getOption("discipline")
559 
561 
562  # by default, we will not have a separate optionDict attached to this
563  # solver. But if we do multipoint optimization, we need to use the
564  # optionDict for each point because each point may have different
565  # objFunc and primalBC options
566  self.optionDict = None
567 
568  # Initialize the design variable functions, e.g., aoa, actuator
569  self.dv_funcs = {}
570 
571  # initialize the dRdWT matrix-free matrix in DASolver
572  DASolver.solverAD.initializedRdWTMatrixFree(DASolver.xvVec, DASolver.wVec)
573 
574  # create the adjoint vector
575  self.psi = self.DASolver.wVec.duplicate()
576  self.psi.zeroEntries()
577 
578  # if true, we need to compute the coloring
579  if DASolver.getOption("adjEqnSolMethod") == "fixedPoint":
580  self.runColoring = False
581  else:
582  self.runColoring = True
583 
584  # determine which function to compute the adjoint
585  self.evalFuncs = []
586  DASolver.setEvalFuncs(self.evalFuncs)
587 
588  local_state_size = DASolver.getNLocalAdjointStates()
589 
590  designVariables = DASolver.getOption("designVar")
591 
592  # get the dvType dict
593  self.dvType = {}
594  for dvName in list(designVariables.keys()):
595  self.dvType[dvName] = designVariables[dvName]["designVarType"]
596 
597  # setup input and output for the solver
598  # we need to add states for all cases
599  self.add_output(
600  "%s_states" % self.discipline, distributed=True, shape=local_state_size, tags=["mphys_coupling"]
601  )
602 
603  couplingInfo = DASolver.getOption("couplingInfo")
604  if couplingInfo["aerothermal"]["active"]:
605  nCells, nFaces = self.DASolver._getSurfaceSize(self.DASolver.couplingSurfacesGroup)
606  # NOTE: here we create two duplicated surface center coords, so the size is nFaces * 2
607  # one is for transferring near wall temperature, the other is for transferring k/d coefficients
608  if self.discipline == "aero":
609  self.add_input("T_convect", distributed=True, shape=2 * nFaces, tags=["mphys_coupling"])
610  if self.discipline == "thermal":
611  self.add_input("q_conduct", distributed=True, shape=2 * nFaces, tags=["mphys_coupling"])
612 
613  # now loop over the design variable keys to determine which other variables we need to add
614  shapeVarAdded = False
615  for dvName in list(designVariables.keys()):
616  dvType = self.dvType[dvName]
617  if dvType == "FFD": # add shape variables
618  if shapeVarAdded is False: # we add the shape variable only once
619  # NOTE: for shape variables, we add dafoam_vol_coords as the input name
620  # the specific name for this shape variable will be added in the geometry component (DVGeo)
621  self.add_input(
622  "%s_vol_coords" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"]
623  )
624  shapeVarAdded = True
625  elif dvType == "AOA": # add angle of attack variable
626  self.add_input(dvName, distributed=False, shape_by_conn=True, tags=["mphys_coupling"])
627  elif dvType == "BC": # add boundary conditions
628  self.add_input(dvName, distributed=False, shape_by_conn=True, tags=["mphys_coupling"])
629  elif dvType == "ACTD": # add actuator parameter variables
630  nACTDVars = 10
631  if "comps" in list(designVariables[dvName].keys()):
632  nACTDVars = len(designVariables[dvName]["comps"])
633  self.add_input(dvName, distributed=False, shape=nACTDVars, tags=["mphys_coupling"])
634  elif dvType == "Field": # add field variables
635  self.add_input(dvName, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
636  else:
637  raise AnalysisError("designVarType %s not supported! " % dvType)
638 
639  def add_dv_func(self, dvName, dv_func):
640  # add a design variable function to self.dv_func
641  # we need to call this function in runScript.py everytime we define a new dv_func, e.g., aoa, actuator
642  # no need to call this for the shape variables because they will be handled in the geometry component
643  # the dv_func should have two inputs, (dvVal, DASolver)
644  if dvName in self.dv_funcs:
645  raise AnalysisError("dvName %s is already in self.dv_funcs! " % dvName)
646  else:
647  self.dv_funcs[dvName] = dv_func
648 
649  def set_options(self, optionDict):
650  # here optionDict should be a dictionary that has a consistent format
651  # with the daOptions defined in the run script
652  self.optionDict = optionDict
653 
654  def apply_options(self, optionDict):
655  if optionDict is not None:
656  # This is a multipoint optimization. We need to replace the
657  # daOptions with optionDict
658  for key in optionDict.keys():
659  self.DASolver.setOption(key, optionDict[key])
660  self.DASolver.updateDAOption()
661 
662  # calculate the residual
663  def apply_nonlinear(self, inputs, outputs, residuals):
664  DASolver = self.DASolver
665  DASolver.setStates(outputs["%s_states" % self.discipline])
666 
667  # get flow residuals from DASolver
668  residuals["%s_states" % self.discipline] = DASolver.getResiduals()
669 
670  # solve the flow
671  def solve_nonlinear(self, inputs, outputs):
672 
673  with cd(self.run_directory):
674 
675  DASolver = self.DASolver
676 
677  # set the runStatus, this is useful when the actuator term is activated
678  DASolver.setOption("runStatus", "solvePrimal")
679 
680  # assign the optionDict to the solver
681  self.apply_options(self.optionDict)
682 
683  # now call the dv_funcs to update the design variables
684  for dvName in self.dv_funcs:
685  func = self.dv_funcs[dvName]
686  dvVal = inputs[dvName]
687  func(dvVal, DASolver)
688 
689  DASolver.updateDAOption()
690 
691  couplingInfo = DASolver.getOption("couplingInfo")
692  if couplingInfo["aerothermal"]["active"]:
693  if self.discipline == "aero":
694  T_convect = inputs["T_convect"]
695  DASolver.solver.setThermal(T_convect)
696  elif self.discipline == "thermal":
697  q_conduct = inputs["q_conduct"]
698  DASolver.solver.setThermal(q_conduct)
699  else:
700  raise AnalysisError("discipline not valid!")
701 
702  # solve the flow with the current design variable
703  DASolver()
704 
705  # get the objective functions
706  funcs = {}
707  DASolver.evalFunctions(funcs, evalFuncs=self.evalFuncs)
708 
709  # assign the computed flow states to outputs
710  outputs["%s_states" % self.discipline] = DASolver.getStates()
711 
712  # if the primal solution fail, we return analysisError and let the optimizer handle it
713  fail = funcs["fail"]
714  if fail:
715  raise AnalysisError("Primal solution failed!")
716 
717  def linearize(self, inputs, outputs, residuals):
718  # NOTE: we do not do any computation in this function, just print some information
719 
720  self.DASolver.setStates(outputs["%s_states" % self.discipline])
721 
722  def apply_linear(self, inputs, outputs, d_inputs, d_outputs, d_residuals, mode):
723  # compute the matrix vector products for states and volume mesh coordinates
724  # i.e., dRdWT*psi, dRdXvT*psi
725 
726  # we do not support forward mode
727  if mode == "fwd":
728  om.issue_warning(
729  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
730  prefix="",
731  stacklevel=2,
732  category=om.OpenMDAOWarning,
733  )
734  return
735 
736  DASolver = self.DASolver
737 
738  # assign the optionDict to the solver
739  self.apply_options(self.optionDict)
740 
741  # now call the dv_funcs to update the design variables
742  for dvName in self.dv_funcs:
743  func = self.dv_funcs[dvName]
744  dvVal = inputs[dvName]
745  func(dvVal, DASolver)
746 
747  # assign the states in outputs to the OpenFOAM flow fields
748  DASolver.setStates(outputs["%s_states" % self.discipline])
749 
750  designVariables = DASolver.getOption("designVar")
751 
752  if "%s_states" % self.discipline in d_residuals:
753 
754  # get the reverse mode AD seed from d_residuals
755  resBar = d_residuals["%s_states" % self.discipline]
756  # convert the seed array to Petsc vector
757  resBarVec = DASolver.array2Vec(resBar)
758 
759  # this computes [dRdW]^T*Psi using reverse mode AD
760  if "%s_states" % self.discipline in d_outputs:
761  prodVec = DASolver.wVec.duplicate()
762  prodVec.zeroEntries()
763  DASolver.solverAD.calcdRdWTPsiAD(DASolver.xvVec, DASolver.wVec, resBarVec, prodVec)
764  wBar = DASolver.vec2Array(prodVec)
765  d_outputs["%s_states" % self.discipline] += wBar
766 
767  # loop over all d_inputs keys and compute the matrix-vector products accordingly
768  for inputName in list(d_inputs.keys()):
769  # this computes [dRdXv]^T*Psi using reverse mode AD
770  if inputName == "%s_vol_coords" % self.discipline:
771  prodVec = DASolver.xvVec.duplicate()
772  prodVec.zeroEntries()
773  DASolver.solverAD.calcdRdXvTPsiAD(DASolver.xvVec, DASolver.wVec, resBarVec, prodVec)
774  xVBar = DASolver.vec2Array(prodVec)
775  d_inputs["%s_vol_coords" % self.discipline] += xVBar
776  elif inputName == "q_conduct":
777  # calculate [dRdQ]^T*Psi for thermal
778  volCoords = inputs["%s_vol_coords" % self.discipline]
779  states = outputs["%s_states" % self.discipline]
780  thermal = inputs["q_conduct"]
781  product = np.zeros_like(thermal)
782  DASolver.solverAD.calcdRdThermalTPsiAD(volCoords, states, thermal, resBar, product)
783  d_inputs["q_conduct"] += product
784  elif inputName == "T_convect":
785  # calculate [dRdT]^T*Psi for aero
786  volCoords = inputs["%s_vol_coords" % self.discipline]
787  states = outputs["%s_states" % self.discipline]
788  thermal = inputs["T_convect"]
789  product = np.zeros_like(thermal)
790  DASolver.solverAD.calcdRdThermalTPsiAD(volCoords, states, thermal, resBar, product)
791  d_inputs["T_convect"] += product
792  else: # now we deal with general input output names
793  # compute [dRdAOA]^T*Psi using reverse mode AD
794  if self.dvType[inputName] == "AOA":
795  prodVec = PETSc.Vec().create(self.comm)
796  prodVec.setSizes((PETSc.DECIDE, 1), bsize=1)
797  prodVec.setFromOptions()
798  DASolver.solverAD.calcdRdAOATPsiAD(
799  DASolver.xvVec, DASolver.wVec, resBarVec, inputName.encode(), prodVec
800  )
801  # The aoaBar variable will be length 1 on the root proc, but length 0 an all slave procs.
802  # The value on the root proc must be broadcast across all procs.
803  if self.comm.rank == 0:
804  aoaBar = DASolver.vec2Array(prodVec)[0]
805  else:
806  aoaBar = 0.0
807 
808  d_inputs[inputName] += self.comm.bcast(aoaBar, root=0)
809 
810  # compute [dRdBC]^T*Psi using reverse mode AD
811  elif self.dvType[inputName] == "BC":
812  prodVec = PETSc.Vec().create(self.comm)
813  prodVec.setSizes((PETSc.DECIDE, 1), bsize=1)
814  prodVec.setFromOptions()
815  DASolver.solverAD.calcdRdBCTPsiAD(
816  DASolver.xvVec, DASolver.wVec, resBarVec, inputName.encode(), prodVec
817  )
818  # The BCBar variable will be length 1 on the root proc, but length 0 an all slave procs.
819  # The value on the root proc must be broadcast across all procs.
820  if self.comm.rank == 0:
821  BCBar = DASolver.vec2Array(prodVec)[0]
822  else:
823  BCBar = 0.0
824 
825  d_inputs[inputName] += self.comm.bcast(BCBar, root=0)
826 
827  # compute [dRdActD]^T*Psi using reverse mode AD
828  elif self.dvType[inputName] == "ACTD":
829  prodVec = PETSc.Vec().create(self.comm)
830  prodVec.setSizes((PETSc.DECIDE, 10), bsize=1)
831  prodVec.setFromOptions()
832  DASolver.solverAD.calcdRdActTPsiAD(
833  DASolver.xvVec, DASolver.wVec, resBarVec, inputName.encode(), prodVec
834  )
835  # we will convert the MPI prodVec to seq array for all procs
836  ACTDBar = DASolver.convertMPIVec2SeqArray(prodVec)
837  if "comps" in list(designVariables[inputName].keys()):
838  nACTDVars = len(designVariables[inputName]["comps"])
839  ACTDBarSub = np.zeros(nACTDVars, "d")
840  for i in range(nACTDVars):
841  comp = designVariables[inputName]["comps"][i]
842  ACTDBarSub[i] = ACTDBar[comp]
843  d_inputs[inputName] += ACTDBarSub
844  else:
845  d_inputs[inputName] += ACTDBar
846 
847  # compute dRdFieldT*Psi using reverse mode AD
848  elif self.dvType[inputName] == "Field":
849  nLocalCells = self.DASolver.solver.getNLocalCells()
850  fieldType = DASolver.getOption("designVar")[inputName]["fieldType"]
851  fieldComp = 1
852  if fieldType == "vector":
853  fieldComp = 3
854  nLocalSize = nLocalCells * fieldComp
855  prodVec = PETSc.Vec().create(self.comm)
856  prodVec.setSizes((nLocalSize, PETSc.DECIDE), bsize=1)
857  prodVec.setFromOptions()
858  DASolver.solverAD.calcdRdFieldTPsiAD(
859  DASolver.xvVec, DASolver.wVec, resBarVec, inputName.encode(), prodVec
860  )
861  fieldBar = DASolver.vec2Array(prodVec)
862  d_inputs[inputName] += fieldBar
863 
864  else:
865  raise AnalysisError("designVarType %s not supported! " % self.dvType[inputName])
866 
867  def solve_linear(self, d_outputs, d_residuals, mode):
868  # solve the adjoint equation [dRdW]^T * Psi = dFdW
869 
870  # we do not support forward mode
871  if mode == "fwd":
872  om.issue_warning(
873  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
874  prefix="",
875  stacklevel=2,
876  category=om.OpenMDAOWarning,
877  )
878  return
879 
880  with cd(self.run_directory):
881 
882  DASolver = self.DASolver
883 
884  # set the runStatus, this is useful when the actuator term is activated
885  DASolver.setOption("runStatus", "solveAdjoint")
886  DASolver.updateDAOption()
887 
888  adjEqnSolMethod = DASolver.getOption("adjEqnSolMethod")
889 
890  # right hand side array from d_outputs
891  dFdWArray = d_outputs["%s_states" % self.discipline]
892  # convert the array to vector
893  dFdW = DASolver.array2Vec(dFdWArray)
894 
895  # run coloring
896  if self.DASolver.getOption("adjUseColoring") and self.runColoring:
897  self.DASolver.runColoring()
898  self.runColoring = False
899 
900  if adjEqnSolMethod == "Krylov":
901  # solve the adjoint equation using the Krylov method
902 
903  # if writeMinorIterations=True, we rename the solution in pyDAFoam.py. So we don't recompute the PC
904  if DASolver.getOption("writeMinorIterations"):
905  if DASolver.dRdWTPC is None or DASolver.ksp is None:
906  DASolver.dRdWTPC = PETSc.Mat().create(self.comm)
907  DASolver.solver.calcdRdWT(DASolver.xvVec, DASolver.wVec, 1, DASolver.dRdWTPC)
908  DASolver.ksp = PETSc.KSP().create(self.comm)
909  DASolver.solverAD.createMLRKSPMatrixFree(DASolver.dRdWTPC, DASolver.ksp)
910  # otherwise, we need to recompute the PC mat based on adjPCLag
911  else:
912  # NOTE: this function will be called multiple times (one time for one obj func) in each opt iteration
913  # so we don't want to print the total info and recompute PC for each obj, we need to use renamed
914  # to check if a recompute is needed. In other words, we only recompute the PC for the first obj func
915  # adjoint solution
916  solutionTime, renamed = DASolver.renameSolution(self.solution_counter)
917  if renamed:
918  # write the deformed FFD for post-processing
919  # DASolver.writeDeformedFFDs(self.solution_counter)
920  # print the solution counter
921  if self.comm.rank == 0:
922  print("Driver total derivatives for iteration: %d" % self.solution_counter)
923  print("---------------------------------------------")
924  self.solution_counter += 1
925 
926  # compute the preconditioner matrix for the adjoint linear equation solution
927  # and initialize the ksp object. We reinitialize them every adjPCLag
928  adjPCLag = DASolver.getOption("adjPCLag")
929  if DASolver.dRdWTPC is None or DASolver.ksp is None or (self.solution_counter - 1) % adjPCLag == 0:
930  if renamed:
931  # calculate the PC mat
932  if DASolver.dRdWTPC is not None:
933  DASolver.dRdWTPC.destroy()
934  DASolver.dRdWTPC = PETSc.Mat().create(self.comm)
935  DASolver.solver.calcdRdWT(DASolver.xvVec, DASolver.wVec, 1, DASolver.dRdWTPC)
936  # reset the KSP
937  if DASolver.ksp is not None:
938  DASolver.ksp.destroy()
939  DASolver.ksp = PETSc.KSP().create(self.comm)
940  DASolver.solverAD.createMLRKSPMatrixFree(DASolver.dRdWTPC, DASolver.ksp)
941 
942  # if useNonZeroInitGuess is False, we will manually reset self.psi to zero
943  # this is important because we need the correct psi to update the KSP tolerance
944  # in the next line
945  if not self.DASolver.getOption("adjEqnOption")["useNonZeroInitGuess"]:
946  self.psi.set(0)
947 
948  if self.DASolver.getOption("adjEqnOption")["dynAdjustTol"]:
949  # if we want to dynamically adjust the tolerance, call this function. This is mostly used
950  # in the block Gauss-Seidel method in two discipline coupling
951  # update the KSP tolerances the coupled adjoint before solving
952  self._updateKSPTolerances(self.psi, dFdW, DASolver.ksp)
953 
954  # actually solving the adjoint linear equation using Petsc
955  fail = DASolver.solverAD.solveLinearEqn(DASolver.ksp, dFdW, self.psi)
956  elif adjEqnSolMethod == "fixedPoint":
957  solutionTime, renamed = DASolver.renameSolution(self.solution_counter)
958  if renamed:
959  # write the deformed FFD for post-processing
960  # DASolver.writeDeformedFFDs(self.solution_counter)
961  # print the solution counter
962  if self.comm.rank == 0:
963  print("Driver total derivatives for iteration: %d" % self.solution_counter)
964  print("---------------------------------------------")
965  self.solution_counter += 1
966  # solve the adjoint equation using the fixed-point adjoint approach
967  fail = DASolver.solverAD.runFPAdj(DASolver.xvVec, DASolver.wVec, dFdW, self.psi)
968  else:
969  raise RuntimeError("adjEqnSolMethod=%s not valid! Options are: Krylov or fixedPoint" % adjEqnSolMethod)
970 
971  # convert the solution vector to array and assign it to d_residuals
972  d_residuals["%s_states" % self.discipline] = DASolver.vec2Array(self.psi)
973 
974  # if the adjoint solution fail, we return analysisError and let the optimizer handle it
975  if fail:
976  raise AnalysisError("Adjoint solution failed!")
977 
978  def _updateKSPTolerances(self, psi, dFdW, ksp):
979  # Here we need to manually update the KSP tolerances because the default
980  # relative tolerance will always want to converge the adjoint to a fixed
981  # tolerance during the LINGS adjoint solution. However, what we want is
982  # to converge just a few orders of magnitude. Here we need to bypass the
983  # rTol in Petsc and manually calculate the aTol.
984 
985  DASolver = self.DASolver
986  # calculate the initial residual for the adjoint before solving
987  rVec = self.DASolver.wVec.duplicate()
988  rVec.zeroEntries()
989  DASolver.solverAD.calcdRdWTPsiAD(DASolver.xvVec, DASolver.wVec, psi, rVec)
990  rVec.axpy(-1.0, dFdW)
991  rNorm = rVec.norm()
992  # read the rTol and aTol from DAOption
993  rTol0 = self.DASolver.getOption("adjEqnOption")["gmresRelTol"]
994  aTol0 = self.DASolver.getOption("adjEqnOption")["gmresAbsTol"]
995  # calculate the new absolute tolerance that gives you rTol residual drop
996  aTolNew = rNorm * rTol0
997  # if aTolNew is smaller than aTol0, assign aTol0 to aTolNew
998  if aTolNew < aTol0:
999  aTolNew = aTol0
1000  # assign the atolNew and distable rTol
1001  ksp.setTolerances(rtol=0.0, atol=aTolNew, divtol=None, max_it=None)
1002 
1003 
1004 class DAFoamMeshGroup(Group):
1005  def initialize(self):
1006  self.options.declare("solver", recordable=False)
1007 
1008  def setup(self):
1009  DASolver = self.options["solver"]
1010 
1011  self.discipline = self.DASolver.getOption("discipline")
1012 
1013  self.add_subsystem("surface_mesh", DAFoamMesh(solver=DASolver), promotes=["*"])
1014  self.add_subsystem(
1015  "volume_mesh",
1016  DAFoamWarper(solver=DASolver),
1017  promotes_inputs=[("x_%s_masked" % self.discipline, "x_%s0" % self.discipline)],
1018  promotes_outputs=["%s_vol_coords" % self.discipline],
1019  )
1020 
1022  # just pass through the call
1023  return self.surface_mesh.mphys_add_coordinate_input()
1024 
1026  # just pass through the call
1027  return self.surface_mesh.mphys_get_triangulated_surface()
1028 
1029 
1030 class DAFoamMesh(ExplicitComponent):
1031  """
1032  Component to get the partitioned initial surface mesh coordinates
1033  """
1034 
1035  def initialize(self):
1036  self.options.declare("solver", recordable=False)
1037 
1038  def setup(self):
1039 
1040  self.DASolver = self.options["solver"]
1041 
1042  self.discipline = self.DASolver.getOption("discipline")
1043 
1044  # design surface coordinates
1045  self.x_a0 = self.DASolver.getSurfaceCoordinates(self.DASolver.designSurfacesGroup).flatten(order="C")
1046 
1047  # add output
1048  coord_size = self.x_a0.size
1049  self.add_output(
1050  "x_%s0" % self.discipline,
1051  distributed=True,
1052  shape=coord_size,
1053  desc="initial aerodynamic surface node coordinates",
1054  tags=["mphys_coordinates"],
1055  )
1056 
1058  self.add_input(
1059  "x_%s0_points" % self.discipline,
1060  distributed=True,
1061  shape_by_conn=True,
1062  desc="aerodynamic surface with geom changes",
1063  )
1064 
1065  # return the promoted name and coordinates
1066  return "x_%s0_points" % self.discipline, self.x_a0
1067 
1069  return self.x_a0
1070 
1071  def mphys_get_triangulated_surface(self, groupName=None):
1072  # this is a list of lists of 3 points
1073  # p0, v1, v2
1074 
1075  return self.DASolver.getTriangulatedMeshSurface()
1076 
1077  def mphys_get_surface_size(self, groupName):
1078  return self.DASolver._getSurfaceSize(groupName)
1079 
1080  def compute(self, inputs, outputs):
1081  # just assign the surface mesh coordinates
1082  if "x_%s0_points" % self.discipline in inputs:
1083  outputs["x_%s0" % self.discipline] = inputs["x_%s0_points" % self.discipline]
1084  else:
1085  outputs["x_%s0" % self.discipline] = self.x_a0
1086 
1087  def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
1088  # we do not support forward mode AD
1089  if mode == "fwd":
1090  om.issue_warning(
1091  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1092  prefix="",
1093  stacklevel=2,
1094  category=om.OpenMDAOWarning,
1095  )
1096  return
1097 
1098  # just assign the matrix-vector product
1099  if "x_%s0_points" % self.discipline in d_inputs:
1100  d_inputs["x_%s0_points" % self.discipline] += d_outputs["x_%s0" % self.discipline]
1101 
1102 
1103 class DAFoamFunctions(ExplicitComponent):
1104  """
1105  DAFoam objective and constraint functions component
1106  """
1107 
1108  def initialize(self):
1109  self.options.declare("solver", recordable=False)
1110 
1111  # a list that contains all function names, e.g., CD, CL
1112  self.funcs = None
1113 
1114  def setup(self):
1115 
1116  self.DASolver = self.options["solver"]
1117 
1118  self.discipline = self.DASolver.getOption("discipline")
1119 
1120  # init the dv_funcs
1121  self.dv_funcs = {}
1122 
1123  self.optionDict = None
1124 
1125  # get the dvType dict
1126  designVariables = self.DASolver.getOption("designVar")
1127  self.dvType = {}
1128  for dvName in list(designVariables.keys()):
1129  self.dvType[dvName] = designVariables[dvName]["designVarType"]
1130 
1131  # setup input and output for the function
1132  # we need to add states for all cases
1133  self.add_input("%s_states" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1134 
1135  # now loop over the design variable keys to determine which other variables we need to add
1136  shapeVarAdded = False
1137  for dvName in list(designVariables.keys()):
1138  dvType = self.dvType[dvName]
1139  if dvType == "FFD": # add shape variables
1140  if shapeVarAdded is False: # we add the shape variable only once
1141  # NOTE: for shape variables, we add dafoam_vol_coords as the input name
1142  # the specific name for this shape variable will be added in the geometry component (DVGeo)
1143  self.add_input(
1144  "%s_vol_coords" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"]
1145  )
1146  shapeVarAdded = True
1147  elif dvType == "AOA": # add angle of attack variable
1148  self.add_input(dvName, distributed=False, shape_by_conn=True, tags=["mphys_coupling"])
1149  elif dvType == "BC": # add boundary conditions
1150  self.add_input(dvName, distributed=False, shape_by_conn=True, tags=["mphys_coupling"])
1151  elif dvType == "ACTD": # add actuator parameter variables
1152  nACTDVars = 10
1153  if "comps" in list(designVariables[dvName].keys()):
1154  nACTDVars = len(designVariables[dvName]["comps"])
1155  self.add_input(dvName, distributed=False, shape=nACTDVars, tags=["mphys_coupling"])
1156  elif dvType == "Field": # add field variables
1157  self.add_input(dvName, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1158  else:
1159  raise AnalysisError("designVarType %s not supported! " % dvType)
1160 
1161  def mphys_add_funcs(self):
1162  # add the function names to this component, called from runScript.py
1163 
1164  # it is called objFunc in DAOptions but it contains both objective and constraint functions
1165  objFuncs = self.DASolver.getOption("objFunc")
1166 
1167  self.funcs = []
1168 
1169  for objFunc in objFuncs:
1170  self.funcs.append(objFunc)
1171 
1172  # loop over the functions here and create the output
1173  for f_name in self.funcs:
1174  self.add_output(f_name, distributed=False, shape=1, units=None, tags=["mphys_result"])
1175 
1176  def add_dv_func(self, dvName, dv_func):
1177  # add a design variable function to self.dv_func
1178  # we need to call this function in runScript.py everytime we define a new dv_func, e.g., aoa, actuator
1179  # no need to call this for the shape variables because they will be handled in the geometry component
1180  # the dv_func should have two inputs, (dvVal, DASolver)
1181  if dvName in self.dv_funcs:
1182  raise AnalysisError("dvName %s is already in self.dv_funcs! " % dvName)
1183  else:
1184  self.dv_funcs[dvName] = dv_func
1185 
1186  def mphys_set_options(self, optionDict):
1187  # here optionDict should be a dictionary that has a consistent format
1188  # with the daOptions defined in the run script
1189  self.optionDict = optionDict
1190 
1191  def apply_options(self, optionDict):
1192  if optionDict is not None:
1193  # This is a multipoint optimization. We need to replace the
1194  # daOptions with optionDict
1195  for key in optionDict.keys():
1196  self.DASolver.setOption(key, optionDict[key])
1197  self.DASolver.updateDAOption()
1198 
1199  # get the objective function from DASolver
1200  def compute(self, inputs, outputs):
1201 
1202  DASolver = self.DASolver
1203 
1204  DASolver.setStates(inputs["%s_states" % self.discipline])
1205 
1206  funcs = {}
1207 
1208  if self.funcs is not None:
1209  DASolver.evalFunctions(funcs, evalFuncs=self.funcs)
1210  for f_name in self.funcs:
1211  if f_name in funcs:
1212  outputs[f_name] = funcs[f_name]
1213 
1214  # compute the partial derivatives of functions
1215  def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
1216 
1217  DASolver = self.DASolver
1218 
1219  # set the runStatus, this is useful when the actuator term is activated
1220  DASolver.setOption("runStatus", "solveAdjoint")
1221  DASolver.updateDAOption()
1222 
1223  designVariables = DASolver.getOption("designVar")
1224 
1225  # assign the optionDict to the solver
1226  self.apply_options(self.optionDict)
1227  # now call the dv_funcs to update the design variables
1228  for dvName in self.dv_funcs:
1229  func = self.dv_funcs[dvName]
1230  dvVal = inputs[dvName]
1231  func(dvVal, DASolver)
1232  DASolver.setStates(inputs["%s_states" % self.discipline])
1233 
1234  # we do not support forward mode AD
1235  if mode == "fwd":
1236  om.issue_warning(
1237  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1238  prefix="",
1239  stacklevel=2,
1240  category=om.OpenMDAOWarning,
1241  )
1242  return
1243 
1244  funcsBar = {}
1245 
1246  # assign value to funcsBar. NOTE: we only assign seed if d_outputs has
1247  # non-zero values!
1248  if self.funcs is None:
1249  raise AnalysisError("functions not set! Forgot to call mphys_add_funcs?")
1250  else:
1251  for func_name in self.funcs:
1252  if func_name in d_outputs and d_outputs[func_name] != 0.0:
1253  funcsBar[func_name] = d_outputs[func_name][0]
1254 
1255  # if self.comm.rank == 0:
1256  # print(funcsBar)
1257 
1258  if self.comm.rank == 0:
1259  print("Computing partials for ", list(funcsBar.keys()))
1260 
1261  # loop over all d_inputs keys and compute the partials accordingly
1262  for objFuncName in list(funcsBar.keys()):
1263 
1264  fBar = funcsBar[objFuncName]
1265 
1266  for inputName in list(d_inputs.keys()):
1267 
1268  # compute dFdW * fBar
1269  if inputName == "%s_states" % self.discipline:
1270  dFdW = DASolver.wVec.duplicate()
1271  dFdW.zeroEntries()
1272  DASolver.solverAD.calcdFdWAD(DASolver.xvVec, DASolver.wVec, objFuncName.encode(), dFdW)
1273  wBar = DASolver.vec2Array(dFdW)
1274  d_inputs["%s_states" % self.discipline] += wBar * fBar
1275 
1276  # compute dFdW * fBar
1277  elif inputName == "%s_vol_coords" % self.discipline:
1278  dFdXv = DASolver.xvVec.duplicate()
1279  dFdXv.zeroEntries()
1280  DASolver.solverAD.calcdFdXvAD(
1281  DASolver.xvVec, DASolver.wVec, objFuncName.encode(), "dummy".encode(), dFdXv
1282  )
1283  xVBar = DASolver.vec2Array(dFdXv)
1284  d_inputs["%s_vol_coords" % self.discipline] += xVBar * fBar
1285 
1286  # now we deal with general input input names
1287  else:
1288  # compute dFdAOA
1289  if self.dvType[inputName] == "AOA":
1290  dFdAOA = PETSc.Vec().create(self.comm)
1291  dFdAOA.setSizes((PETSc.DECIDE, 1), bsize=1)
1292  dFdAOA.setFromOptions()
1293  DASolver.calcdFdAOAAnalytical(objFuncName, dFdAOA)
1294 
1295  # The aoaBar variable will be length 1 on the root proc, but length 0 an all slave procs.
1296  # The value on the root proc must be broadcast across all procs.
1297  if self.comm.rank == 0:
1298  aoaBar = DASolver.vec2Array(dFdAOA)[0] * fBar
1299  else:
1300  aoaBar = 0.0
1301 
1302  d_inputs[inputName] += self.comm.bcast(aoaBar, root=0)
1303 
1304  # compute dFdBC
1305  elif self.dvType[inputName] == "BC":
1306  dFdBC = PETSc.Vec().create(self.comm)
1307  dFdBC.setSizes((PETSc.DECIDE, 1), bsize=1)
1308  dFdBC.setFromOptions()
1309  DASolver.solverAD.calcdFdBCAD(
1310  DASolver.xvVec, DASolver.wVec, objFuncName.encode(), inputName.encode(), dFdBC
1311  )
1312  # The BCBar variable will be length 1 on the root proc, but length 0 an all slave procs.
1313  # The value on the root proc must be broadcast across all procs.
1314  if self.comm.rank == 0:
1315  BCBar = DASolver.vec2Array(dFdBC)[0] * fBar
1316  else:
1317  BCBar = 0.0
1318 
1319  d_inputs[inputName] += self.comm.bcast(BCBar, root=0)
1320 
1321  # compute dFdActD
1322  elif self.dvType[inputName] == "ACTD":
1323  dFdACTD = PETSc.Vec().create(self.comm)
1324  dFdACTD.setSizes((PETSc.DECIDE, 10), bsize=1)
1325  dFdACTD.setFromOptions()
1326  DASolver.solverAD.calcdFdACTAD(
1327  DASolver.xvVec, DASolver.wVec, objFuncName.encode(), inputName.encode(), dFdACTD
1328  )
1329  # we will convert the MPI dFdACTD to seq array for all procs
1330  ACTDBar = DASolver.convertMPIVec2SeqArray(dFdACTD)
1331  if "comps" in list(designVariables[inputName].keys()):
1332  nACTDVars = len(designVariables[inputName]["comps"])
1333  ACTDBarSub = np.zeros(nACTDVars, "d")
1334  for i in range(nACTDVars):
1335  comp = designVariables[inputName]["comps"][i]
1336  ACTDBarSub[i] = ACTDBar[comp]
1337  d_inputs[inputName] += ACTDBarSub * fBar
1338  else:
1339  d_inputs[inputName] += ACTDBar * fBar
1340 
1341  # compute dFdField
1342  elif self.dvType[inputName] == "Field":
1343  nLocalCells = self.DASolver.solver.getNLocalCells()
1344  fieldType = DASolver.getOption("designVar")[inputName]["fieldType"]
1345  fieldComp = 1
1346  if fieldType == "vector":
1347  fieldComp = 3
1348  nLocalSize = nLocalCells * fieldComp
1349  dFdField = PETSc.Vec().create(self.comm)
1350  dFdField.setSizes((nLocalSize, PETSc.DECIDE), bsize=1)
1351  dFdField.setFromOptions()
1352  DASolver.solverAD.calcdFdFieldAD(
1353  DASolver.xvVec, DASolver.wVec, objFuncName.encode(), inputName.encode(), dFdField
1354  )
1355  fieldBar = DASolver.vec2Array(dFdField)
1356  d_inputs[inputName] += fieldBar * fBar
1357 
1358  else:
1359  raise AnalysisError("designVarType %s not supported! " % self.dvType[inputName])
1360 
1361 
1362 class DAFoamWarper(ExplicitComponent):
1363  """
1364  OpenMDAO component that wraps the warping.
1365  """
1366 
1367  def initialize(self):
1368  self.options.declare("solver", recordable=False)
1369 
1370  def setup(self):
1371 
1372  self.DASolver = self.options["solver"]
1373  DASolver = self.DASolver
1374 
1375  self.discipline = self.DASolver.getOption("discipline")
1376 
1377  # state inputs and outputs
1378  local_volume_coord_size = DASolver.mesh.getSolverGrid().size
1379 
1380  self.add_input("x_%s" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1381  self.add_output(
1382  "%s_vol_coords" % self.discipline, distributed=True, shape=local_volume_coord_size, tags=["mphys_coupling"]
1383  )
1384 
1385  def compute(self, inputs, outputs):
1386  # given the new surface mesh coordinates, compute the new volume mesh coordinates
1387  # the mesh warping will be called in getSolverGrid()
1388  DASolver = self.DASolver
1389 
1390  x_a = inputs["x_%s" % self.discipline].reshape((-1, 3))
1391  DASolver.setSurfaceCoordinates(x_a, DASolver.designSurfacesGroup)
1392  DASolver.mesh.warpMesh()
1393  solverGrid = DASolver.mesh.getSolverGrid()
1394  # actually change the mesh in the C++ layer by setting xvVec
1395  DASolver.xvFlatten2XvVec(solverGrid, DASolver.xvVec)
1396  outputs["%s_vol_coords" % self.discipline] = solverGrid
1397 
1398  # compute the mesh warping products in IDWarp
1399  def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
1400 
1401  # we do not support forward mode AD
1402  if mode == "fwd":
1403  om.issue_warning(
1404  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1405  prefix="",
1406  stacklevel=2,
1407  category=om.OpenMDAOWarning,
1408  )
1409  return
1410 
1411  # compute dXv/dXs such that we can propagate the partials (e.g., dF/dXv) to Xs
1412  # then the partial will be further propagated to XFFD in pyGeo
1413  if "%s_vol_coords" % self.discipline in d_outputs:
1414  if "x_%s" % self.discipline in d_inputs:
1415  dxV = d_outputs["%s_vol_coords" % self.discipline]
1416  self.DASolver.mesh.warpDeriv(dxV)
1417  dxS = self.DASolver.mesh.getdXs()
1418  dxS = self.DASolver.mapVector(dxS, self.DASolver.allWallsGroup, self.DASolver.designSurfacesGroup)
1419  d_inputs["x_%s" % self.discipline] += dxS.flatten()
1420 
1421 
1422 class DAFoamThermal(ExplicitComponent):
1423  """
1424  OpenMDAO component that wraps conjugate heat transfer integration
1425 
1426  """
1427 
1428  def initialize(self):
1429  self.options.declare("solver", recordable=False)
1430 
1431  def setup(self):
1432 
1433  self.DASolver = self.options["solver"]
1434 
1435  self.discipline = self.DASolver.getOption("discipline")
1436 
1437  self.nCouplingFaces = self.DASolver.solver.getNCouplingFaces()
1438 
1439  self.add_input("%s_vol_coords" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1440  self.add_input("%s_states" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1441 
1442  if self.discipline == "thermal":
1443  self.add_output("T_conduct", distributed=True, shape=self.nCouplingFaces * 2, tags=["mphys_coupling"])
1444  elif self.discipline == "aero":
1445  self.add_output("q_convect", distributed=True, shape=self.nCouplingFaces * 2, tags=["mphys_coupling"])
1446  else:
1447  raise AnalysisError("%s not supported! Options are: aero or thermal" % self.discipline)
1448 
1449  def compute(self, inputs, outputs):
1450 
1451  self.DASolver.setStates(inputs["%s_states" % self.discipline])
1452 
1453  vol_coords = inputs["%s_vol_coords" % self.discipline]
1454  states = inputs["%s_states" % self.discipline]
1455 
1456  thermal = np.zeros(self.nCouplingFaces * 2)
1457 
1458  if self.discipline == "thermal":
1459 
1460  self.DASolver.solver.getThermal(vol_coords, states, thermal)
1461 
1462  outputs["T_conduct"] = thermal
1463 
1464  elif self.discipline == "aero":
1465 
1466  self.DASolver.solver.getThermal(vol_coords, states, thermal)
1467 
1468  outputs["q_convect"] = thermal
1469 
1470  else:
1471  raise AnalysisError("%s not supported! Options are: aero or thermal" % self.discipline)
1472 
1473  def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
1474 
1475  if mode == "fwd":
1476  om.issue_warning(
1477  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1478  prefix="",
1479  stacklevel=2,
1480  category=om.OpenMDAOWarning,
1481  )
1482  return
1483 
1484  DASolver = self.DASolver
1485 
1486  vol_coords = inputs["%s_vol_coords" % self.discipline]
1487  states = inputs["%s_states" % self.discipline]
1488 
1489  if "T_conduct" in d_outputs:
1490  seeds = d_outputs["T_conduct"]
1491 
1492  if "%s_states" % self.discipline in d_inputs:
1493  product = np.zeros_like(d_inputs["%s_states" % self.discipline])
1494  DASolver.solverAD.getThermalAD("states", vol_coords, states, seeds, product)
1495  d_inputs["%s_states" % self.discipline] += product
1496 
1497  if "%s_vol_coords" % self.discipline in d_inputs:
1498  product = np.zeros_like(d_inputs["%s_vol_coords" % self.discipline])
1499  DASolver.solverAD.getThermalAD("volCoords", vol_coords, states, seeds, product)
1500  d_inputs["%s_vol_coords" % self.discipline] += product
1501 
1502  if "q_convect" in d_outputs:
1503  seeds = d_outputs["q_convect"]
1504 
1505  if "%s_states" % self.discipline in d_inputs:
1506  product = np.zeros_like(d_inputs["%s_states" % self.discipline])
1507  DASolver.solverAD.getThermalAD("states", vol_coords, states, seeds, product)
1508  d_inputs["%s_states" % self.discipline] += product
1509 
1510  if "%s_vol_coords" % self.discipline in d_inputs:
1511  product = np.zeros_like(d_inputs["%s_vol_coords" % self.discipline])
1512  DASolver.solverAD.getThermalAD("volCoords", vol_coords, states, seeds, product)
1513  d_inputs["%s_vol_coords" % self.discipline] += product
1514 
1515 
1516 class DAFoamFaceCoords(ExplicitComponent):
1517  """
1518  Calculate coupling surface coordinates based on volume coordinates
1519 
1520  """
1521 
1522  def initialize(self):
1523  self.options.declare("solver", recordable=False)
1524  self.options.declare("groupName", recordable=False)
1525 
1526  def setup(self):
1527 
1528  self.DASolver = self.options["solver"]
1529  self.discipline = self.DASolver.getOption("discipline")
1530  groupName = self.options["groupName"]
1531 
1532  self.add_input("%s_vol_coords" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1533 
1534  nPts, self.nFaces = self.DASolver._getSurfaceSize(groupName)
1535  # NOTE: here we create two duplicated surface center coordinates, so the size is nFaces * 6
1536  # one is for transferring near wall temperature, the other is for transferring k/d coefficients
1537  self.add_output(
1538  "x_%s_surface0" % self.discipline, distributed=True, shape=self.nFaces * 6, tags=["mphys_coupling"]
1539  )
1540 
1541  def compute(self, inputs, outputs):
1542 
1543  volCoords = inputs["%s_vol_coords" % self.discipline]
1544 
1545  nCouplingFaces = self.DASolver.solver.getNCouplingFaces()
1546  surfCoords = np.zeros(nCouplingFaces * 6)
1547  self.DASolver.solver.calcCouplingFaceCoords(volCoords, surfCoords)
1548 
1549  outputs["x_%s_surface0" % self.discipline] = surfCoords
1550 
1551  def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
1552 
1553  if mode == "fwd":
1554  om.issue_warning(
1555  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1556  prefix="",
1557  stacklevel=2,
1558  category=om.OpenMDAOWarning,
1559  )
1560  return
1561 
1562  DASolver = self.DASolver
1563 
1564  if "x_%s_surface0" % self.discipline in d_outputs:
1565  seeds = d_outputs["x_%s_surface0" % self.discipline]
1566 
1567  if "%s_vol_coords" % self.discipline in d_inputs:
1568  volCoords = inputs["%s_vol_coords" % self.discipline]
1569  product = np.zeros_like(volCoords)
1570  DASolver.solverAD.calcCouplingFaceCoordsAD(volCoords, seeds, product)
1571  d_inputs["%s_vol_coords" % self.discipline] += product
1572 
1573 
1574 class DAFoamForces(ExplicitComponent):
1575  """
1576  OpenMDAO component that wraps force integration
1577 
1578  """
1579 
1580  def initialize(self):
1581  self.options.declare("solver", recordable=False)
1582 
1583  def setup(self):
1584 
1585  self.DASolver = self.options["solver"]
1586 
1587  self.discipline = self.DASolver.getOption("discipline")
1588 
1589  self.add_input("%s_vol_coords" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1590  self.add_input("%s_states" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1591 
1592  local_surface_coord_size = self.DASolver.getSurfaceCoordinates(self.DASolver.couplingSurfacesGroup).size
1593  self.add_output("f_aero", distributed=True, shape=local_surface_coord_size, tags=["mphys_coupling"])
1594 
1595  def compute(self, inputs, outputs):
1596 
1597  self.DASolver.setStates(inputs["%s_states" % self.discipline])
1598 
1599  outputs["f_aero"] = self.DASolver.getForces().flatten(order="C")
1600 
1601  def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
1602 
1603  DASolver = self.DASolver
1604 
1605  if mode == "fwd":
1606  om.issue_warning(
1607  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1608  prefix="",
1609  stacklevel=2,
1610  category=om.OpenMDAOWarning,
1611  )
1612  return
1613 
1614  if "f_aero" in d_outputs:
1615  fBar = d_outputs["f_aero"]
1616  fBarVec = DASolver.array2Vec(fBar)
1617 
1618  if "%s_vol_coords" % self.discipline in d_inputs:
1619  dForcedXv = DASolver.xvVec.duplicate()
1620  dForcedXv.zeroEntries()
1621  DASolver.solverAD.calcdForcedXvAD(DASolver.xvVec, DASolver.wVec, fBarVec, dForcedXv)
1622  xVBar = DASolver.vec2Array(dForcedXv)
1623  d_inputs["%s_vol_coords" % self.discipline] += xVBar
1624  if "%s_states" % self.discipline in d_inputs:
1625  dForcedW = DASolver.wVec.duplicate()
1626  dForcedW.zeroEntries()
1627  DASolver.solverAD.calcdForcedWAD(DASolver.xvVec, DASolver.wVec, fBarVec, dForcedW)
1628  wBar = DASolver.vec2Array(dForcedW)
1629  d_inputs["%s_states" % self.discipline] += wBar
1630 
1631 
1632 class DAFoamAcoustics(ExplicitComponent):
1633  """
1634  OpenMDAO component that wraps acoustic coupling
1635 
1636  """
1637 
1638  def initialize(self):
1639  self.options.declare("solver", recordable=False)
1640  self.options.declare("groupName", recordable=False)
1641 
1642  def setup(self):
1643 
1644  self.DASolver = self.options["solver"]
1645  self.groupName = self.options["groupName"]
1646 
1647  self.discipline = self.DASolver.getOption("discipline")
1648 
1649  self.add_input("%s_vol_coords" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1650  self.add_input("%s_states" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1651 
1652  _, nCls = self.DASolver._getSurfaceSize(self.groupName)
1653  self.add_output("xAcou", distributed=True, shape=nCls * 3)
1654  self.add_output("nAcou", distributed=True, shape=nCls * 3)
1655  self.add_output("aAcou", distributed=True, shape=nCls)
1656  self.add_output("fAcou", distributed=True, shape=nCls * 3)
1657 
1658  def compute(self, inputs, outputs):
1659 
1660  self.DASolver.setStates(inputs["%s_states" % self.discipline])
1661 
1662  positions, normals, areas, forces = self.DASolver.getAcousticData(self.groupName)
1663 
1664  outputs["xAcou"] = positions.flatten(order="C")
1665  outputs["nAcou"] = normals.flatten(order="C")
1666  outputs["aAcou"] = areas.flatten(order="C")
1667  outputs["fAcou"] = forces.flatten(order="C")
1668 
1669  def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
1670 
1671  DASolver = self.DASolver
1672 
1673  if mode == "fwd":
1674  om.issue_warning(
1675  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1676  prefix="",
1677  stacklevel=2,
1678  category=om.OpenMDAOWarning,
1679  )
1680  return
1681 
1682  for varName in ["xAcou", "nAcou", "aAcou", "fAcou"]:
1683  if varName in d_outputs:
1684  fBar = d_outputs[varName]
1685  fBarVec = DASolver.array2Vec(fBar)
1686  if "%s_vol_coords" % self.discipline in d_inputs:
1687  dAcoudXv = DASolver.xvVec.duplicate()
1688  dAcoudXv.zeroEntries()
1689  DASolver.solverAD.calcdAcousticsdXvAD(
1690  DASolver.xvVec, DASolver.wVec, fBarVec, dAcoudXv, varName.encode(), self.groupName.encode()
1691  )
1692  xVBar = DASolver.vec2Array(dAcoudXv)
1693  d_inputs["%s_vol_coords" % self.discipline] += xVBar
1694  if "%s_states" % self.discipline in d_inputs:
1695  dAcoudW = DASolver.wVec.duplicate()
1696  dAcoudW.zeroEntries()
1697  DASolver.solverAD.calcdAcousticsdWAD(
1698  DASolver.xvVec, DASolver.wVec, fBarVec, dAcoudW, varName.encode(), self.groupName.encode()
1699  )
1700  wBar = DASolver.vec2Array(dAcoudW)
1701  d_inputs["%s_states" % self.discipline] += wBar
1702 
1703 
1704 class DAFoamPropForce(ExplicitComponent):
1705  """
1706  DAFoam component that computes the propeller force and radius profile based on the CFD surface states
1707  """
1708 
1709  """
1710  def initialize(self):
1711  self.options.declare("solver", recordable=False)
1712 
1713  def setup(self):
1714 
1715  self.DASolver = self.options["solver"]
1716 
1717  self.discipline = self.DASolver.getOption("discipline")
1718 
1719  self.add_input("%s_states" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1720  self.add_input("%s_vol_coords" % self.discipline, distributed=True, shape_by_conn=True, tags=["mphys_coupling"])
1721 
1722  self.nForceSections = self.DASolver.getOption("wingProp")["nForceSections"]
1723  self.add_output("force_profile", distributed=False, shape=3 * self.nForceSections, tags=["mphys_coupling"])
1724  self.add_output("radial_location", distributed=False, shape=self.nForceSections, tags=["mphys_coupling"])
1725 
1726  def compute(self, inputs, outputs):
1727 
1728  DASolver = self.DASolver
1729 
1730  dafoam_states = inputs["%s_states" % self.discipline]
1731  dafoam_xv = inputs["%s_vol_coords" % self.discipline]
1732 
1733  stateVec = DASolver.array2Vec(dafoam_states)
1734  xvVec = DASolver.array2Vec(dafoam_xv)
1735 
1736  fProfileVec = PETSc.Vec().createSeq(3 * self.nForceSections, bsize=1, comm=PETSc.COMM_SELF)
1737  fProfileVec.zeroEntries()
1738 
1739  sRadiusVec = PETSc.Vec().createSeq(self.nForceSections, bsize=1, comm=PETSc.COMM_SELF)
1740  sRadiusVec.zeroEntries()
1741 
1742  DASolver.solver.calcForceProfile(xvVec, stateVec, fProfileVec, sRadiusVec)
1743 
1744  outputs["force_profile"] = DASolver.vec2ArraySeq(fProfileVec)
1745  outputs["radial_location"] = DASolver.vec2ArraySeq(sRadiusVec)
1746 
1747  def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
1748  DASolver = self.DASolver
1749 
1750  dafoam_states = inputs["%s_states" % self.discipline]
1751  dafoam_xv = inputs["%s_vol_coords" % self.discipline]
1752 
1753  stateVec = DASolver.array2Vec(dafoam_states)
1754  xvVec = DASolver.array2Vec(dafoam_xv)
1755 
1756  if mode == "fwd":
1757  raise AnalysisError("fwd not implemented!")
1758 
1759  if "force_profile" in d_outputs:
1760  fBar = d_outputs["force_profile"]
1761  fBarVec = DASolver.array2VecSeq(fBar)
1762 
1763  if "%s_states" % self.discipline in d_inputs:
1764  prodVec = self.DASolver.wVec.duplicate()
1765  prodVec.zeroEntries()
1766  DASolver.solverAD.calcdForcedStateTPsiAD("dFdW".encode(), xvVec, stateVec, fBarVec, prodVec)
1767  pBar = DASolver.vec2Array(prodVec)
1768  d_inputs["%s_states" % self.discipline] += pBar
1769 
1770  # xv has no contribution to the force profile
1771  if "%s_vol_coords" % self.discipline in d_inputs:
1772  prodVec = self.DASolver.xvVec.duplicate()
1773  prodVec.zeroEntries()
1774  pBar = DASolver.vec2Array(prodVec)
1775  d_inputs["%s_vol_coords" % self.discipline] += pBar
1776 
1777  if "radial_location" in d_outputs:
1778  rBar = d_outputs["radial_location"]
1779  rBarVec = DASolver.array2VecSeq(rBar)
1780 
1781  # states have no effect on the radius
1782  if "%s_states" % self.discipline in d_inputs:
1783  prodVec = self.DASolver.wVec.duplicate()
1784  prodVec.zeroEntries()
1785  pBar = DASolver.vec2Array(prodVec)
1786  d_inputs["%s_states" % self.discipline] += pBar
1787 
1788  if "%s_vol_coords" % self.discipline in d_inputs:
1789  prodVec = self.DASolver.xvVec.duplicate()
1790  DASolver.solverAD.calcdForcedStateTPsiAD("dRdX".encode(), xvVec, stateVec, rBarVec, prodVec)
1791  prodVec.zeroEntries()
1792  pBar = DASolver.vec2Array(prodVec)
1793  d_inputs["%s_vol_coords" % self.discipline] += pBar
1794  """
1795 
1796 
1797 class DAFoamFvSource(ExplicitComponent):
1798  """
1799  DAFoam component that computes the actuator source term based on force and radius profiles and prop center
1800  """
1801 
1802  def initialize(self):
1803  self.options.declare("solver", recordable=False)
1804 
1805  def setup(self):
1806 
1807  self.DASolver = self.options["solver"]
1808 
1809  # loop over all the propNames and check if any of them is active. If yes, add inputs for this prop
1810  for propName in list(self.DASolver.getOption("wingProp").keys()):
1811  if self.DASolver.getOption("wingProp")[propName]["active"]:
1812  self.nForceSections = self.DASolver.getOption("wingProp")[propName]["nForceSections"]
1813  self.add_input(
1814  propName + "_axial_force", distributed=False, shape=self.nForceSections, tags=["mphys_coupling"]
1815  )
1816  self.add_input(
1817  propName + "_tangential_force",
1818  distributed=False,
1819  shape=self.nForceSections,
1820  tags=["mphys_coupling"],
1821  )
1822  self.add_input(
1823  propName + "_radial_location",
1824  distributed=False,
1825  shape=self.nForceSections,
1826  tags=["mphys_coupling"],
1827  )
1828  self.add_input(propName + "_integral_force", distributed=False, shape=2, tags=["mphys_coupling"])
1829  self.add_input(propName + "_prop_center", distributed=False, shape=3, tags=["mphys_coupling"])
1830 
1831  # we have only one output
1832  self.nLocalCells = self.DASolver.solver.getNLocalCells()
1833  self.add_output("fvSource", distributed=True, shape=self.nLocalCells * 3, tags=["mphys_coupling"])
1834 
1835  def compute(self, inputs, outputs):
1836 
1837  DASolver = self.DASolver
1838 
1839  # initialize output to zeros
1840  fvSourceVec = PETSc.Vec().create(self.comm)
1841  fvSourceVec.setSizes((self.nLocalCells * 3, PETSc.DECIDE), bsize=1)
1842  fvSourceVec.setFromOptions()
1843  fvSourceVec.zeroEntries()
1844 
1845  outputs["fvSource"] = DASolver.vec2Array(fvSourceVec)
1846 
1847  # we call calcFvSource multiple times and add contributions from all the propellers
1848  for propName in list(self.DASolver.getOption("wingProp").keys()):
1849  if self.DASolver.getOption("wingProp")[propName]["active"]:
1850 
1851  axial_force = inputs[propName + "_axial_force"]
1852  tangential_force = inputs[propName + "_tangential_force"]
1853  radial_location = inputs[propName + "_radial_location"]
1854  integral_force = inputs[propName + "_integral_force"]
1855  prop_center = inputs[propName + "_prop_center"]
1856 
1857  axial_force_vec = DASolver.array2VecSeq(axial_force)
1858  tangential_force_vec = DASolver.array2VecSeq(tangential_force)
1859  radial_location_vec = DASolver.array2VecSeq(radial_location)
1860  integral_force_vec = DASolver.array2VecSeq(integral_force)
1861  prop_center_vec = DASolver.array2VecSeq(prop_center)
1862 
1863  fvSourceVec.zeroEntries()
1864 
1865  DASolver.solver.calcFvSource(
1866  propName.encode(),
1867  axial_force_vec,
1868  tangential_force_vec,
1869  radial_location_vec,
1870  integral_force_vec,
1871  prop_center_vec,
1872  fvSourceVec,
1873  )
1874 
1875  outputs["fvSource"] += DASolver.vec2Array(fvSourceVec)
1876 
1877  def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
1878 
1879  DASolver = self.DASolver
1880 
1881  if mode == "fwd":
1882  om.issue_warning(
1883  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1884  prefix="",
1885  stacklevel=2,
1886  category=om.OpenMDAOWarning,
1887  )
1888  return
1889 
1890  if "fvSource" in d_outputs:
1891  sBar = d_outputs["fvSource"]
1892  sBarVec = DASolver.array2Vec(sBar)
1893 
1894  for propName in list(self.DASolver.getOption("wingProp").keys()):
1895  if self.DASolver.getOption("wingProp")[propName]["active"]:
1896 
1897  a = inputs[propName + "_axial_force"]
1898  t = inputs[propName + "_tangential_force"]
1899  r = inputs[propName + "_radial_location"]
1900  f = inputs[propName + "_integral_force"]
1901  c = inputs[propName + "_prop_center"]
1902 
1903  aVec = DASolver.array2VecSeq(a)
1904  tVec = DASolver.array2VecSeq(t)
1905  rVec = DASolver.array2VecSeq(r)
1906  fVec = DASolver.array2VecSeq(f)
1907  cVec = DASolver.array2VecSeq(c)
1908 
1909  if propName + "_axial_force" in d_inputs:
1910  prodVec = PETSc.Vec().createSeq(self.nForceSections, bsize=1, comm=PETSc.COMM_SELF)
1911  prodVec.zeroEntries()
1912  DASolver.solverAD.calcdFvSourcedInputsTPsiAD(
1913  propName.encode(), "aForce".encode(), aVec, tVec, rVec, fVec, cVec, sBarVec, prodVec
1914  )
1915  aBar = DASolver.vec2ArraySeq(prodVec)
1916  aBar = self.comm.allreduce(aBar, op=MPI.SUM)
1917  d_inputs[propName + "_axial_force"] += aBar
1918 
1919  if propName + "_tangential_force" in d_inputs:
1920  prodVec = PETSc.Vec().createSeq(self.nForceSections, bsize=1, comm=PETSc.COMM_SELF)
1921  prodVec.zeroEntries()
1922  DASolver.solverAD.calcdFvSourcedInputsTPsiAD(
1923  propName.encode(), "tForce".encode(), aVec, tVec, rVec, fVec, cVec, sBarVec, prodVec
1924  )
1925  tBar = DASolver.vec2ArraySeq(prodVec)
1926  tBar = self.comm.allreduce(tBar, op=MPI.SUM)
1927  d_inputs[propName + "_tangential_force"] += tBar
1928 
1929  if propName + "_radial_location" in d_inputs:
1930  prodVec = PETSc.Vec().createSeq(self.nForceSections, bsize=1, comm=PETSc.COMM_SELF)
1931  prodVec.zeroEntries()
1932  DASolver.solverAD.calcdFvSourcedInputsTPsiAD(
1933  propName.encode(), "rDist".encode(), aVec, tVec, rVec, fVec, cVec, sBarVec, prodVec
1934  )
1935  rBar = DASolver.vec2ArraySeq(prodVec)
1936  rBar = self.comm.allreduce(rBar, op=MPI.SUM)
1937  d_inputs[propName + "_radial_location"] += rBar
1938 
1939  if propName + "_integral_force" in d_inputs:
1940  prodVec = PETSc.Vec().createSeq(2, bsize=1, comm=PETSc.COMM_SELF)
1941  prodVec.zeroEntries()
1942  DASolver.solverAD.calcdFvSourcedInputsTPsiAD(
1943  propName.encode(), "targetForce".encode(), aVec, tVec, rVec, fVec, cVec, sBarVec, prodVec
1944  )
1945  fBar = DASolver.vec2ArraySeq(prodVec)
1946  fBar = self.comm.allreduce(fBar, op=MPI.SUM)
1947  d_inputs[propName + "_integral_force"] += fBar
1948 
1949  if propName + "_prop_center" in d_inputs:
1950  prodVec = PETSc.Vec().createSeq(3, bsize=1, comm=PETSc.COMM_SELF)
1951  prodVec.zeroEntries()
1952  DASolver.solverAD.calcdFvSourcedInputsTPsiAD(
1953  propName.encode(), "center".encode(), aVec, tVec, rVec, fVec, cVec, sBarVec, prodVec
1954  )
1955  cBar = DASolver.vec2ArraySeq(prodVec)
1956  cBar = self.comm.allreduce(cBar, op=MPI.SUM)
1957  d_inputs[propName + "_prop_center"] += cBar
1958 
1959 
1960 class DAFoamPropNodes(ExplicitComponent):
1961  """
1962  Component that computes propeller aero-node locations that link with structural nodes in aerostructural cases.
1963  """
1964 
1965  def initialize(self):
1966  self.options.declare("solver", default=None, recordable=False)
1967 
1968  def setup(self):
1969  self.DASolver = self.options["solver"]
1970 
1971  self.aerostructDict = self.DASolver.getOption("couplingInfo")["aerostructural"]
1972  self.fvSourceDict = self.DASolver.getOption("fvSource")
1973 
1974  if "fvSource" in self.aerostructDict.keys():
1975  # Iterate through Actuator Disks
1976  for fvSource, parameters in self.aerostructDict["fvSource"].items():
1977  # Check if Actuator Disk Exists
1978  if fvSource not in self.fvSourceDict:
1979  raise RuntimeWarning("Actuator disk %s not found when adding masked nodes" % fvSource)
1980 
1981  # Add Input
1982  self.add_input("x_prop0_%s" % fvSource, shape=3, distributed=False, tags=["mphys_coordinates"])
1983 
1984  # Add Output
1985  if self.comm.rank == 0:
1986  self.add_output(
1987  "x_prop0_nodes_%s" % fvSource,
1988  shape=(1 + parameters["nNodes"]) * 3,
1989  distributed=True,
1990  tags=["mphys_coordinates"],
1991  )
1992  self.add_output(
1993  "f_prop_%s" % fvSource,
1994  shape=(1 + parameters["nNodes"]) * 3,
1995  distributed=True,
1996  tags=["mphys_coordinates"],
1997  )
1998  else:
1999  self.add_output(
2000  "x_prop0_nodes_%s" % fvSource, shape=(0), distributed=True, tags=["mphys_coordinates"]
2001  )
2002  self.add_output("f_prop_%s" % fvSource, shape=(0), distributed=True, tags=["mphys_coordinates"])
2003 
2004  def compute(self, inputs, outputs):
2005  # Loop over all actuator disks to generate ring of nodes for each
2006  for fvSource, parameters in self.aerostructDict["fvSource"].items():
2007  # Nodes should only be on root proc
2008  if self.comm.rank == 0:
2009  center = inputs["x_prop0_%s" % fvSource]
2010 
2011  # Compute local coordinate frame for ring of nodes
2012  direction = self.fvSourceDict[fvSource]["direction"]
2013  direction = direction / np.linalg.norm(direction, 2)
2014  temp_vec = np.array([1.0, 0.0, 0.0])
2015  y_local = np.cross(direction, temp_vec)
2016  if np.linalg.norm(y_local, 2) < 1e-5:
2017  temp_vec = np.array([0.0, 1.0, 0.0])
2018  y_local = np.cross(direction, temp_vec)
2019  y_local = y_local / np.linalg.norm(y_local, 2)
2020  z_local = np.cross(direction, y_local)
2021  z_local = z_local / np.linalg.norm(z_local, 2)
2022 
2023  n_theta = parameters["nNodes"]
2024  radial_loc = parameters["radialLoc"]
2025 
2026  # Set ring of nodes location and force values
2027  nodes_x = np.zeros((n_theta + 1, 3))
2028  nodes_x[0, :] = center
2029  nodes_f = np.zeros((n_theta + 1, 3))
2030  if n_theta == 0:
2031  nodes_f[0, :] = -self.fvSourceDict[fvSource]["targetThrust"] * direction
2032  else:
2033  nodes_f[0, :] = 0.0
2034  for i in range(n_theta):
2035  theta = i / n_theta * 2 * np.pi
2036  nodes_x[i + 1, :] = (
2037  center + radial_loc * y_local * np.cos(theta) + radial_loc * z_local * np.sin(theta)
2038  )
2039  nodes_f[i + 1, :] = -self.fvSourceDict[fvSource]["targetThrust"] * direction / n_theta
2040 
2041  outputs["x_prop0_nodes_%s" % fvSource] = nodes_x.flatten()
2042  outputs["f_prop_%s" % fvSource] = nodes_f.flatten()
2043 
2044  def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
2045  if mode == "fwd":
2046  om.issue_warning(
2047  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
2048  prefix="",
2049  stacklevel=2,
2050  category=om.OpenMDAOWarning,
2051  )
2052  return
2053 
2054  for fvSource, parameters in self.aerostructDict["fvSource"].items():
2055  if "x_prop0_%s" % fvSource in d_inputs:
2056  if "x_prop0_nodes_%s" % fvSource in d_outputs:
2057  temp = np.zeros((parameters["nNodes"] + 1) * 3)
2058  # Take ring of node seeds, broadcast them, and add them to all procs
2059  if self.comm.rank == 0:
2060  temp[:] = d_outputs["x_prop0_nodes_%s" % fvSource]
2061  self.comm.Bcast(temp, root=0)
2062  for i in range(parameters["nNodes"]):
2063  d_inputs["x_prop0_%s" % fvSource] += temp[3 * i : 3 * i + 3]
2064 
2065 
2066 class DAFoamActuator(ExplicitComponent):
2067  """
2068  Component that updates actuator disk definition variables when actuator disks are displaced in an aerostructural case.
2069  """
2070 
2071  def initialize(self):
2072  self.options.declare("solver", recordable=False)
2073 
2074  def setup(self):
2075  self.DASolver = self.options["solver"]
2076 
2077  self.aerostructDict = self.DASolver.getOption("couplingInfo")["aerostructural"]
2078  self.fvSourceDict = self.DASolver.getOption("fvSource")
2079 
2080  for fvSource, _ in self.aerostructDict["fvSource"].items():
2081  self.add_input("dv_actuator_%s" % fvSource, shape=(7), distributed=False, tags=["mphys_coupling"])
2082  self.add_input("x_prop_%s" % fvSource, shape_by_conn=True, distributed=True, tags=["mphys_coupling"])
2083 
2084  self.add_output("actuator_%s" % fvSource, shape_by_conn=(10), distributed=False, tags=["mphys_coupling"])
2085 
2086  def compute(self, inputs, outputs):
2087  # Loop over all actuator disks
2088  for fvSource, _ in self.aerostructDict["fvSource"].items():
2089  actuator = np.zeros(10)
2090  # Update variables on root proc
2091  if self.comm.rank == 0:
2092  actuator[3:] = inputs["dv_actuator_%s" % fvSource][:]
2093  actuator[:3] = inputs["x_prop_%s" % fvSource][:3]
2094 
2095  # Broadcast variables to all procs and set as output
2096  self.comm.Bcast(actuator, root=0)
2097  outputs["actuator_%s" % fvSource] = actuator
2098 
2099  def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
2100  if mode == "fwd":
2101  om.issue_warning(
2102  " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
2103  prefix="",
2104  stacklevel=2,
2105  category=om.OpenMDAOWarning,
2106  )
2107  return
2108 
2109  # Loop over all actuator disks
2110  for fvSource, _ in self.aerostructDict["fvSource"].items():
2111  if "actuator_%s" % fvSource in d_outputs:
2112  if "dv_actuator_%s" % fvSource in d_inputs:
2113  # Add non-location seeds to all procs
2114  d_inputs["dv_actuator_%s" % fvSource][:] += d_outputs["actuator_%s" % fvSource][3:]
2115  if "x_prop_%s" % fvSource in d_inputs:
2116  # Add location seeds to only root proc
2117  if self.comm.rank == 0:
2118  d_inputs["x_prop_%s" % fvSource][:3] += d_outputs["actuator_%s" % fvSource][:3]
2119 
2120 
2121 class OptFuncs(object):
2122  """
2123  Some utility functions
2124  """
2125 
2126  def __init__(self, daOptions, om_prob):
2127  """
2128  daOptions: dict or list
2129  The daOptions dict from runScript.py. Support more than two dicts
2130 
2131  om_prob:
2132  The om.Problem() object
2133  """
2134 
2135  self.daOptions = daOptions
2136  self.om_prob = om_prob
2137  self.comm = MPI.COMM_WORLD
2138 
2139  # we need to check if the design variable set in the OM model is also set
2140  # in the designVar key in DAOptions. If they are not consistent, we print
2141  # an error and exit because it will produce wrong gradient
2142 
2143  modelDesignVars = self.om_prob.model.get_design_vars()
2144 
2145  isList = isinstance(self.daOptions, list)
2146  if isList:
2147  DADesignVars = []
2148  for subDict in self.daOptions:
2149  for key in list(subDict["designVar"].keys()):
2150  DADesignVars.append(key)
2151  else:
2152  DADesignVars = list(self.daOptions["designVar"].keys())
2153  for modelDV in modelDesignVars:
2154  dvFound = False
2155  for dv in DADesignVars:
2156  # NOTE. modelDV has format like dvs.shape, so we have to use "in" to check
2157  if dv in modelDV:
2158  dvFound = True
2159  if dvFound is not True:
2160  raise RuntimeError(
2161  "Design variable %s is defined in the model but not found in designVar in DAOptions! " % modelDV
2162  )
2163 
2165  self,
2166  constraints,
2167  designVars,
2168  targets,
2169  constraintsComp=None,
2170  designVarsComp=None,
2171  epsFD=None,
2172  maxIter=10,
2173  tol=1e-4,
2174  maxNewtonStep=None,
2175  ):
2176  """
2177  Find the design variables that meet the prescribed constraints. This can be used to get a
2178  feasible design to start the optimization. For example, finding the angle of attack and
2179  tail rotation angle that give the target lift and pitching moment. The sizes of cons and
2180  designvars have to be the same.
2181  NOTE: we use the Newton method to find the feasible design.
2182  """
2183 
2184  if self.comm.rank == 0:
2185  print("Finding a feasible design using the Newton method. ")
2186  print("Constraints: ", constraints)
2187  print("Design Vars: ", designVars)
2188  print("Target: ", targets)
2189 
2190  if len(constraints) != len(designVars):
2191  raise RuntimeError("Sizes of the constraints and designVars lists need to be the same! ")
2192 
2193  size = len(constraints)
2194 
2195  # if the component is empty, set it to 0
2196  if constraintsComp is None:
2197  constraintsComp = size * [0]
2198  if designVarsComp is None:
2199  designVarsComp = size * [0]
2200  # if the FD step size is None, set it to 1e-3
2201  if epsFD is None:
2202  epsFD = size * [1e-3]
2203  # if the max Newton step is None, set it to a very large value
2204  if maxNewtonStep is None:
2205  maxNewtonStep = size * [1e16]
2206 
2207  # main Newton loop
2208  for n in range(maxIter):
2209 
2210  # Newton Jacobian
2211  jacMat = np.zeros((size, size))
2212 
2213  # run the primal for the reference dvs
2214  self.om_prob.run_model()
2215 
2216  # get the reference design vars and constraints values
2217  dv0 = np.zeros(size)
2218  for i in range(size):
2219  dvName = designVars[i]
2220  comp = designVarsComp[i]
2221  val = self.om_prob.get_val(dvName)
2222  dv0[i] = val[comp]
2223  con0 = np.zeros(size)
2224  for i in range(size):
2225  conName = constraints[i]
2226  comp = constraintsComp[i]
2227  val = self.om_prob.get_val(conName)
2228  con0[i] = val[comp]
2229 
2230  # calculate the residual. Constraints - Targets
2231  res = con0 - targets
2232 
2233  # compute the residual norm
2234  norm = np.linalg.norm(res / targets)
2235 
2236  if self.comm.rank == 0:
2237  print("FindFeasibleDesign Iter: ", n)
2238  print("DesignVars: ", dv0)
2239  print("Constraints: ", con0)
2240  print("Residual Norm: ", norm)
2241 
2242  # break the loop if residual is already smaller than the tolerance
2243  if norm < tol:
2244  if self.comm.rank == 0:
2245  print("FindFeasibleDesign Converged! ")
2246  break
2247 
2248  # perturb design variables and compute the Jacobian matrix
2249  for i in range(size):
2250  dvName = designVars[i]
2251  comp = designVarsComp[i]
2252  # perturb +step
2253  dvP = dv0[i] + epsFD[i]
2254  self.om_prob.set_val(dvName, dvP, indices=comp)
2255  # run the primal
2256  self.om_prob.run_model()
2257  # reset the perturbation
2258  self.om_prob.set_val(dvName, dv0[i], indices=comp)
2259 
2260  # get the perturb constraints and compute the Jacobian
2261  for j in range(size):
2262  conName = constraints[j]
2263  comp = constraintsComp[j]
2264  val = self.om_prob.get_val(conName)
2265  conP = val[comp]
2266 
2267  deriv = (conP - con0[j]) / epsFD[i]
2268  jacMat[j][i] = deriv
2269 
2270  # calculate the deltaDV using the Newton method
2271  deltaDV = -np.linalg.inv(jacMat).dot(res)
2272 
2273  # we can bound the delta change to ensure a more robust Newton solver.
2274  for i in range(size):
2275  if abs(deltaDV[i]) > abs(maxNewtonStep[i]):
2276  if deltaDV[i] > 0:
2277  deltaDV[i] = abs(maxNewtonStep[i])
2278  else:
2279  deltaDV[i] = -abs(maxNewtonStep[i])
2280 
2281  # update the dv
2282  dv1 = dv0 + deltaDV
2283  for i in range(size):
2284  dvName = designVars[i]
2285  comp = designVarsComp[i]
2286  self.om_prob.set_val(dvName, dv1[i], indices=comp)
dafoam.mphys.mphys_dafoam.DAFoamFunctions.dvType
dvType
Definition: mphys_dafoam.py:1127
dafoam.mphys.mphys_dafoam.DAFoamActuator.DASolver
DASolver
Definition: mphys_dafoam.py:2075
dafoam.mphys.mphys_dafoam.DAFoamBuilder.options
options
Definition: mphys_dafoam.py:25
dafoam.mphys.mphys_dafoam.DAFoamPostcouplingGroup.add_dv_func
def add_dv_func(self, dvName, dv_func)
Definition: mphys_dafoam.py:531
dafoam.mphys.mphys_dafoam.DAFoamFaceCoords.initialize
def initialize(self)
Definition: mphys_dafoam.py:1522
dafoam.mphys.mphys_dafoam.DAFoamFaceCoords.compute
def compute(self, inputs, outputs)
Definition: mphys_dafoam.py:1541
dafoam.mphys.mphys_dafoam.DAFoamMesh.DASolver
DASolver
Definition: mphys_dafoam.py:1040
dafoam.mphys.mphys_dafoam.DAFoamFaceCoords
Definition: mphys_dafoam.py:1516
dafoam.mphys.mphys_dafoam.DAFoamFvSource.setup
def setup(self)
Definition: mphys_dafoam.py:1805
dafoam.mphys.mphys_dafoam.DAFoamFunctions.DASolver
DASolver
Definition: mphys_dafoam.py:1116
dafoam.mphys.mphys_dafoam.DAFoamBuilder.prop_coupling
prop_coupling
Definition: mphys_dafoam.py:46
dafoam.mphys.mphys_dafoam.DAFoamSolver.run_directory
run_directory
Definition: mphys_dafoam.py:556
dafoam.mphys.mphys_dafoam.DAFoamPrecouplingGroup.discipline
discipline
Definition: mphys_dafoam.py:397
dafoam.mphys.mphys_dafoam.DAFoamGroup.prop_coupling
prop_coupling
Definition: mphys_dafoam.py:159
dafoam.mphys.mphys_dafoam.DAFoamAcoustics.setup
def setup(self)
Definition: mphys_dafoam.py:1642
dafoam.mphys.mphys_dafoam.DAFoamBuilder.struct_coupling
struct_coupling
Definition: mphys_dafoam.py:38
dafoam.mphys.mphys_dafoam.DAFoamSolver.add_dv_func
def add_dv_func(self, dvName, dv_func)
Definition: mphys_dafoam.py:639
dafoam.mphys.mphys_dafoam.DAFoamThermal.compute_jacvec_product
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode)
Definition: mphys_dafoam.py:1473
dafoam.mphys.mphys_dafoam.DAFoamGroup.mphys_set_masking
def mphys_set_masking(self)
Definition: mphys_dafoam.py:263
dafoam.mphys.mphys_dafoam.DAFoamPostcouplingGroup.initialize
def initialize(self)
Definition: mphys_dafoam.py:511
dafoam.mphys.mphys_dafoam.DAFoamFvSource
Definition: mphys_dafoam.py:1797
dafoam.mphys.mphys_dafoam.DAFoamMesh.discipline
discipline
Definition: mphys_dafoam.py:1042
dafoam.mphys.mphys_dafoam.DAFoamFvSource.nLocalCells
nLocalCells
Definition: mphys_dafoam.py:1832
dafoam.mphys.mphys_dafoam.DAFoamGroup.mphys_set_options
def mphys_set_options(self, optionDict)
Definition: mphys_dafoam.py:377
dafoam.mphys.mphys_dafoam.DAFoamMesh.initialize
def initialize(self)
Definition: mphys_dafoam.py:1035
dafoam.mphys.mphys_dafoam.DAFoamForces.compute
def compute(self, inputs, outputs)
Definition: mphys_dafoam.py:1595
dafoam.mphys.mphys_dafoam.DAFoamBuilder.initialize
def initialize(self, comm)
Definition: mphys_dafoam.py:72
dafoam.pyDAFoam.PYDAFOAM
Definition: pyDAFoam.py:673
dafoam.mphys.mphys_dafoam.DAFoamPropNodes.compute
def compute(self, inputs, outputs)
Definition: mphys_dafoam.py:2004
dafoam.mphys.mphys_dafoam.DAFoamFunctions.optionDict
optionDict
Definition: mphys_dafoam.py:1123
dafoam.mphys.mphys_dafoam.OptFuncs.om_prob
om_prob
Definition: mphys_dafoam.py:2136
dafoam.mphys.mphys_dafoam.DAFoamSolver.solution_counter
solution_counter
Definition: mphys_dafoam.py:560
dafoam.mphys.mphys_dafoam.DAFoamGroup.thermal_coupling
thermal_coupling
Definition: mphys_dafoam.py:160
dafoam.mphys.mphys_dafoam.DAFoamPropNodes.initialize
def initialize(self)
Definition: mphys_dafoam.py:1965
dafoam.mphys.mphys_dafoam.DAFoamAcoustics.discipline
discipline
Definition: mphys_dafoam.py:1647
dafoam.mphys.mphys_dafoam.DAFoamSolver.dvType
dvType
Definition: mphys_dafoam.py:593
dafoam.mphys.mphys_dafoam.DAFoamActuator.setup
def setup(self)
Definition: mphys_dafoam.py:2074
dafoam.mphys.mphys_dafoam.DAFoamWarper.compute
def compute(self, inputs, outputs)
Definition: mphys_dafoam.py:1385
dafoam.mphys.mphys_dafoam.DAFoamWarper.DASolver
DASolver
Definition: mphys_dafoam.py:1372
dafoam.mphys.mphys_dafoam.DAFoamBuilder.get_coupling_group_subsystem
def get_coupling_group_subsystem(self, scenario_name=None)
Definition: mphys_dafoam.py:90
dafoam.mphys.mphys_dafoam.DAFoamMesh.mphys_get_triangulated_surface
def mphys_get_triangulated_surface(self, groupName=None)
Definition: mphys_dafoam.py:1071
dafoam.mphys.mphys_dafoam.DAFoamMeshGroup.mphys_add_coordinate_input
def mphys_add_coordinate_input(self)
Definition: mphys_dafoam.py:1021
dafoam.mphys.mphys_dafoam.DAFoamSolver.prop_coupling
prop_coupling
Definition: mphys_dafoam.py:551
dafoam.mphys.mphys_dafoam.DAFoamWarper.compute_jacvec_product
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode)
Definition: mphys_dafoam.py:1399
dafoam.mphys.mphys_dafoam.DAFoamBuilder.__init__
def __init__(self, options, mesh_options=None, scenario="aerodynamic", prop_coupling=None, run_directory="")
Definition: mphys_dafoam.py:22
dafoam.mphys.mphys_dafoam.DAFoamFaceCoords.compute_jacvec_product
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode)
Definition: mphys_dafoam.py:1551
dafoam.mphys.mphys_dafoam.DAFoamBuilder.thermal_coupling
thermal_coupling
Definition: mphys_dafoam.py:40
dafoam.mphys.mphys_dafoam.DAFoamActuator.aerostructDict
aerostructDict
Definition: mphys_dafoam.py:2077
dafoam.mphys.mphys_dafoam.DAFoamBuilder.comm
comm
Definition: mphys_dafoam.py:74
dafoam.mphys.mphys_dafoam.DAFoamBuilder.DASolver
DASolver
Definition: mphys_dafoam.py:78
dafoam.mphys.mphys_dafoam.DAFoamFvSource.DASolver
DASolver
Definition: mphys_dafoam.py:1807
dafoam.mphys.mphys_dafoam.DAFoamFaceCoords.discipline
discipline
Definition: mphys_dafoam.py:1529
dafoam.mphys.mphys_dafoam.DAFoamGroup.initialize
def initialize(self)
Definition: mphys_dafoam.py:146
dafoam.mphys.mphys_dafoam.DAFoamMesh
Definition: mphys_dafoam.py:1030
dafoam.mphys.mphys_dafoam.DAFoamMeshGroup.mphys_get_triangulated_surface
def mphys_get_triangulated_surface(self)
Definition: mphys_dafoam.py:1025
dafoam.mphys.mphys_dafoam.DAFoamForces.DASolver
DASolver
Definition: mphys_dafoam.py:1585
dafoam.mphys.mphys_dafoam.DAFoamMesh.compute
def compute(self, inputs, outputs)
Definition: mphys_dafoam.py:1080
dafoam.mphys.mphys_dafoam.DAFoamSolver.dv_funcs
dv_funcs
Definition: mphys_dafoam.py:569
dafoam.mphys.mphys_dafoam.OptFuncs.findFeasibleDesign
def findFeasibleDesign(self, constraints, designVars, targets, constraintsComp=None, designVarsComp=None, epsFD=None, maxIter=10, tol=1e-4, maxNewtonStep=None)
Definition: mphys_dafoam.py:2164
dafoam.mphys.mphys_dafoam.DAFoamSolver._updateKSPTolerances
def _updateKSPTolerances(self, psi, dFdW, ksp)
Definition: mphys_dafoam.py:978
dafoam.mphys.mphys_dafoam.DAFoamBuilder.get_number_of_nodes
def get_number_of_nodes(self, groupName=None)
Definition: mphys_dafoam.py:118
dafoam.mphys.mphys_dafoam.DAFoamBuilder.get_pre_coupling_subsystem
def get_pre_coupling_subsystem(self, scenario_name=None)
Definition: mphys_dafoam.py:106
dafoam.mphys.mphys_dafoam.DAFoamFunctions.funcs
funcs
Definition: mphys_dafoam.py:1112
dafoam.mphys.mphys_dafoam.DAFoamMesh.x_a0
x_a0
Definition: mphys_dafoam.py:1045
dafoam.mphys.mphys_dafoam.DAFoamSolver.set_options
def set_options(self, optionDict)
Definition: mphys_dafoam.py:649
dafoam.mphys.mphys_dafoam.DAFoamPostcouplingGroup.mphys_add_funcs
def mphys_add_funcs(self)
Definition: mphys_dafoam.py:528
dafoam.mphys.mphys_dafoam.DAFoamFunctions.mphys_set_options
def mphys_set_options(self, optionDict)
Definition: mphys_dafoam.py:1186
dafoam.mphys.mphys_dafoam.DAFoamMesh.mphys_add_coordinate_input
def mphys_add_coordinate_input(self)
Definition: mphys_dafoam.py:1057
dafoam.mphys.mphys_dafoam.DAFoamGroup.setup
def setup(self)
Definition: mphys_dafoam.py:154
dafoam.mphys.mphys_dafoam.DAFoamThermal.DASolver
DASolver
Definition: mphys_dafoam.py:1433
dafoam.mphys.mphys_dafoam.DAFoamGroup.discipline
discipline
Definition: mphys_dafoam.py:162
dafoam.mphys.mphys_dafoam.DAFoamActuator
Definition: mphys_dafoam.py:2066
dafoam.mphys.mphys_dafoam.DAFoamPropNodes.DASolver
DASolver
Definition: mphys_dafoam.py:1969
dafoam.mphys.mphys_dafoam.DAFoamMeshGroup.initialize
def initialize(self)
Definition: mphys_dafoam.py:1005
dafoam.mphys.mphys_dafoam.DAFoamWarper
Definition: mphys_dafoam.py:1362
dafoam.mphys.mphys_dafoam.OptFuncs.daOptions
daOptions
Definition: mphys_dafoam.py:2135
dafoam.mphys.mphys_dafoam.DAFoamMesh.mphys_get_surface_mesh
def mphys_get_surface_mesh(self)
Definition: mphys_dafoam.py:1068
dafoam.mphys.mphys_dafoam.DAFoamBuilder.get_solver
def get_solver(self)
Definition: mphys_dafoam.py:85
dafoam.mphys.mphys_dafoam.OptFuncs
Definition: mphys_dafoam.py:2121
dafoam.mphys.mphys_dafoam.DAFoamSolver.discipline
discipline
Definition: mphys_dafoam.py:558
dafoam.mphys.mphys_dafoam.DAFoamThermal.compute
def compute(self, inputs, outputs)
Definition: mphys_dafoam.py:1449
dafoam.mphys.mphys_dafoam.DAFoamSolver.initialize
def initialize(self)
Definition: mphys_dafoam.py:543
dafoam.mphys.mphys_dafoam.DAFoamFunctions.setup
def setup(self)
Definition: mphys_dafoam.py:1114
dafoam.mphys.mphys_dafoam.DAFoamPostcouplingGroup.mphys_set_options
def mphys_set_options(self, options)
Definition: mphys_dafoam.py:534
dafoam.mphys.mphys_dafoam.DAFoamPropNodes.setup
def setup(self)
Definition: mphys_dafoam.py:1968
dafoam.mphys.mphys_dafoam.DAFoamMeshGroup
Definition: mphys_dafoam.py:1004
dafoam.mphys.mphys_dafoam.DAFoamThermal
Definition: mphys_dafoam.py:1422
dafoam.mphys.mphys_dafoam.DAFoamGroup
Definition: mphys_dafoam.py:141
dafoam.mphys.mphys_dafoam.DAFoamMesh.compute_jacvec_product
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode)
Definition: mphys_dafoam.py:1087
dafoam.mphys.mphys_dafoam.DAFoamFvSource.nForceSections
nForceSections
Definition: mphys_dafoam.py:1812
dafoam.mphys.mphys_dafoam.DAFoamBuilder.mesh_options
mesh_options
Definition: mphys_dafoam.py:29
dafoam.mphys.mphys_dafoam.DAFoamSolver.apply_options
def apply_options(self, optionDict)
Definition: mphys_dafoam.py:654
dafoam.mphys.mphys_dafoam.DAFoamForces.compute_jacvec_product
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode)
Definition: mphys_dafoam.py:1601
dafoam.mphys.mphys_dafoam.DAFoamGroup.mphys_set_unmasking
def mphys_set_unmasking(self, forces=False)
Definition: mphys_dafoam.py:320
dafoam.mphys.mphys_dafoam.DAFoamPropNodes.compute_jacvec_product
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode)
Definition: mphys_dafoam.py:2044
dafoam.mphys.mphys_dafoam.DAFoamFunctions.compute_jacvec_product
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode)
Definition: mphys_dafoam.py:1215
dafoam.mphys.mphys_dafoam.DAFoamMeshGroup.setup
def setup(self)
Definition: mphys_dafoam.py:1008
dafoam.mphys.mphys_dafoam.DAFoamSolver.apply_linear
def apply_linear(self, inputs, outputs, d_inputs, d_outputs, d_residuals, mode)
Definition: mphys_dafoam.py:722
dafoam.mphys.mphys_dafoam.DAFoamSolver.solve_nonlinear
def solve_nonlinear(self, inputs, outputs)
Definition: mphys_dafoam.py:671
dafoam.mphys.mphys_dafoam.DAFoamThermal.initialize
def initialize(self)
Definition: mphys_dafoam.py:1428
dafoam.mphys.mphys_dafoam.DAFoamAcoustics.groupName
groupName
Definition: mphys_dafoam.py:1645
dafoam.mphys.mphys_dafoam.DAFoamForces.setup
def setup(self)
Definition: mphys_dafoam.py:1583
dafoam.mphys.mphys_dafoam.DAFoamMesh.setup
def setup(self)
Definition: mphys_dafoam.py:1038
dafoam.mphys.mphys_dafoam.DAFoamBuilder.get_post_coupling_subsystem
def get_post_coupling_subsystem(self, scenario_name=None)
Definition: mphys_dafoam.py:115
dafoam.mphys.mphys_dafoam.DAFoamSolver.solve_linear
def solve_linear(self, d_outputs, d_residuals, mode)
Definition: mphys_dafoam.py:867
dafoam.mphys.mphys_dafoam.DAFoamPropNodes.aerostructDict
aerostructDict
Definition: mphys_dafoam.py:1971
dafoam.mphys.mphys_dafoam.DAFoamFunctions.discipline
discipline
Definition: mphys_dafoam.py:1118
dafoam.mphys.mphys_dafoam.DAFoamFunctions.add_dv_func
def add_dv_func(self, dvName, dv_func)
Definition: mphys_dafoam.py:1176
dafoam.mphys.mphys_dafoam.DAFoamPrecouplingGroup.thermal_coupling
thermal_coupling
Definition: mphys_dafoam.py:396
dafoam.mphys.mphys_dafoam.DAFoamPrecouplingGroup.warp_in_solver
warp_in_solver
Definition: mphys_dafoam.py:395
dafoam.mphys.mphys_dafoam.DAFoamSolver.evalFuncs
evalFuncs
Definition: mphys_dafoam.py:585
dafoam.mphys.mphys_dafoam.OptFuncs.__init__
def __init__(self, daOptions, om_prob)
Definition: mphys_dafoam.py:2126
dafoam.mphys.mphys_dafoam.DAFoamForces.initialize
def initialize(self)
Definition: mphys_dafoam.py:1580
dafoam.mphys.mphys_dafoam.DAFoamBuilder.get_mesh_coordinate_subsystem
def get_mesh_coordinate_subsystem(self, scenario_name=None)
Definition: mphys_dafoam.py:101
dafoam.mphys.mphys_dafoam.DAFoamGroup.struct_coupling
struct_coupling
Definition: mphys_dafoam.py:157
dafoam.mphys.mphys_dafoam.DAFoamFunctions.dv_funcs
dv_funcs
Definition: mphys_dafoam.py:1121
dafoam.mphys.mphys_dafoam.DAFoamFvSource.compute_jacvec_product
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode)
Definition: mphys_dafoam.py:1877
dafoam.mphys.mphys_dafoam.DAFoamWarper.initialize
def initialize(self)
Definition: mphys_dafoam.py:1367
dafoam.mphys.mphys_dafoam.DAFoamSolver.psi
psi
Definition: mphys_dafoam.py:575
dafoam.mphys.mphys_dafoam.DAFoamAcoustics
Definition: mphys_dafoam.py:1632
dafoam.mphys.mphys_dafoam.DAFoamFaceCoords.nFaces
nFaces
Definition: mphys_dafoam.py:1534
dafoam.mphys.mphys_dafoam.DAFoamSolver.linearize
def linearize(self, inputs, outputs, residuals)
Definition: mphys_dafoam.py:717
dafoam.mphys.mphys_dafoam.DAFoamWarper.discipline
discipline
Definition: mphys_dafoam.py:1375
dafoam.mphys.mphys_dafoam.DAFoamBuilder.warp_in_solver
warp_in_solver
Definition: mphys_dafoam.py:36
dafoam.mphys.mphys_dafoam.DAFoamFunctions.mphys_add_funcs
def mphys_add_funcs(self)
Definition: mphys_dafoam.py:1161
dafoam.mphys.mphys_dafoam.DAFoamAcoustics.compute_jacvec_product
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode)
Definition: mphys_dafoam.py:1669
dafoam.mphys.mphys_dafoam.DAFoamActuator.fvSourceDict
fvSourceDict
Definition: mphys_dafoam.py:2078
dafoam.mphys.mphys_dafoam.OptFuncs.comm
comm
Definition: mphys_dafoam.py:2137
dafoam.mphys.mphys_dafoam.DAFoamPrecouplingGroup.DASolver
DASolver
Definition: mphys_dafoam.py:394
dafoam.mphys.mphys_dafoam.DAFoamAcoustics.compute
def compute(self, inputs, outputs)
Definition: mphys_dafoam.py:1658
dafoam.mphys.mphys_dafoam.DAFoamPrecouplingGroup.initialize
def initialize(self)
Definition: mphys_dafoam.py:388
dafoam.mphys.mphys_dafoam.DAFoamThermal.nCouplingFaces
nCouplingFaces
Definition: mphys_dafoam.py:1437
dafoam.mphys.mphys_dafoam.DAFoamSolver
Definition: mphys_dafoam.py:538
dafoam.mphys.mphys_dafoam.DAFoamBuilder
Definition: mphys_dafoam.py:17
dafoam.mphys.mphys_dafoam.DAFoamSolver.setup
def setup(self)
Definition: mphys_dafoam.py:548
dafoam.mphys.mphys_dafoam.DAFoamFaceCoords.setup
def setup(self)
Definition: mphys_dafoam.py:1526
dafoam.mphys.mphys_dafoam.DAFoamMeshGroup.discipline
discipline
Definition: mphys_dafoam.py:1011
dafoam.mphys.mphys_dafoam.DAFoamPropForce
Definition: mphys_dafoam.py:1704
dafoam.mphys.mphys_dafoam.DAFoamWarper.setup
def setup(self)
Definition: mphys_dafoam.py:1370
dafoam.mphys.mphys_dafoam.DAFoamActuator.compute
def compute(self, inputs, outputs)
Definition: mphys_dafoam.py:2086
dafoam.mphys.mphys_dafoam.DAFoamForces
Definition: mphys_dafoam.py:1574
dafoam.mphys.mphys_dafoam.DAFoamAcoustics.DASolver
DASolver
Definition: mphys_dafoam.py:1644
dafoam.mphys.mphys_dafoam.DAFoamForces.discipline
discipline
Definition: mphys_dafoam.py:1587
dafoam.mphys.mphys_dafoam.DAFoamPropNodes
Definition: mphys_dafoam.py:1960
dafoam.mphys.mphys_dafoam.DAFoamAcoustics.initialize
def initialize(self)
Definition: mphys_dafoam.py:1638
dafoam.mphys.mphys_dafoam.DAFoamPropNodes.fvSourceDict
fvSourceDict
Definition: mphys_dafoam.py:1972
dafoam.mphys.mphys_dafoam.DAFoamSolver.optionDict
optionDict
Definition: mphys_dafoam.py:566
dafoam.mphys.mphys_dafoam.DAFoamMesh.mphys_get_surface_size
def mphys_get_surface_size(self, groupName)
Definition: mphys_dafoam.py:1077
dafoam.mphys.mphys_dafoam.DAFoamGroup.use_warper
use_warper
Definition: mphys_dafoam.py:158
dafoam.mphys.mphys_dafoam.DAFoamPostcouplingGroup
Definition: mphys_dafoam.py:506
dafoam.mphys.mphys_dafoam.DAFoamBuilder.run_directory
run_directory
Definition: mphys_dafoam.py:43
dafoam.mphys.mphys_dafoam.DAFoamPostcouplingGroup.DASolver
DASolver
Definition: mphys_dafoam.py:515
dafoam.mphys.mphys_dafoam.DAFoamSolver.runColoring
runColoring
Definition: mphys_dafoam.py:580
dafoam.mphys.mphys_dafoam.DAFoamActuator.initialize
def initialize(self)
Definition: mphys_dafoam.py:2071
dafoam.mphys.mphys_dafoam.DAFoamGroup.mphys_compute_nodes
def mphys_compute_nodes(self)
Definition: mphys_dafoam.py:238
dafoam.mphys.mphys_dafoam.DAFoamThermal.discipline
discipline
Definition: mphys_dafoam.py:1435
dafoam.mphys.mphys_dafoam.DAFoamFunctions.initialize
def initialize(self)
Definition: mphys_dafoam.py:1108
dafoam.mphys.mphys_dafoam.DAFoamPrecouplingGroup
Definition: mphys_dafoam.py:383
dafoam.mphys.mphys_dafoam.DAFoamFunctions
Definition: mphys_dafoam.py:1103
dafoam.mphys.mphys_dafoam.DAFoamFvSource.compute
def compute(self, inputs, outputs)
Definition: mphys_dafoam.py:1835
dafoam.mphys.mphys_dafoam.DAFoamActuator.compute_jacvec_product
def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode)
Definition: mphys_dafoam.py:2099
dafoam.mphys.mphys_dafoam.DAFoamSolver.DASolver
DASolver
Definition: mphys_dafoam.py:553
dafoam.mphys.mphys_dafoam.DAFoamSolver.apply_nonlinear
def apply_nonlinear(self, inputs, outputs, residuals)
Definition: mphys_dafoam.py:663
dafoam.mphys.mphys_dafoam.DAFoamFvSource.initialize
def initialize(self)
Definition: mphys_dafoam.py:1802
dafoam.mphys.mphys_dafoam.DAFoamFunctions.apply_options
def apply_options(self, optionDict)
Definition: mphys_dafoam.py:1191
dafoam.mphys.mphys_dafoam.DAFoamThermal.setup
def setup(self)
Definition: mphys_dafoam.py:1431
dafoam.mphys.mphys_dafoam.DAFoamPrecouplingGroup.setup
def setup(self)
Definition: mphys_dafoam.py:393
dafoam.mphys.mphys_dafoam.DAFoamGroup.DASolver
DASolver
Definition: mphys_dafoam.py:156
dafoam.mphys.mphys_dafoam.DAFoamFaceCoords.DASolver
DASolver
Definition: mphys_dafoam.py:1528
dafoam.mphys.mphys_dafoam.DAFoamFunctions.compute
def compute(self, inputs, outputs)
Definition: mphys_dafoam.py:1200
dafoam.mphys.mphys_dafoam.DAFoamPostcouplingGroup.setup
def setup(self)
Definition: mphys_dafoam.py:514
dafoam.mphys.mphys_dafoam.DAFoamGroup.run_directory
run_directory
Definition: mphys_dafoam.py:161