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
8 from petsc4py
import PETSc
10 from mpi4py
import MPI
11 from mphys
import MaskedConverter, UnmaskedConverter, MaskedVariableDescription
12 from mphys.utils.directory_utils
import cd
14 petsc4py.init(sys.argv)
19 DAFoam builder called from runScript.py
26 scenario="aerodynamic",
56 if scenario.lower() ==
"aerodynamic":
59 elif scenario.lower() ==
"aerostructural":
63 elif scenario.lower() ==
"aerothermal":
68 "scenario %s not valid! Options: aerodynamic, aerostructural, and aerothermal" % scenario
120 if groupName
is None:
121 groupName = self.
DASolver.couplingSurfacesGroup
122 nodes = int(self.
DASolver.getSurfaceCoordinates(groupName=groupName).size / 3)
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():
131 for fvSource, parameters
in aerostructDict[
"fvSource"].items():
133 if fvSource
not in fvSourceDict:
134 raise RuntimeWarning(
"Actuator disk {} not found when adding masked nodes".format(fvSource))
137 nodes += 1 + parameters[
"nNodes"]
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=
"")
166 raise AnalysisError(
"prop_coupling can be either Wing or Prop, while %s is given!" % self.
prop_coupling)
168 aerostructDict = self.
DASolver.getOption(
"couplingInfo")[
"aerostructural"]
174 if aerostructDict[
"active"]
and aerostructDict[
"propMovement"]:
176 self.add_subsystem(
"prop_movement", prop_movement, promotes_inputs=[
"*"], promotes_outputs=[
"*"])
185 promotes_outputs=[
"%s_vol_coords" % self.
discipline],
187 elif aerostructDict[
"active"]
and aerostructDict[
"propMovement"]:
189 "Propeller movement not possible when the warper is outside of the solver. Check for a valid scenario."
197 promotes_inputs=[
"*"],
198 promotes_outputs=[
"*"],
205 promotes_inputs=[
"*"],
206 promotes_outputs=[
"%s_states" % self.
discipline],
214 promotes_inputs=[
"*"],
215 promotes_outputs=[
"*"],
223 promotes_outputs=[(
"f_aero",
"f_aero_masked")],
231 promotes_inputs=[
"*"],
232 promotes_outputs=[
"*"],
239 aerostructDict = self.
DASolver.getOption(
"couplingInfo")[
"aerostructural"]
240 fvSourceDict = self.
DASolver.getOption(
"fvSource")
244 if self.comm.rank == 0:
245 if aerostructDict[
"active"]
and aerostructDict[
"propMovement"]:
246 if "fvSource" in aerostructDict.keys():
248 for fvSource, parameters
in aerostructDict[
"fvSource"].items():
250 if fvSource
not in fvSourceDict:
251 raise RuntimeWarning(
"Actuator disk %s not found when adding masked nodes" % fvSource)
254 nodes_prop += 1 + parameters[
"nNodes"]
257 nodes_aero = int(self.
DASolver.getSurfaceCoordinates(groupName=self.
DASolver.designSurfacesGroup).size / 3)
260 nodes_total = nodes_aero + nodes_prop
261 return nodes_total, nodes_aero, nodes_prop
267 aerostructDict = self.
DASolver.getOption(
"couplingInfo")[
"aerostructural"]
272 promotes_outputs = []
275 mask.append(np.zeros([(nodes_total) * 3], dtype=bool))
279 mask[0][3 * nodes_aero :] =
False
282 MaskedVariableDescription(
"x_%s_masked" % self.
discipline, shape=(nodes_aero) * 3, tags=[
"mphys_coupling"])
285 promotes_outputs.append(
"x_%s_masked" % self.
discipline)
288 if aerostructDict[
"active"]
and aerostructDict[
"propMovement"]:
289 if "fvSource" in aerostructDict.keys():
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
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"])
301 MaskedVariableDescription(
302 "x_prop_%s" % fvSource, shape=((1 + parameters[
"nNodes"])) * 3, tags=[
"mphys_coupling"]
307 MaskedVariableDescription(
"x_prop_%s" % fvSource, shape=(0), tags=[
"mphys_coupling"])
310 promotes_outputs.append(
"x_prop_%s" % fvSource)
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)
326 aerostructDict = self.
DASolver.getOption(
"couplingInfo")[
"aerostructural"]
331 promotes_outputs = []
334 mask.append(np.zeros([(nodes_total) * 3], dtype=bool))
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")
341 if aerostructDict[
"active"]
and aerostructDict[
"propMovement"]:
342 if "fvSource" in aerostructDict.keys():
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
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"])
355 MaskedVariableDescription(
356 "f_prop_%s" % fvSource,
357 shape=((1 + parameters[
"nNodes"])) * 3,
358 tags=[
"mphys_coordinates"],
363 MaskedVariableDescription(
"f_prop_%s" % fvSource, shape=(0), tags=[
"mphys_coupling"])
365 promotes_inputs.append(
"f_prop_%s" % fvSource)
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)
374 "force_unmasker", unmasker, promotes_inputs=promotes_inputs, promotes_outputs=promotes_outputs
380 self.solver.set_options(optionDict)
385 Pre-coupling group that configures any components that happen before the solver and post-processor.
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)
399 aerostructDict = self.
DASolver.getOption(
"couplingInfo")[
"aerostructural"]
403 if aerostructDict[
"active"]
and aerostructDict[
"propMovement"]:
405 "Propeller movement not possible when the warper is outside of the solver. Check for a valid scenario."
412 promotes_outputs=[
"%s_vol_coords" % self.
discipline],
417 fvSourceDict = self.
DASolver.getOption(
"fvSource")
421 if aerostructDict[
"active"]
and aerostructDict[
"propMovement"]:
427 if self.comm.rank == 0:
428 if "fvSource" in aerostructDict.keys():
430 for fvSource, parameters
in aerostructDict[
"fvSource"].items():
432 if fvSource
not in fvSourceDict:
433 raise RuntimeWarning(
"Actuator disk %s not found when adding masked nodes" % fvSource)
436 nodes_prop += 1 + parameters[
"nNodes"]
438 nodes_aero = int(self.
DASolver.getSurfaceCoordinates(groupName=self.
DASolver.designSurfacesGroup).size / 3)
439 nodes_total = nodes_aero + nodes_prop
446 mask.append(np.zeros([(nodes_total) * 3], dtype=bool))
449 mask[0][3 * nodes_aero :] =
False
451 MaskedVariableDescription(
452 "x_%s0_masked" % self.
discipline, shape=(nodes_aero) * 3, tags=[
"mphys_coordinates"]
455 promotes_inputs.append(
"x_%s0_masked" % self.
discipline)
458 if aerostructDict[
"active"]
and aerostructDict[
"propMovement"]:
460 if "fvSource" in aerostructDict.keys():
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
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"])
472 MaskedVariableDescription(
473 "x_prop0_nodes_%s" % fvSource,
474 shape=((1 + parameters[
"nNodes"])) * 3,
475 tags=[
"mphys_coordinates"],
480 MaskedVariableDescription(
481 "x_prop0_nodes_%s" % fvSource, shape=(0), tags=[
"mphys_coordinates"]
484 promotes_inputs.append(
"x_prop0_nodes_%s" % fvSource)
488 output = MaskedVariableDescription(
489 "x_%s0" % self.
discipline, shape=(nodes_total) * 3, tags=[
"mphys_coordinates"]
492 unmasker = UnmaskedConverter(input=input, output=output, mask=mask, distributed=
True, default_values=0.0)
494 "unmasker", unmasker, promotes_inputs=promotes_inputs, promotes_outputs=[
"x_%s0" % self.
discipline]
501 promotes_inputs=[
"*"],
502 promotes_outputs=[
"*"],
508 Post-coupling group that configures any components that happen in the post-processor.
512 self.options.declare(
"solver", default=
None, recordable=
False)
521 couplingInfo = self.
DASolver.getOption(
"couplingInfo")
522 if couplingInfo[
"aeroacoustic"][
"active"]:
523 for groupName
in couplingInfo[
"aeroacoustic"][
"couplingSurfaceGroups"]:
540 OpenMDAO component that wraps the DAFoam flow and adjoint solvers
544 self.options.declare(
"solver", recordable=
False)
545 self.options.declare(
"prop_coupling", recordable=
False)
546 self.options.declare(
"run_directory", default=
"")
572 DASolver.solverAD.initializedRdWTMatrixFree(DASolver.xvVec, DASolver.wVec)
576 self.
psi.zeroEntries()
579 if DASolver.getOption(
"adjEqnSolMethod") ==
"fixedPoint":
588 local_state_size = DASolver.getNLocalAdjointStates()
590 designVariables = DASolver.getOption(
"designVar")
594 for dvName
in list(designVariables.keys()):
595 self.
dvType[dvName] = designVariables[dvName][
"designVarType"]
600 "%s_states" % self.
discipline, distributed=
True, shape=local_state_size, tags=[
"mphys_coupling"]
603 couplingInfo = DASolver.getOption(
"couplingInfo")
604 if couplingInfo[
"aerothermal"][
"active"]:
605 nCells, nFaces = self.
DASolver._getSurfaceSize(self.
DASolver.couplingSurfacesGroup)
609 self.add_input(
"T_convect", distributed=
True, shape=2 * nFaces, tags=[
"mphys_coupling"])
611 self.add_input(
"q_conduct", distributed=
True, shape=2 * nFaces, tags=[
"mphys_coupling"])
614 shapeVarAdded =
False
615 for dvName
in list(designVariables.keys()):
616 dvType = self.
dvType[dvName]
618 if shapeVarAdded
is False:
622 "%s_vol_coords" % self.
discipline, distributed=
True, shape_by_conn=
True, tags=[
"mphys_coupling"]
625 elif dvType ==
"AOA":
626 self.add_input(dvName, distributed=
False, shape_by_conn=
True, tags=[
"mphys_coupling"])
628 self.add_input(dvName, distributed=
False, shape_by_conn=
True, tags=[
"mphys_coupling"])
629 elif dvType ==
"ACTD":
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":
635 self.add_input(dvName, distributed=
True, shape_by_conn=
True, tags=[
"mphys_coupling"])
637 raise AnalysisError(
"designVarType %s not supported! " % dvType)
645 raise AnalysisError(
"dvName %s is already in self.dv_funcs! " % dvName)
655 if optionDict
is not None:
658 for key
in optionDict.keys():
659 self.
DASolver.setOption(key, optionDict[key])
665 DASolver.setStates(outputs[
"%s_states" % self.
discipline])
668 residuals[
"%s_states" % self.
discipline] = DASolver.getResiduals()
678 DASolver.setOption(
"runStatus",
"solvePrimal")
686 dvVal = inputs[dvName]
687 func(dvVal, DASolver)
689 DASolver.updateDAOption()
691 couplingInfo = DASolver.getOption(
"couplingInfo")
692 if couplingInfo[
"aerothermal"][
"active"]:
694 T_convect = inputs[
"T_convect"]
695 DASolver.solver.setThermal(T_convect)
697 q_conduct = inputs[
"q_conduct"]
698 DASolver.solver.setThermal(q_conduct)
700 raise AnalysisError(
"discipline not valid!")
707 DASolver.evalFunctions(funcs, evalFuncs=self.
evalFuncs)
710 outputs[
"%s_states" % self.
discipline] = DASolver.getStates()
715 raise AnalysisError(
"Primal solution failed!")
722 def apply_linear(self, inputs, outputs, d_inputs, d_outputs, d_residuals, mode):
729 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
732 category=om.OpenMDAOWarning,
744 dvVal = inputs[dvName]
745 func(dvVal, DASolver)
748 DASolver.setStates(outputs[
"%s_states" % self.
discipline])
750 designVariables = DASolver.getOption(
"designVar")
752 if "%s_states" % self.
discipline in d_residuals:
755 resBar = d_residuals[
"%s_states" % self.
discipline]
757 resBarVec = DASolver.array2Vec(resBar)
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
768 for inputName
in list(d_inputs.keys()):
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":
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":
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
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
803 if self.comm.rank == 0:
804 aoaBar = DASolver.vec2Array(prodVec)[0]
808 d_inputs[inputName] += self.comm.bcast(aoaBar, root=0)
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
820 if self.comm.rank == 0:
821 BCBar = DASolver.vec2Array(prodVec)[0]
825 d_inputs[inputName] += self.comm.bcast(BCBar, root=0)
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
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
845 d_inputs[inputName] += ACTDBar
848 elif self.
dvType[inputName] ==
"Field":
849 nLocalCells = self.
DASolver.solver.getNLocalCells()
850 fieldType = DASolver.getOption(
"designVar")[inputName][
"fieldType"]
852 if fieldType ==
"vector":
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
861 fieldBar = DASolver.vec2Array(prodVec)
862 d_inputs[inputName] += fieldBar
865 raise AnalysisError(
"designVarType %s not supported! " % self.
dvType[inputName])
873 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
876 category=om.OpenMDAOWarning,
885 DASolver.setOption(
"runStatus",
"solveAdjoint")
886 DASolver.updateDAOption()
888 adjEqnSolMethod = DASolver.getOption(
"adjEqnSolMethod")
891 dFdWArray = d_outputs[
"%s_states" % self.
discipline]
893 dFdW = DASolver.array2Vec(dFdWArray)
900 if adjEqnSolMethod ==
"Krylov":
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)
921 if self.comm.rank == 0:
923 print(
"---------------------------------------------")
928 adjPCLag = DASolver.getOption(
"adjPCLag")
929 if DASolver.dRdWTPC
is None or DASolver.ksp
is None or (self.
solution_counter - 1) % adjPCLag == 0:
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)
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)
945 if not self.
DASolver.getOption(
"adjEqnOption")[
"useNonZeroInitGuess"]:
948 if self.
DASolver.getOption(
"adjEqnOption")[
"dynAdjustTol"]:
955 fail = DASolver.solverAD.solveLinearEqn(DASolver.ksp, dFdW, self.
psi)
956 elif adjEqnSolMethod ==
"fixedPoint":
962 if self.comm.rank == 0:
964 print(
"---------------------------------------------")
967 fail = DASolver.solverAD.runFPAdj(DASolver.xvVec, DASolver.wVec, dFdW, self.
psi)
969 raise RuntimeError(
"adjEqnSolMethod=%s not valid! Options are: Krylov or fixedPoint" % adjEqnSolMethod)
972 d_residuals[
"%s_states" % self.
discipline] = DASolver.vec2Array(self.
psi)
976 raise AnalysisError(
"Adjoint solution failed!")
978 def _updateKSPTolerances(self, psi, dFdW, ksp):
987 rVec = self.
DASolver.wVec.duplicate()
989 DASolver.solverAD.calcdRdWTPsiAD(DASolver.xvVec, DASolver.wVec, psi, rVec)
990 rVec.axpy(-1.0, dFdW)
993 rTol0 = self.
DASolver.getOption(
"adjEqnOption")[
"gmresRelTol"]
994 aTol0 = self.
DASolver.getOption(
"adjEqnOption")[
"gmresAbsTol"]
996 aTolNew = rNorm * rTol0
1001 ksp.setTolerances(rtol=0.0, atol=aTolNew, divtol=
None, max_it=
None)
1006 self.options.declare(
"solver", recordable=
False)
1009 DASolver = self.options[
"solver"]
1013 self.add_subsystem(
"surface_mesh",
DAFoamMesh(solver=DASolver), promotes=[
"*"])
1018 promotes_outputs=[
"%s_vol_coords" % self.
discipline],
1032 Component to get the partitioned initial surface mesh coordinates
1036 self.options.declare(
"solver", recordable=
False)
1048 coord_size = self.
x_a0.size
1053 desc=
"initial aerodynamic surface node coordinates",
1054 tags=[
"mphys_coordinates"],
1062 desc=
"aerodynamic surface with geom changes",
1075 return self.
DASolver.getTriangulatedMeshSurface()
1078 return self.
DASolver._getSurfaceSize(groupName)
1082 if "x_%s0_points" % self.
discipline in inputs:
1091 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1094 category=om.OpenMDAOWarning,
1099 if "x_%s0_points" % self.
discipline in d_inputs:
1105 DAFoam objective and constraint functions component
1109 self.options.declare(
"solver", recordable=
False)
1126 designVariables = self.
DASolver.getOption(
"designVar")
1128 for dvName
in list(designVariables.keys()):
1129 self.
dvType[dvName] = designVariables[dvName][
"designVarType"]
1133 self.add_input(
"%s_states" % self.
discipline, distributed=
True, shape_by_conn=
True, tags=[
"mphys_coupling"])
1136 shapeVarAdded =
False
1137 for dvName
in list(designVariables.keys()):
1138 dvType = self.
dvType[dvName]
1140 if shapeVarAdded
is False:
1144 "%s_vol_coords" % self.
discipline, distributed=
True, shape_by_conn=
True, tags=[
"mphys_coupling"]
1146 shapeVarAdded =
True
1147 elif dvType ==
"AOA":
1148 self.add_input(dvName, distributed=
False, shape_by_conn=
True, tags=[
"mphys_coupling"])
1149 elif dvType ==
"BC":
1150 self.add_input(dvName, distributed=
False, shape_by_conn=
True, tags=[
"mphys_coupling"])
1151 elif dvType ==
"ACTD":
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":
1157 self.add_input(dvName, distributed=
True, shape_by_conn=
True, tags=[
"mphys_coupling"])
1159 raise AnalysisError(
"designVarType %s not supported! " % dvType)
1165 objFuncs = self.
DASolver.getOption(
"objFunc")
1169 for objFunc
in objFuncs:
1170 self.
funcs.append(objFunc)
1173 for f_name
in self.
funcs:
1174 self.add_output(f_name, distributed=
False, shape=1, units=
None, tags=[
"mphys_result"])
1182 raise AnalysisError(
"dvName %s is already in self.dv_funcs! " % dvName)
1192 if optionDict
is not None:
1195 for key
in optionDict.keys():
1196 self.
DASolver.setOption(key, optionDict[key])
1204 DASolver.setStates(inputs[
"%s_states" % self.
discipline])
1208 if self.
funcs is not None:
1209 DASolver.evalFunctions(funcs, evalFuncs=self.
funcs)
1210 for f_name
in self.
funcs:
1212 outputs[f_name] = funcs[f_name]
1220 DASolver.setOption(
"runStatus",
"solveAdjoint")
1221 DASolver.updateDAOption()
1223 designVariables = DASolver.getOption(
"designVar")
1230 dvVal = inputs[dvName]
1231 func(dvVal, DASolver)
1232 DASolver.setStates(inputs[
"%s_states" % self.
discipline])
1237 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1240 category=om.OpenMDAOWarning,
1248 if self.
funcs is None:
1249 raise AnalysisError(
"functions not set! Forgot to call mphys_add_funcs?")
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]
1258 if self.comm.rank == 0:
1259 print(
"Computing partials for ", list(funcsBar.keys()))
1262 for objFuncName
in list(funcsBar.keys()):
1264 fBar = funcsBar[objFuncName]
1266 for inputName
in list(d_inputs.keys()):
1269 if inputName ==
"%s_states" % self.
discipline:
1270 dFdW = DASolver.wVec.duplicate()
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
1277 elif inputName ==
"%s_vol_coords" % self.
discipline:
1278 dFdXv = DASolver.xvVec.duplicate()
1280 DASolver.solverAD.calcdFdXvAD(
1281 DASolver.xvVec, DASolver.wVec, objFuncName.encode(),
"dummy".encode(), dFdXv
1283 xVBar = DASolver.vec2Array(dFdXv)
1284 d_inputs[
"%s_vol_coords" % self.
discipline] += xVBar * fBar
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)
1297 if self.comm.rank == 0:
1298 aoaBar = DASolver.vec2Array(dFdAOA)[0] * fBar
1302 d_inputs[inputName] += self.comm.bcast(aoaBar, root=0)
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
1314 if self.comm.rank == 0:
1315 BCBar = DASolver.vec2Array(dFdBC)[0] * fBar
1319 d_inputs[inputName] += self.comm.bcast(BCBar, root=0)
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
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
1339 d_inputs[inputName] += ACTDBar * fBar
1342 elif self.
dvType[inputName] ==
"Field":
1343 nLocalCells = self.
DASolver.solver.getNLocalCells()
1344 fieldType = DASolver.getOption(
"designVar")[inputName][
"fieldType"]
1346 if fieldType ==
"vector":
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
1355 fieldBar = DASolver.vec2Array(dFdField)
1356 d_inputs[inputName] += fieldBar * fBar
1359 raise AnalysisError(
"designVarType %s not supported! " % self.
dvType[inputName])
1364 OpenMDAO component that wraps the warping.
1368 self.options.declare(
"solver", recordable=
False)
1378 local_volume_coord_size = DASolver.mesh.getSolverGrid().size
1380 self.add_input(
"x_%s" % self.
discipline, distributed=
True, shape_by_conn=
True, tags=[
"mphys_coupling"])
1382 "%s_vol_coords" % self.
discipline, distributed=
True, shape=local_volume_coord_size, tags=[
"mphys_coupling"]
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()
1395 DASolver.xvFlatten2XvVec(solverGrid, DASolver.xvVec)
1396 outputs[
"%s_vol_coords" % self.
discipline] = solverGrid
1404 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1407 category=om.OpenMDAOWarning,
1413 if "%s_vol_coords" % self.
discipline in d_outputs:
1415 dxV = d_outputs[
"%s_vol_coords" % self.
discipline]
1419 d_inputs[
"x_%s" % self.
discipline] += dxS.flatten()
1424 OpenMDAO component that wraps conjugate heat transfer integration
1429 self.options.declare(
"solver", recordable=
False)
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"])
1443 self.add_output(
"T_conduct", distributed=
True, shape=self.
nCouplingFaces * 2, tags=[
"mphys_coupling"])
1445 self.add_output(
"q_convect", distributed=
True, shape=self.
nCouplingFaces * 2, tags=[
"mphys_coupling"])
1447 raise AnalysisError(
"%s not supported! Options are: aero or thermal" % self.
discipline)
1453 vol_coords = inputs[
"%s_vol_coords" % self.
discipline]
1454 states = inputs[
"%s_states" % self.
discipline]
1460 self.
DASolver.solver.getThermal(vol_coords, states, thermal)
1462 outputs[
"T_conduct"] = thermal
1466 self.
DASolver.solver.getThermal(vol_coords, states, thermal)
1468 outputs[
"q_convect"] = thermal
1471 raise AnalysisError(
"%s not supported! Options are: aero or thermal" % self.
discipline)
1477 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1480 category=om.OpenMDAOWarning,
1486 vol_coords = inputs[
"%s_vol_coords" % self.
discipline]
1487 states = inputs[
"%s_states" % self.
discipline]
1489 if "T_conduct" in d_outputs:
1490 seeds = d_outputs[
"T_conduct"]
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
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
1502 if "q_convect" in d_outputs:
1503 seeds = d_outputs[
"q_convect"]
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
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
1518 Calculate coupling surface coordinates based on volume coordinates
1523 self.options.declare(
"solver", recordable=
False)
1524 self.options.declare(
"groupName", recordable=
False)
1530 groupName = self.options[
"groupName"]
1532 self.add_input(
"%s_vol_coords" % self.
discipline, distributed=
True, shape_by_conn=
True, tags=[
"mphys_coupling"])
1538 "x_%s_surface0" % self.
discipline, distributed=
True, shape=self.
nFaces * 6, tags=[
"mphys_coupling"]
1543 volCoords = inputs[
"%s_vol_coords" % self.
discipline]
1545 nCouplingFaces = self.
DASolver.solver.getNCouplingFaces()
1546 surfCoords = np.zeros(nCouplingFaces * 6)
1547 self.
DASolver.solver.calcCouplingFaceCoords(volCoords, surfCoords)
1549 outputs[
"x_%s_surface0" % self.
discipline] = surfCoords
1555 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1558 category=om.OpenMDAOWarning,
1564 if "x_%s_surface0" % self.
discipline in d_outputs:
1565 seeds = d_outputs[
"x_%s_surface0" % self.
discipline]
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
1576 OpenMDAO component that wraps force integration
1581 self.options.declare(
"solver", recordable=
False)
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"])
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"])
1599 outputs[
"f_aero"] = self.
DASolver.getForces().flatten(order=
"C")
1607 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1610 category=om.OpenMDAOWarning,
1614 if "f_aero" in d_outputs:
1615 fBar = d_outputs[
"f_aero"]
1616 fBarVec = DASolver.array2Vec(fBar)
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
1634 OpenMDAO component that wraps acoustic coupling
1639 self.options.declare(
"solver", recordable=
False)
1640 self.options.declare(
"groupName", recordable=
False)
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"])
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)
1662 positions, normals, areas, forces = self.
DASolver.getAcousticData(self.
groupName)
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")
1675 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1678 category=om.OpenMDAOWarning,
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()
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()
1700 wBar = DASolver.vec2Array(dAcoudW)
1701 d_inputs[
"%s_states" % self.
discipline] += wBar
1706 DAFoam component that computes the propeller force and radius profile based on the CFD surface states
1710 def initialize(self):
1711 self.options.declare("solver", recordable=False)
1715 self.DASolver = self.options["solver"]
1717 self.discipline = self.DASolver.getOption("discipline")
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"])
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"])
1726 def compute(self, inputs, outputs):
1728 DASolver = self.DASolver
1730 dafoam_states = inputs["%s_states" % self.discipline]
1731 dafoam_xv = inputs["%s_vol_coords" % self.discipline]
1733 stateVec = DASolver.array2Vec(dafoam_states)
1734 xvVec = DASolver.array2Vec(dafoam_xv)
1736 fProfileVec = PETSc.Vec().createSeq(3 * self.nForceSections, bsize=1, comm=PETSc.COMM_SELF)
1737 fProfileVec.zeroEntries()
1739 sRadiusVec = PETSc.Vec().createSeq(self.nForceSections, bsize=1, comm=PETSc.COMM_SELF)
1740 sRadiusVec.zeroEntries()
1742 DASolver.solver.calcForceProfile(xvVec, stateVec, fProfileVec, sRadiusVec)
1744 outputs["force_profile"] = DASolver.vec2ArraySeq(fProfileVec)
1745 outputs["radial_location"] = DASolver.vec2ArraySeq(sRadiusVec)
1747 def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
1748 DASolver = self.DASolver
1750 dafoam_states = inputs["%s_states" % self.discipline]
1751 dafoam_xv = inputs["%s_vol_coords" % self.discipline]
1753 stateVec = DASolver.array2Vec(dafoam_states)
1754 xvVec = DASolver.array2Vec(dafoam_xv)
1757 raise AnalysisError("fwd not implemented!")
1759 if "force_profile" in d_outputs:
1760 fBar = d_outputs["force_profile"]
1761 fBarVec = DASolver.array2VecSeq(fBar)
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
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
1777 if "radial_location" in d_outputs:
1778 rBar = d_outputs["radial_location"]
1779 rBarVec = DASolver.array2VecSeq(rBar)
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
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
1799 DAFoam component that computes the actuator source term based on force and radius profiles and prop center
1803 self.options.declare(
"solver", recordable=
False)
1810 for propName
in list(self.
DASolver.getOption(
"wingProp").keys()):
1811 if self.
DASolver.getOption(
"wingProp")[propName][
"active"]:
1814 propName +
"_axial_force", distributed=
False, shape=self.
nForceSections, tags=[
"mphys_coupling"]
1817 propName +
"_tangential_force",
1820 tags=[
"mphys_coupling"],
1823 propName +
"_radial_location",
1826 tags=[
"mphys_coupling"],
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"])
1833 self.add_output(
"fvSource", distributed=
True, shape=self.
nLocalCells * 3, tags=[
"mphys_coupling"])
1840 fvSourceVec = PETSc.Vec().create(self.comm)
1841 fvSourceVec.setSizes((self.
nLocalCells * 3, PETSc.DECIDE), bsize=1)
1842 fvSourceVec.setFromOptions()
1843 fvSourceVec.zeroEntries()
1845 outputs[
"fvSource"] = DASolver.vec2Array(fvSourceVec)
1848 for propName
in list(self.
DASolver.getOption(
"wingProp").keys()):
1849 if self.
DASolver.getOption(
"wingProp")[propName][
"active"]:
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"]
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)
1863 fvSourceVec.zeroEntries()
1865 DASolver.solver.calcFvSource(
1868 tangential_force_vec,
1869 radial_location_vec,
1875 outputs[
"fvSource"] += DASolver.vec2Array(fvSourceVec)
1883 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
1886 category=om.OpenMDAOWarning,
1890 if "fvSource" in d_outputs:
1891 sBar = d_outputs[
"fvSource"]
1892 sBarVec = DASolver.array2Vec(sBar)
1894 for propName
in list(self.
DASolver.getOption(
"wingProp").keys()):
1895 if self.
DASolver.getOption(
"wingProp")[propName][
"active"]:
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"]
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)
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
1915 aBar = DASolver.vec2ArraySeq(prodVec)
1916 aBar = self.comm.allreduce(aBar, op=MPI.SUM)
1917 d_inputs[propName +
"_axial_force"] += aBar
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
1925 tBar = DASolver.vec2ArraySeq(prodVec)
1926 tBar = self.comm.allreduce(tBar, op=MPI.SUM)
1927 d_inputs[propName +
"_tangential_force"] += tBar
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
1935 rBar = DASolver.vec2ArraySeq(prodVec)
1936 rBar = self.comm.allreduce(rBar, op=MPI.SUM)
1937 d_inputs[propName +
"_radial_location"] += rBar
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
1945 fBar = DASolver.vec2ArraySeq(prodVec)
1946 fBar = self.comm.allreduce(fBar, op=MPI.SUM)
1947 d_inputs[propName +
"_integral_force"] += fBar
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
1955 cBar = DASolver.vec2ArraySeq(prodVec)
1956 cBar = self.comm.allreduce(cBar, op=MPI.SUM)
1957 d_inputs[propName +
"_prop_center"] += cBar
1962 Component that computes propeller aero-node locations that link with structural nodes in aerostructural cases.
1966 self.options.declare(
"solver", default=
None, recordable=
False)
1976 for fvSource, parameters
in self.
aerostructDict[
"fvSource"].items():
1979 raise RuntimeWarning(
"Actuator disk %s not found when adding masked nodes" % fvSource)
1982 self.add_input(
"x_prop0_%s" % fvSource, shape=3, distributed=
False, tags=[
"mphys_coordinates"])
1985 if self.comm.rank == 0:
1987 "x_prop0_nodes_%s" % fvSource,
1988 shape=(1 + parameters[
"nNodes"]) * 3,
1990 tags=[
"mphys_coordinates"],
1993 "f_prop_%s" % fvSource,
1994 shape=(1 + parameters[
"nNodes"]) * 3,
1996 tags=[
"mphys_coordinates"],
2000 "x_prop0_nodes_%s" % fvSource, shape=(0), distributed=
True, tags=[
"mphys_coordinates"]
2002 self.add_output(
"f_prop_%s" % fvSource, shape=(0), distributed=
True, tags=[
"mphys_coordinates"])
2006 for fvSource, parameters
in self.
aerostructDict[
"fvSource"].items():
2008 if self.comm.rank == 0:
2009 center = inputs[
"x_prop0_%s" % fvSource]
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)
2023 n_theta = parameters[
"nNodes"]
2024 radial_loc = parameters[
"radialLoc"]
2027 nodes_x = np.zeros((n_theta + 1, 3))
2028 nodes_x[0, :] = center
2029 nodes_f = np.zeros((n_theta + 1, 3))
2031 nodes_f[0, :] = -self.
fvSourceDict[fvSource][
"targetThrust"] * direction
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)
2039 nodes_f[i + 1, :] = -self.
fvSourceDict[fvSource][
"targetThrust"] * direction / n_theta
2041 outputs[
"x_prop0_nodes_%s" % fvSource] = nodes_x.flatten()
2042 outputs[
"f_prop_%s" % fvSource] = nodes_f.flatten()
2047 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
2050 category=om.OpenMDAOWarning,
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)
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]
2068 Component that updates actuator disk definition variables when actuator disks are displaced in an aerostructural case.
2072 self.options.declare(
"solver", recordable=
False)
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"])
2084 self.add_output(
"actuator_%s" % fvSource, shape_by_conn=(10), distributed=
False, tags=[
"mphys_coupling"])
2089 actuator = np.zeros(10)
2091 if self.comm.rank == 0:
2092 actuator[3:] = inputs[
"dv_actuator_%s" % fvSource][:]
2093 actuator[:3] = inputs[
"x_prop_%s" % fvSource][:3]
2096 self.comm.Bcast(actuator, root=0)
2097 outputs[
"actuator_%s" % fvSource] = actuator
2102 " mode = %s, but the forward mode functions are not implemented for DAFoam!" % mode,
2105 category=om.OpenMDAOWarning,
2111 if "actuator_%s" % fvSource
in d_outputs:
2112 if "dv_actuator_%s" % fvSource
in d_inputs:
2114 d_inputs[
"dv_actuator_%s" % fvSource][:] += d_outputs[
"actuator_%s" % fvSource][3:]
2115 if "x_prop_%s" % fvSource
in d_inputs:
2117 if self.comm.rank == 0:
2118 d_inputs[
"x_prop_%s" % fvSource][:3] += d_outputs[
"actuator_%s" % fvSource][:3]
2123 Some utility functions
2128 daOptions: dict or list
2129 The daOptions dict from runScript.py. Support more than two dicts
2132 The om.Problem() object
2143 modelDesignVars = self.
om_prob.model.get_design_vars()
2145 isList = isinstance(self.
daOptions, list)
2149 for key
in list(subDict[
"designVar"].keys()):
2150 DADesignVars.append(key)
2152 DADesignVars = list(self.
daOptions[
"designVar"].keys())
2153 for modelDV
in modelDesignVars:
2155 for dv
in DADesignVars:
2159 if dvFound
is not True:
2161 "Design variable %s is defined in the model but not found in designVar in DAOptions! " % modelDV
2169 constraintsComp=None,
2170 designVarsComp=None,
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.
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)
2190 if len(constraints) != len(designVars):
2191 raise RuntimeError(
"Sizes of the constraints and designVars lists need to be the same! ")
2193 size = len(constraints)
2196 if constraintsComp
is None:
2197 constraintsComp = size * [0]
2198 if designVarsComp
is None:
2199 designVarsComp = size * [0]
2202 epsFD = size * [1e-3]
2204 if maxNewtonStep
is None:
2205 maxNewtonStep = size * [1e16]
2208 for n
in range(maxIter):
2211 jacMat = np.zeros((size, size))
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)
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)
2231 res = con0 - targets
2234 norm = np.linalg.norm(res / targets)
2236 if self.
comm.rank == 0:
2237 print(
"FindFeasibleDesign Iter: ", n)
2238 print(
"DesignVars: ", dv0)
2239 print(
"Constraints: ", con0)
2240 print(
"Residual Norm: ", norm)
2244 if self.
comm.rank == 0:
2245 print(
"FindFeasibleDesign Converged! ")
2249 for i
in range(size):
2250 dvName = designVars[i]
2251 comp = designVarsComp[i]
2253 dvP = dv0[i] + epsFD[i]
2254 self.
om_prob.set_val(dvName, dvP, indices=comp)
2258 self.
om_prob.set_val(dvName, dv0[i], indices=comp)
2261 for j
in range(size):
2262 conName = constraints[j]
2263 comp = constraintsComp[j]
2264 val = self.
om_prob.get_val(conName)
2267 deriv = (conP - con0[j]) / epsFD[i]
2268 jacMat[j][i] = deriv
2271 deltaDV = -np.linalg.inv(jacMat).dot(res)
2274 for i
in range(size):
2275 if abs(deltaDV[i]) > abs(maxNewtonStep[i]):
2277 deltaDV[i] = abs(maxNewtonStep[i])
2279 deltaDV[i] = -abs(maxNewtonStep[i])
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)