1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2007-2015 CEA/DEN, EDF R&D, OPEN CASCADE
5 # This library is free software; you can redistribute it and/or
6 # modify it under the terms of the GNU Lesser General Public
7 # License as published by the Free Software Foundation; either
8 # version 2.1 of the License, or (at your option) any later version.
10 # This library is distributed in the hope that it will be useful,
11 # but WITHOUT ANY WARRANTY; without even the implied warranty of
12 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 # Lesser General Public License for more details.
15 # You should have received a copy of the GNU Lesser General Public
16 # License along with this library; if not, write to the Free Software
17 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
22 ## \defgroup structelem structelem - Structural elements package
25 # This package is used to create and visualize structural elements.
26 # It contains three modules:
27 # - This module \ref structelem "salome.geom.structelem" defines the main classes
28 # StructuralElement and StructuralElementManager that can be
29 # directly used to build structural elements.
30 # - The module \ref parts "salome.geom.structelem.parts" defines
31 # the classes corresponding to the different parts (beams, grids, etc.) that make up
32 # a structural element. It is used to build the geometric shapes in the structural element.
33 # - The module \ref orientation "salome.geom.structelem.orientation" defines
34 # the classes that are used to compute the orientation of the structural element parts
35 # and to build the corresponding markers.
37 # A structural element is a set of geometric shapes (beams, grids, etc.) that
38 # are built semi-automatically along a set of geometric primitives (edges for
39 # instance). They are visualized with the same color as their base primitives in
41 # \n Structural elements are generally created by the StructuralElementManager class,
42 # from a list of commands describing the element to create.
46 # commandList = [('VisuPoutreGenerale', {'Group_Maille': 'Edge_1'}),
47 # ('VisuBarreCercle', {'R': 30, 'Group_Maille': 'Edge_1', 'EP': 15}),
50 # structElemManager = StructuralElementManager()
51 # elem = structElemManager.createElement(commandList)
53 # salome.sg.updateObjBrowser(True)
56 # \defgroup orientation
61 This package is used to create and visualize structural elements. It contains
64 * This module :mod:`salome.geom.structelem` defines the main classes
65 :class:`StructuralElement` and :class:`StructuralElementManager` that can be
66 directly used to build structural elements.
67 * The module :mod:`salome.geom.structelem.parts` defines the classes corresponding to
68 the different parts (beams, grids, etc.) that make up a structural element.
69 It is used to build the geometric shapes in the structural element.
70 * The module :mod:`salome.geom.structelem.orientation` defines the classes that are
71 used to compute the orientation of the structural element parts and to build
72 the corresponding markers.
74 A structural element is a set of geometric shapes (beams, grids, etc.) that
75 are built semi-automatically along a set of geometric primitives (edges for
76 instance). They are visualized with the same color as their base primitives in
79 Structural elements are generally created by the
80 :class:`StructuralElementManager` class, from a list of commands describing
81 the element to create.
85 commandList = [('VisuPoutreGenerale', {'Group_Maille': 'Edge_1'}),
87 {'R': 30, 'Group_Maille': 'Edge_1', 'EP': 15}),
90 structElemManager = StructuralElementManager()
91 elem = structElemManager.createElement(commandList)
93 salome.sg.updateObjBrowser(True)
101 from salome.kernel.logger import Logger
102 from salome.kernel import termcolor
103 logger = Logger("salome.geom.structelem", color = termcolor.RED)
104 from salome.kernel.studyedit import getStudyEditor
106 __all__ = ["parts", "orientation"]
108 from salome.geom.geomtools import getGeompy
109 from salome.geom.structelem import parts
110 from salome.geom.structelem.parts import InvalidParameterError
112 ## This class manages the structural elements in the study. It is used to
113 # create a new structural element from a list of commands. The parameter
114 # \em studyId defines the ID of the study in which the manager will create
115 # structural elements. If it is \b None or not specified, it will use
116 # the ID of the current study as defined by
117 # \b salome.kernel.studyedit.getActiveStudyId() function.
118 # \ingroup structelem
119 class StructuralElementManager:
121 This class manages the structural elements in the study. It is used to
122 create a new structural element from a list of commands. The parameter
123 `studyId` defines the ID of the study in which the manager will create
124 structural elements. If it is :const:`None` or not specified, it will use
125 the ID of the current study as defined by
126 :func:`salome.kernel.studyedit.getActiveStudyId` function.
128 def __init__(self, studyId = None):
129 self._studyEditor = getStudyEditor(studyId)
131 ## Create a structural element from the list of commands \em commandList.
132 # Each command in this list represent a part of the structural element,
133 # that is a specific kind of shape (circular beam, grid, etc.)
134 # associated with one or several geometrical primitives. A command must
135 # be a tuple. The first element is the structural element part class
136 # name or alias name. The second element is a dictionary containing the
137 # parameters describing the part. Valid class names are all the classes
138 # defined in the module salome.geom.structelem.parts and inheriting
139 # parts.StructuralElementPart. There are also several
140 # aliases for backward compatibility. Here is the complete list:
141 # - parts.GeneralBeam
142 # - parts.CircularBeam
143 # - parts.RectangularBeam
146 # - parts.VisuPoutreGenerale() (alias for parts.GeneralBeam)
147 # - parts.VisuPoutreCercle() (alias for parts.CircularBeam)
148 # - parts.VisuPoutreRectangle() (alias for parts.RectangularBeam)
149 # - parts.VisuBarreGenerale() (alias for parts.GeneralBeam)
150 # - parts.VisuBarreRectangle() (alias for parts.RectangularBeam)
151 # - parts.VisuBarreCercle() (alias for parts.CircularBeam)
152 # - parts.VisuCable() (alias for parts.CircularBeam)
153 # - parts.VisuCoque() (alias for parts.ThickShell)
154 # - parts.VisuGrille() (alias for parts.Grid)
155 # - \b Orientation: This identifier is used to specify the orientation
156 # of one or several 1D structural element parts (i.e. beams). The
157 # parameters are described in class orientation.Orientation1D.
159 # The valid parameters in the dictionary depend on the type of the
160 # structural element part, and are detailed in the documentation of
161 # the corresponding class. The only parameter that is common to all the
162 # classes is "MeshGroups" (that can also be named "Group_Maille"). It
163 # defines the name of the geometrical object(s) in the study that will
164 # be used as primitives to build the structural element part. This
165 # parameter can be either a list of strings or a single string with
166 # comma separated names.
167 def createElement(self, commandList):
169 Create a structural element from the list of commands `commandList`.
170 Each command in this list represent a part of the structural element,
171 that is a specific kind of shape (circular beam, grid, etc.)
172 associated with one or several geometrical primitives. A command must
173 be a tuple. The first element is the structural element part class
174 name or alias name. The second element is a dictionary containing the
175 parameters describing the part. Valid class names are all the classes
176 defined in the module :mod:`~salome.geom.structelem.parts` and inheriting
177 class :class:`~parts.StructuralElementPart`. There are also several
178 aliases for backward compatibility. Here is the complete list:
180 * :class:`~parts.GeneralBeam`
181 * :class:`~parts.CircularBeam`
182 * :class:`~parts.RectangularBeam`
183 * :class:`~parts.ThickShell`
184 * :class:`~parts.Grid`
186 * :func:`~parts.VisuPoutreGenerale` (alias for
187 :class:`~parts.GeneralBeam`)
188 * :func:`~parts.VisuPoutreCercle` (alias for
189 :class:`~parts.CircularBeam`)
190 * :func:`~parts.VisuPoutreRectangle` (alias for
191 :class:`~parts.RectangularBeam`)
192 * :func:`~parts.VisuBarreGenerale` (alias for
193 :class:`~parts.GeneralBeam`)
194 * :func:`~parts.VisuBarreRectangle` (alias for
195 :class:`~parts.RectangularBeam`)
196 * :func:`~parts.VisuBarreCercle` (alias for
197 :class:`~parts.CircularBeam`)
198 * :func:`~parts.VisuCable` (alias for :class:`~parts.CircularBeam`)
199 * :func:`~parts.VisuCoque` (alias for :class:`~parts.ThickShell`)
200 * :func:`~parts.VisuGrille` (alias for :class:`~parts.Grid`)
202 * ``Orientation``: This identifier is used to specify the orientation
203 of one or several 1D structural element parts (i.e. beams). The
204 parameters are described in class
205 :class:`~orientation.Orientation1D`.
207 The valid parameters in the dictionary depend on the type of the
208 structural element part, and are detailed in the documentation of
209 the corresponding class. The only parameter that is common to all the
210 classes is "MeshGroups" (that can also be named "Group_Maille"). It
211 defines the name of the geometrical object(s) in the study that will
212 be used as primitives to build the structural element part. This
213 parameter can be either a list of strings or a single string with
214 comma separated names.
216 logger.debug("StructuralElementManager.createElement: START")
217 logger.debug("Command list: %s" % commandList)
219 element = StructuralElement(self._studyEditor.studyId)
220 orientationCmdList = []
221 for command in commandList:
222 (parttype, parameters) = command
223 if parttype == "Orientation":
224 orientationCmdList += [command]
225 elif parttype not in dir(parts):
226 logger.warning('Invalid structural element part name "%s"'
227 ' in command %s, this command will be '
228 'ignored.' % (parttype, command))
230 (meshGroupList, newparams) = self._extractMeshGroups(command)
231 for meshGroup in meshGroupList:
232 # Get the geometrical primitive object
233 if meshGroup.startswith('/'):
234 groupSObj = self._studyEditor.study.FindObjectByPath(meshGroup)
235 meshGroup = meshGroup.split('/')[-1]
238 groupSObj = self._studyEditor.study.FindObject(meshGroup)
241 if groupSObj is not None:
243 self._studyEditor.getOrLoadObject(groupSObj)
244 if groupGeomObj is None:
245 logger.error("Can't get geom object corresponding to "
246 'mesh group "%s", structural element '
247 "part %s will not be built." %
248 (meshGroup, command))
253 part = parts.__dict__[parttype](
254 self._studyEditor.studyId, meshGroup,
255 groupGeomObj, newparams)
256 element.addPart(part)
257 except InvalidParameterError, e:
258 logger.error("Invalid parameter error: %s" % e)
261 logger.exception("Can't create structural element"
262 " part with command %s." %
265 # Orientations are parsed after the parts because they must be
266 # associated with existing parts.
267 for command in orientationCmdList:
268 (parttype, parameters) = command
269 (meshGroupList, orientParams) = self._extractMeshGroups(command)
270 for meshGroup in meshGroupList:
271 element.addOrientation(meshGroup, orientParams)
274 logger.debug("StructuralElementManager.createElement: END")
277 ## This method extracts the names of the mesh groups (i.e. the
278 # geometrical objects used to build the structural element part) in the
279 # command in parameter. It returns a tuple containing the mesh groups as
280 # a list of strings and the other parameters of the command as a new
282 def _extractMeshGroups(self, command):
284 This method extracts the names of the mesh groups (i.e. the
285 geometrical objects used to build the structural element part) in the
286 command in parameter. It returns a tuple containing the mesh groups as
287 a list of strings and the other parameters of the command as a new
290 (parttype, parameters) = command
291 newparams = parameters.copy()
292 groupMailleParam = newparams.pop("Group_Maille", None)
293 meshGroupParam = newparams.pop("MeshGroups", None)
294 if groupMailleParam is None and meshGroupParam is None:
295 logger.warning("No mesh group specified in command %s, this "
296 "command will be ignored." % command)
297 return ([], newparams)
298 elif groupMailleParam is not None and meshGroupParam is not None:
299 logger.warning('Both "MeshGroups" and "Group_Maille" specified in'
300 ' command %s, only "MeshGroups" will be used.' %
302 elif groupMailleParam is not None and meshGroupParam is None:
303 meshGroupParam = groupMailleParam
305 if isinstance(meshGroupParam, types.StringTypes):
306 meshGroupList = [meshGroupParam]
308 meshGroupList = meshGroupParam
310 if len(meshGroupList) == 0:
311 logger.warning("Mesh group list is empty in command %s, this "
312 "command will be ignored." % command)
314 return (meshGroupList, newparams)
317 ## This class represents a structural element, i.e. a set of geometrical
318 # objects built along geometrical primitives. The parameter \em studyId
319 # defines the ID of the study that will contain the structural element. If
320 # it is \b None or not specified, the constructor will use the ID of
321 # the active study as defined by \b salome.kernel.studyedit.getActiveStudyId
322 # function. Structural elements are normally created by the class
323 # StructuralElementManager, so this class should not be
324 # instantiated directly in the general case.
325 # \ingroup structelem
326 class StructuralElement:
328 This class represents a structural element, i.e. a set of geometrical
329 objects built along geometrical primitives. The parameter `studyId`
330 defines the ID of the study that will contain the structural element. If
331 it is :const:`None` or not specified, the constructor will use the ID of
332 the active study as defined by :func:`salome.kernel.studyedit.getActiveStudyId`
333 function. Structural elements are normally created by the class
334 :class:`StructuralElementManager`, so this class should not be
335 instantiated directly in the general case.
339 MAIN_FOLDER_NAME = "Structural Elements"
341 def __init__(self, studyId = None):
342 # _parts is the dictionary mapping group name to structural element
343 # part. _shapeDict is the dictionary mapping SubShapeID objects to
344 # structural element parts. Both are used to avoid duplicate shapes
345 # in structural elements.
348 self._id = StructuralElement._counter
349 StructuralElement._counter += 1
350 self._studyEditor = getStudyEditor(studyId)
351 logger.debug("Creating structural element in study %s" %
352 self._studyEditor.studyId)
355 ## Find or create the study object corresponding to the structural
356 # element. This object is a Geom Folder named "SE_N" where N is a
358 def _getSObject(self):
360 Find or create the study object corresponding to the structural
361 element. This object is a Geom Folder named "SE_N" where N is a
364 if self._SObject is None:
365 geompy = getGeompy(self._studyEditor.studyId)
366 geomComponent = self._studyEditor.study.FindComponent("GEOM")
367 mainFolder = self._studyEditor.findItem(geomComponent,
368 name = StructuralElement.MAIN_FOLDER_NAME,
370 if mainFolder is None:
371 mainFolder = geompy.NewFolder(StructuralElement.MAIN_FOLDER_NAME)
372 self._SObject = geompy.NewFolder("SE_" + str(self._id), mainFolder)
375 ## Add a part to the structural element.
377 # \param newpart (StructuralElementPart) the part to add to the structural element.
378 def addPart(self, newpart):
380 Add a part to the structural element.
382 :type newpart: :class:`~parts.StructuralElementPart`
383 :param newpart: the part to add to the structural element.
386 newshapes = newpart.baseShapesSet
388 # Check duplicate groups
389 if self._parts.has_key(newpart.groupName):
390 logger.warning('Mesh group "%s" is used several times in the '
391 'structural element. Only the last definition '
392 'will be used.' % newpart.groupName)
394 # Check duplicate shapes
395 intersect = newshapes.intersection(self._shapeDict.keys())
396 while len(intersect) > 0:
398 oldpartwithshape = self._shapeDict[shape]
399 oldpartshapes = oldpartwithshape.baseShapesSet
400 intersectwitholdpart = intersect.intersection(oldpartshapes)
401 logger.warning('Some shapes are common to groups "%s" and '
402 '"%s". For those, the parameters defined for '
403 '"%s" will be used.' %
404 (oldpartwithshape.groupName, newpart.groupName,
406 oldpartwithshape.baseShapesSet = \
407 oldpartshapes.difference(intersectwitholdpart)
408 intersect = intersect.difference(intersectwitholdpart)
410 # Finally add the new part in the structural element
411 self._parts[newpart.groupName] = newpart
412 for shape in newshapes:
413 self._shapeDict[shape] = newpart
415 ## Add orientation information to a part in the structural element. This
416 # information will be used to build the corresponding markers.
418 # \param meshGroup (string) the name of a geometrical primitive. The orientation
419 # information will apply to the structural element part built along this primitive.
420 # \param orientParams (dictionary) parameters defining the orientation of the
421 # structural element part. Those parameters are detailed in class orientation.Orientation1D.
422 def addOrientation(self, meshGroup, orientParams):
424 Add orientation information to a part in the structural element. This
425 information will be used to build the corresponding markers.
427 :type meshGroup: string
428 :param meshGroup: the name of a geometrical primitive. The orientation
429 information will apply to the structural element
430 part built along this primitive.
432 :type orientParams: dictionary
433 :param orientParams: parameters defining the orientation of the
434 structural element part. Those parameters are
436 :class:`~orientation.Orientation1D`.
439 if self._parts.has_key(meshGroup):
440 self._parts[meshGroup].addOrientation(orientParams)
442 logger.warning('Mesh group "%s" not found in structural element, '
443 'cannot set orientation.' % meshGroup)
445 ## Build the geometric shapes and the markers corresponding to the
446 # different parts of the structural element, and add them to the study.
449 Build the geometric shapes and the markers corresponding to the
450 different parts of the structural element, and add them to the study.
452 gg = salome.ImportComponentGUI("GEOM")
453 geompy = getGeompy(self._studyEditor.studyId)
454 for part in self._parts.itervalues():
455 # Build the structural element part
456 logger.debug("Building %s" % part)
458 (shape, markers) = part.build()
460 logger.error("Part %s has not been built" % part)
463 logger.exception("Couldn't build part %s" % part)
466 # Add the new objects to the study
467 shapeSObjName = part.name + "_" + part.groupName
468 geompy.addToStudy(shape, shapeSObjName)
469 geompy.PutToFolder(shape, self._getSObject())
471 if markers is not None and len(markers) > 0:
472 for i, marker in enumerate(markers, start = 1):
473 markerSObjName = "Orient_" + shapeSObjName
475 markerSObjName += "_%d" % i
476 geompy.addToStudy(marker, markerSObjName)
477 geompy.PutToFolder(marker, self._getSObject())
479 ## Display the structural element in the geom view.
482 Display the structural element in the geom view.
484 StructuralElement.showElement(self._SObject)
487 ## Display the structural element corresponding to the study object \b theSObject
488 def showElement(theSObject):
490 Display the structural element corresponding to the study object
493 if theSObject is not None:
494 gg = salome.ImportComponentGUI("GEOM")
495 aStudy = theSObject.GetStudy()
496 useCaseBuilder = aStudy.GetUseCaseBuilder()
497 editor = getStudyEditor(aStudy._get_StudyId())
498 aIterator = useCaseBuilder.GetUseCaseIterator(theSObject)
499 aIterator.Init(False)
500 while aIterator.More():
501 sobj = aIterator.Value()
502 icon = editor.getIcon(sobj)
503 if icon != "ICON_OBJBROWSER_LCS":
504 entry = aIterator.Value().GetID()
505 gg.createAndDisplayGO(entry)
506 gg.setDisplayMode(entry, 2) # Shading + edges
510 def TEST_CreateGeometry():
514 from salome.geom import geomBuilder
515 geompy = geomBuilder.New(salome.myStudy)
517 geompy.init_geom(salome.myStudy)
518 Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200)
519 edges = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["EDGE"])
520 geompy.addToStudy(Box_1, "Box_1")
521 for i in range(len(edges)):
522 geompy.addToStudyInFather(Box_1, edges[i], "Edge_%d" % i)
523 faces = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["FACE"])
524 faces[3].SetColor(SALOMEDS.Color(1.0,0.5,0.0))
525 faces[4].SetColor(SALOMEDS.Color(0.0,1.0,0.5))
526 for i in range(len(faces)):
527 geompy.addToStudyInFather(Box_1, faces[i], "Face_%d" % i)
528 Cylinder_1 = geompy.MakeCylinderRH(50, 200)
529 geompy.TranslateDXDYDZ(Cylinder_1, 300, 300, 0)
530 cyl_faces = geompy.SubShapeAllSorted(Cylinder_1, geompy.ShapeType["FACE"])
531 geompy.addToStudy(Cylinder_1, "Cylinder_1")
532 for i in range(len(cyl_faces)):
533 geompy.addToStudyInFather(Cylinder_1, cyl_faces[i], "CylFace_%d" % i)
534 Cylinder_2 = geompy.MakeTranslation(Cylinder_1, 100, 100, 0)
535 cyl_faces2 = geompy.SubShapeAllSorted(Cylinder_2,
536 geompy.ShapeType["FACE"])
537 geompy.addToStudy(Cylinder_2, "Cylinder_2")
538 for i in range(len(cyl_faces2)):
539 geompy.addToStudyInFather(Cylinder_2, cyl_faces2[i],
543 def TEST_StructuralElement():
545 TEST_CreateGeometry()
546 liste_commandes = [('Orientation', {'MeshGroups': 'Edge_4',
547 'VECT_Y': (1.0, 0.0, 1.0)}),
548 ('Orientation', {'MeshGroups': 'Edge_5',
550 ('GeneralBeam', {'MeshGroups': ['Edge_1', 'Edge_7'],
551 'A': 1, 'IY1': 20, 'IY2': 40,
552 'IZ1': 60, 'IZ2': 30}),
553 ('VisuPoutreCercle', {'MeshGroups': ['Edge_6'],
554 'R1': 30, 'R2': 20}),
555 ('CircularBeam', {'MeshGroups': ['Edge_2', 'Edge_3'],
557 ('RectangularBeam', {'MeshGroups': ['Edge_4', 'Edge_5'],
558 'HZ1': 60, 'HY1': 40,
559 'EPZ1': 15, 'EPY1': 10,
560 'HZ2': 40, 'HY2': 60,
561 'EPZ2': 10, 'EPY2': 15}),
562 ('VisuCable', {'MeshGroups': 'Edge_7', 'R': 5}),
563 ('VisuCoque', {'MeshGroups': 'Face_4',
564 'Epais': 10, 'Excentre': 5,
565 'angleAlpha': 45, 'angleBeta': 60}),
566 ('VisuCoque', {'MeshGroups': 'CylFace_2', 'Epais': 5}),
567 ('VisuGrille', {'MeshGroups': 'Face_5', 'Excentre': 5,
568 'angleAlpha': 45, 'angleBeta': 60}),
569 ('VisuGrille', {'MeshGroups': 'CylFace2_2',
570 'Excentre': 5, 'origAxeX': 400,
571 'origAxeY': 400, 'origAxeZ': 0,
572 'axeX': 0, 'axeY': 0, 'axeZ': 100}),
575 structElemManager = StructuralElementManager()
576 elem = structElemManager.createElement(liste_commandes)
577 if salome.hasDesktop():
579 salome.sg.updateObjBrowser(True)
582 # Main function only used to test the module
583 if __name__ == "__main__":
584 TEST_StructuralElement()