55from openmdao .api import AnalysisError
66
77# Local modules
8- from .. import DVConstraints , DVGeometry , DVGeometryESP , DVGeometryVSP
8+ from .. import DVConstraints , DVGeometry , DVGeometryESP , DVGeometryMulti , DVGeometryVSP
99
1010
1111# class that actually calls the DVGeometry methods
@@ -83,6 +83,9 @@ def setup(self):
8383 elif info ["type" ] == "esp" :
8484 self .DVGeos .update ({name : DVGeometryESP (info ["file" ], comm = self .comm , name = DVGeoName , ** options )})
8585
86+ elif info ["type" ] == "multi" :
87+ self .DVGeos .update ({name : DVGeometryMulti (comm = self .comm , ** options )})
88+
8689 # add each geometry to the constraints object
8790 for _ , DVGeo in self .DVGeos .items ():
8891 self .DVCon .setDVGeo (DVGeo , name = DVConName )
@@ -125,7 +128,39 @@ def compute(self, inputs, outputs):
125128 # next time the jacvec product routine is called
126129 self .update_jac = True
127130
128- def nom_addChild (self , ffd_file , DVGeoName = None , childName = None ):
131+ def nom_addComponent (self , comp , ffd_file , triMesh , DVGeoName = None ):
132+ """
133+ Add a component a DVGeometryMulti object. This is a wrapper for the DVGeoMulti.addComponent method.
134+
135+ Parameters
136+ ----------
137+ comp : str
138+ The name of the component.
139+
140+ ffd_file : str
141+ Path to the FFD file for the new DVGeo object for this component.
142+
143+ triMesh : str, optional
144+ Path to the triangulated mesh file for this component.
145+
146+ DVGeoName : str, optional
147+ The name of the DVGeo object to add this component to.
148+ """
149+
150+ # if we have multiple DVGeos use the one specified by name
151+ DVGeo = self .nom_getDVGeo (DVGeoName = DVGeoName )
152+
153+ # can only add a DVGeo to a DVGeoMulti
154+ if not isinstance (DVGeo , DVGeometryMulti ):
155+ raise RuntimeError (
156+ f"Only multi-based DVGeo objects can have components added to them, not type:{ self .geo_type } "
157+ )
158+
159+ # Add component
160+ DVGeoComp = DVGeometry (ffd_file )
161+ DVGeo .addComponent (comp = comp , DVGeo = DVGeoComp , triMesh = triMesh )
162+
163+ def nom_addChild (self , ffd_file , DVGeoName = None , childName = None , comp = None ):
129164 # if we have multiple DVGeos use the one specified by name
130165 DVGeo = self .nom_getDVGeo (DVGeoName = DVGeoName )
131166
@@ -137,24 +172,27 @@ def nom_addChild(self, ffd_file, DVGeoName=None, childName=None):
137172
138173 # Add child FFD
139174 child_ffd = DVGeometry (ffd_file , child = True , name = childName )
140- DVGeo .addChild (child_ffd )
175+
176+ if comp is None :
177+ DVGeo .addChild (child_ffd )
178+ else :
179+ DVGeo .DVGeoDict [comp ].addChild (child_ffd )
141180
142181 # Embed points from parent if not already done
143182 for pointSet in DVGeo .points :
144183 if pointSet not in child_ffd .points :
145184 child_ffd .addPointSet (DVGeo .points [pointSet ], pointSet )
146185
147- def nom_add_discipline_coords (self , discipline , points = None , DVGeoName = None ):
186+ def nom_add_discipline_coords (self , discipline , points = None , DVGeoName = None , ** kwargs ):
148187 # TODO remove one of these methods to keep only one method to add pointsets
149-
150188 if points is None :
151189 # no pointset info is provided, just do a generic i/o. We will add these points during the first compute
152190 self .add_input ("x_%s_in" % discipline , distributed = True , shape_by_conn = True )
153191 self .add_output ("x_%s0" % discipline , distributed = True , copy_shape = "x_%s_in" % discipline )
154192
155193 else :
156194 # we are provided with points. we can do the full initialization now
157- self .nom_addPointSet (points , "x_%s0" % discipline , add_output = False , DVGeoName = DVGeoName )
195+ self .nom_addPointSet (points , "x_%s0" % discipline , add_output = False , DVGeoName = DVGeoName , ** kwargs )
158196 self .add_input ("x_%s_in" % discipline , distributed = True , val = points .flatten ())
159197 self .add_output ("x_%s0" % discipline , distributed = True , val = points .flatten ())
160198
@@ -182,18 +220,21 @@ def nom_add_point_dict(self, point_dict):
182220 for k , v in point_dict .items ():
183221 self .nom_addPointSet (v , k )
184222
185- def nom_getDVGeo (self , childName = None , DVGeoName = None ):
223+ def nom_getDVGeo (self , childName = None , comp = None , DVGeoName = None ):
186224 """
187225 Gets the DVGeometry object held in the geometry component so DVGeo methods can be called directly on it
188226
189227 Parameters
190228 ----------
191- DVGeoName : string, optional
192- The name of the DVGeo to return, necessary if there are multiple DVGeo objects
193-
194229 childName : str, optional
195230 Name of the child FFD, if you want a child DVGeo returned
196231
232+ comp : str, optional
233+ Name of the DVGeoMulti component, if this DV is for a multi component
234+
235+ DVGeoName : str, optional
236+ The name of the DVGeo to return, necessary if there are multiple DVGeo objects
237+
197238 Returns
198239 -------
199240 DVGeometry object
@@ -205,14 +246,22 @@ def nom_getDVGeo(self, childName=None, DVGeoName=None):
205246 else :
206247 DVGeo = self .DVGeos ["defaultDVGeo" ]
207248
208- # return the top level DVGeo
209- if childName is None :
210- return DVGeo
249+ # return a child of a DVGeoMulti component
250+ if childName is not None and comp is not None :
251+ return DVGeo . DVGeoDict [ comp ]. children [ childName ]
211252
212- # return a child DVGeo
213- else :
253+ # return a component of a DVGeoMulti
254+ elif comp is not None :
255+ return DVGeo .DVGeoDict [comp ]
256+
257+ # return a child of a DVGeo
258+ elif childName is not None :
214259 return DVGeo .children [childName ]
215260
261+ # return the top level DVGeo
262+ else :
263+ return DVGeo
264+
216265 def nom_getDVCon (self ):
217266 """
218267 Gets the DVConstraints object held in the geometry component so DVCon methods can be called directly on it
@@ -229,7 +278,16 @@ def nom_getDVCon(self):
229278 """
230279
231280 def nom_addGlobalDV (
232- self , dvName , value , func , childName = None , isComposite = False , DVGeoName = None , prependName = False , config = None
281+ self ,
282+ dvName ,
283+ value ,
284+ func ,
285+ childName = None ,
286+ comp = None ,
287+ isComposite = False ,
288+ DVGeoName = None ,
289+ prependName = False ,
290+ config = None ,
233291 ):
234292 """
235293 Add a global design variable to the DVGeo object. This is a wrapper for the DVGeo.addGlobalDV method.
@@ -248,6 +306,9 @@ def nom_addGlobalDV(
248306 childName : str, optional
249307 Name of the child FFD, if this DV is for a child FFD.
250308
309+ comp : str, optional
310+ Name of the DVGeoMulti component, if this DV is for a multi component
311+
251312 isComposite : bool, optional
252313 Whether this DV is to be included in the composite DVs, by default False
253314
@@ -261,10 +322,10 @@ def nom_addGlobalDV(
261322 """
262323
263324 # if we have multiple DVGeos use the one specified by name
264- DVGeo = self .nom_getDVGeo (childName = childName , DVGeoName = DVGeoName )
325+ DVGeo = self .nom_getDVGeo (childName = childName , comp = comp , DVGeoName = DVGeoName )
265326
266327 # global DVs are only added to FFD-based DVGeo objects
267- if not isinstance (DVGeo , DVGeometry ):
328+ if not isinstance (DVGeo , ( DVGeometry , DVGeometryMulti ) ):
268329 raise RuntimeError (f"Only FFD-based DVGeo objects can use global DVs, not type: { type (DVGeo ).__name__ } " )
269330
270331 # if this DVGeo object has a name attribute, prepend it to match up with what DVGeo is expecting
@@ -287,17 +348,18 @@ def nom_addLocalDV(
287348 axis = "y" ,
288349 pointSelect = None ,
289350 childName = None ,
351+ comp = None ,
290352 isComposite = False ,
291353 DVGeoName = None ,
292354 prependName = False ,
293355 volList = None ,
294356 config = None ,
295357 ):
296358 # if we have multiple DVGeos use the one specified by name
297- DVGeo = self .nom_getDVGeo (childName = childName , DVGeoName = DVGeoName )
359+ DVGeo = self .nom_getDVGeo (childName = childName , comp = comp , DVGeoName = DVGeoName )
298360
299361 # local DVs are only added to FFD-based DVGeo objects
300- if not isinstance (DVGeo , DVGeometry ):
362+ if not isinstance (DVGeo , ( DVGeometry , DVGeometryMulti ) ):
301363 raise RuntimeError (f"Only FFD-based DVGeo objects can use local DVs, not type: { type (DVGeo ).__name__ } " )
302364
303365 # if this DVGeo object has a name attribute, prepend it to match up with what DVGeo is expecting
@@ -322,6 +384,7 @@ def nom_addLocalSectionDV(
322384 dvName ,
323385 secIndex ,
324386 childName = None ,
387+ comp = None ,
325388 axis = 1 ,
326389 pointSelect = None ,
327390 volList = None ,
@@ -347,6 +410,9 @@ def nom_addLocalSectionDV(
347410 childName : str, optional
348411 Name of the child FFD, if this DV is for a child FFD.
349412
413+ comp : str, optional
414+ Name of the DVGeoMulti component, if this DV is for a multi component
415+
350416 axis : int, optional
351417 See wrapped
352418
@@ -383,7 +449,7 @@ def nom_addLocalSectionDV(
383449 DVGeo = self .nom_getDVGeo (childName = childName , DVGeoName = DVGeoName )
384450
385451 # local DVs are only added to FFD-based DVGeo objects
386- if not isinstance (DVGeo , DVGeometry ):
452+ if not isinstance (DVGeo , ( DVGeometry , DVGeometryMulti ) ):
387453 raise RuntimeError (
388454 f"Only FFD-based DVGeo objects can use local section DVs, not type: { type (DVGeo ).__name__ } "
389455 )
@@ -402,7 +468,9 @@ def nom_addLocalSectionDV(
402468 self .add_input (dvName , distributed = False , shape = nVal )
403469 return nVal
404470
405- def nom_addShapeFunctionDV (self , dvName , shapes , childName = None , config = None , DVGeoName = None , prependName = False ):
471+ def nom_addShapeFunctionDV (
472+ self , dvName , shapes , childName = None , comp = None , config = None , DVGeoName = None , prependName = False
473+ ):
406474 """
407475 Add one or more local shape function design variables to the DVGeometry object
408476 Wrapper for :meth:`addShapeFunctionDV <.DVGeometry.addShapeFunctionDV>`
@@ -419,6 +487,9 @@ def nom_addShapeFunctionDV(self, dvName, shapes, childName=None, config=None, DV
419487 childName : str, optional
420488 Name of the child FFD, if this DV is for a child FFD.
421489
490+ comp : str, optional
491+ Name of the DVGeoMulti component, if this DV is for a multi component
492+
422493 config : str or list, optional
423494 See wrapped
424495
@@ -522,6 +593,7 @@ def nom_addRefAxis(
522593 self ,
523594 name ,
524595 childName = None ,
596+ comp = None ,
525597 DVGeoName = None ,
526598 curve = None ,
527599 xFraction = None ,
@@ -542,10 +614,10 @@ def nom_addRefAxis(
542614 # But doing this may create backward incompatibility. So we will use `volumes` for now
543615
544616 # if we have multiple DVGeos use the one specified by name
545- DVGeo = self .nom_getDVGeo (childName = childName , DVGeoName = DVGeoName )
617+ DVGeo = self .nom_getDVGeo (childName = childName , comp = comp , DVGeoName = DVGeoName )
546618
547619 # references axes are only needed in FFD-based DVGeo objects
548- if not isinstance (DVGeo , DVGeometry ):
620+ if not isinstance (DVGeo , ( DVGeometry , DVGeometryMulti ) ):
549621 raise RuntimeError (f"Only FFD-based DVGeo objects can use reference axes, not type: { type (DVGeo ).__name__ } " )
550622
551623 # add ref axis to this DVGeo
@@ -914,7 +986,16 @@ def compute_jacvec_product(self, inputs, d_inputs, d_outputs, mode):
914986 d_inputs [dvname ] += jvtmp
915987
916988 for _ , DVGeo in self .DVGeos .items ():
917- for ptSetName in DVGeo .ptSetNames :
989+ if isinstance (DVGeo , DVGeometryMulti ):
990+ ptSetNames = []
991+ for comp in DVGeo .DVGeoDict .keys ():
992+ for ptSet in DVGeo .DVGeoDict [comp ].ptSetNames :
993+ if ptSet not in ptSetNames :
994+ ptSetNames .append (ptSet )
995+ else :
996+ ptSetNames = DVGeo .ptSetNames
997+
998+ for ptSetName in ptSetNames :
918999 if ptSetName in self .omPtSetList :
9191000 dout = d_outputs [ptSetName ].reshape (len (d_outputs [ptSetName ]) // 3 , 3 )
9201001
0 commit comments