1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2007-2016 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
114 ## This class manages the structural elements in the study. It is used to
115 # create a new structural element from a list of commands. The parameter
116 # \em studyId defines the ID of the study in which the manager will create
117 # structural elements. If it is \b None or not specified, it will use
118 # the ID of the current study as defined by
119 # \b salome.kernel.studyedit.getActiveStudyId() function.
120 # \ingroup structelem
121 class StructuralElementManager:
123 This class manages the structural elements in the study. It is used to
124 create a new structural element from a list of commands. The parameter
125 `studyId` defines the ID of the study in which the manager will create
126 structural elements. If it is :const:`None` or not specified, it will use
127 the ID of the current study as defined by
128 :func:`salome.kernel.studyedit.getActiveStudyId` function.
130 def __init__(self, studyId = None):
131 self._studyEditor = getStudyEditor(studyId)
133 ## Create a structural element from the list of commands \em commandList.
134 # Each command in this list represent a part of the structural element,
135 # that is a specific kind of shape (circular beam, grid, etc.)
136 # associated with one or several geometrical primitives. A command must
137 # be a tuple. The first element is the structural element part class
138 # name or alias name. The second element is a dictionary containing the
139 # parameters describing the part. Valid class names are all the classes
140 # defined in the module salome.geom.structelem.parts and inheriting
141 # parts.StructuralElementPart. There are also several
142 # aliases for backward compatibility. Here is the complete list:
143 # - parts.GeneralBeam
144 # - parts.CircularBeam
145 # - parts.RectangularBeam
148 # - parts.VisuPoutreGenerale() (alias for parts.GeneralBeam)
149 # - parts.VisuPoutreCercle() (alias for parts.CircularBeam)
150 # - parts.VisuPoutreRectangle() (alias for parts.RectangularBeam)
151 # - parts.VisuBarreGenerale() (alias for parts.GeneralBeam)
152 # - parts.VisuBarreRectangle() (alias for parts.RectangularBeam)
153 # - parts.VisuBarreCercle() (alias for parts.CircularBeam)
154 # - parts.VisuCable() (alias for parts.CircularBeam)
155 # - parts.VisuCoque() (alias for parts.ThickShell)
156 # - parts.VisuGrille() (alias for parts.Grid)
157 # - \b Orientation: This identifier is used to specify the orientation
158 # of one or several 1D structural element parts (i.e. beams). The
159 # parameters are described in class orientation.Orientation1D.
161 # The valid parameters in the dictionary depend on the type of the
162 # structural element part, and are detailed in the documentation of
163 # the corresponding class. The only parameter that is common to all the
164 # classes is "MeshGroups" (that can also be named "Group_Maille"). It
165 # defines the name of the geometrical object(s) in the study that will
166 # be used as primitives to build the structural element part. This
167 # parameter can be either a list of strings or a single string with
168 # comma separated names.
169 def createElement(self, commandList):
171 Create a structural element from the list of commands `commandList`.
172 Each command in this list represent a part of the structural element,
173 that is a specific kind of shape (circular beam, grid, etc.)
174 associated with one or several geometrical primitives. A command must
175 be a tuple. The first element is the structural element part class
176 name or alias name. The second element is a dictionary containing the
177 parameters describing the part. Valid class names are all the classes
178 defined in the module :mod:`~salome.geom.structelem.parts` and inheriting
179 class :class:`~parts.StructuralElementPart`. There are also several
180 aliases for backward compatibility. Here is the complete list:
182 * :class:`~parts.GeneralBeam`
183 * :class:`~parts.CircularBeam`
184 * :class:`~parts.RectangularBeam`
185 * :class:`~parts.ThickShell`
186 * :class:`~parts.Grid`
188 * :func:`~parts.VisuPoutreGenerale` (alias for
189 :class:`~parts.GeneralBeam`)
190 * :func:`~parts.VisuPoutreCercle` (alias for
191 :class:`~parts.CircularBeam`)
192 * :func:`~parts.VisuPoutreRectangle` (alias for
193 :class:`~parts.RectangularBeam`)
194 * :func:`~parts.VisuBarreGenerale` (alias for
195 :class:`~parts.GeneralBeam`)
196 * :func:`~parts.VisuBarreRectangle` (alias for
197 :class:`~parts.RectangularBeam`)
198 * :func:`~parts.VisuBarreCercle` (alias for
199 :class:`~parts.CircularBeam`)
200 * :func:`~parts.VisuCable` (alias for :class:`~parts.CircularBeam`)
201 * :func:`~parts.VisuCoque` (alias for :class:`~parts.ThickShell`)
202 * :func:`~parts.VisuGrille` (alias for :class:`~parts.Grid`)
204 * ``Orientation``: This identifier is used to specify the orientation
205 of one or several 1D structural element parts (i.e. beams). The
206 parameters are described in class
207 :class:`~orientation.Orientation1D`.
209 The valid parameters in the dictionary depend on the type of the
210 structural element part, and are detailed in the documentation of
211 the corresponding class. The only parameter that is common to all the
212 classes is "MeshGroups" (that can also be named "Group_Maille"). It
213 defines the name of the geometrical object(s) in the study that will
214 be used as primitives to build the structural element part. This
215 parameter can be either a list of strings or a single string with
216 comma separated names.
218 logger.debug("StructuralElementManager.createElement: START")
219 logger.debug("Command list: %s" % commandList)
221 element = StructuralElement(self._studyEditor.studyId)
222 orientationCmdList = []
223 for command in commandList:
224 (parttype, parameters) = command
225 if parttype == "Orientation":
226 orientationCmdList += [command]
227 elif parttype not in dir(parts):
228 logger.warning('Invalid structural element part name "%s"'
229 ' in command %s, this command will be '
230 'ignored.' % (parttype, command))
232 (meshGroupList, newparams) = self._extractMeshGroups(command)
233 for meshGroup in meshGroupList:
234 # Get the geometrical primitive object
235 if meshGroup.startswith('/'):
236 groupSObj = self._studyEditor.study.FindObjectByPath(meshGroup)
237 meshGroup = meshGroup.split('/')[-1]
240 groupSObj = self._studyEditor.study.FindObject(meshGroup)
243 if groupSObj is not None:
245 self._studyEditor.getOrLoadObject(groupSObj)
246 if groupGeomObj is None:
247 logger.error("Can't get geom object corresponding to "
248 'mesh group "%s", structural element '
249 "part %s will not be built." %
250 (meshGroup, command))
255 part = parts.__dict__[parttype](
256 self._studyEditor.studyId, meshGroup,
257 groupGeomObj, newparams)
258 element.addPart(part)
259 except InvalidParameterError, e:
260 logger.error("Invalid parameter error: %s" % e)
263 logger.exception("Can't create structural element"
264 " part with command %s." %
267 # Orientations are parsed after the parts because they must be
268 # associated with existing parts.
269 for command in orientationCmdList:
270 (parttype, parameters) = command
271 (meshGroupList, orientParams) = self._extractMeshGroups(command)
272 for meshGroup in meshGroupList:
273 element.addOrientation(meshGroup, orientParams)
276 logger.debug("StructuralElementManager.createElement: END")
279 ## This method extracts the names of the mesh groups (i.e. the
280 # geometrical objects used to build the structural element part) in the
281 # command in parameter. It returns a tuple containing the mesh groups as
282 # a list of strings and the other parameters of the command as a new
284 def _extractMeshGroups(self, command):
286 This method extracts the names of the mesh groups (i.e. the
287 geometrical objects used to build the structural element part) in the
288 command in parameter. It returns a tuple containing the mesh groups as
289 a list of strings and the other parameters of the command as a new
292 (parttype, parameters) = command
293 newparams = parameters.copy()
294 groupMailleParam = newparams.pop("Group_Maille", None)
295 meshGroupParam = newparams.pop("MeshGroups", None)
296 if groupMailleParam is None and meshGroupParam is None:
297 logger.warning("No mesh group specified in command %s, this "
298 "command will be ignored." % command)
299 return ([], newparams)
300 elif groupMailleParam is not None and meshGroupParam is not None:
301 logger.warning('Both "MeshGroups" and "Group_Maille" specified in'
302 ' command %s, only "MeshGroups" will be used.' %
304 elif groupMailleParam is not None and meshGroupParam is None:
305 meshGroupParam = groupMailleParam
307 if isinstance(meshGroupParam, types.StringTypes):
308 meshGroupList = [meshGroupParam]
310 meshGroupList = meshGroupParam
312 if len(meshGroupList) == 0:
313 logger.warning("Mesh group list is empty in command %s, this "
314 "command will be ignored." % command)
316 return (meshGroupList, newparams)
319 ## This class represents a structural element, i.e. a set of geometrical
320 # objects built along geometrical primitives. The parameter \em studyId
321 # defines the ID of the study that will contain the structural element. If
322 # it is \b None or not specified, the constructor will use the ID of
323 # the active study as defined by \b salome.kernel.studyedit.getActiveStudyId
324 # function. Structural elements are normally created by the class
325 # StructuralElementManager, so this class should not be
326 # instantiated directly in the general case.
327 # \ingroup structelem
328 class StructuralElement:
330 This class represents a structural element, i.e. a set of geometrical
331 objects built along geometrical primitives. The parameter `studyId`
332 defines the ID of the study that will contain the structural element. If
333 it is :const:`None` or not specified, the constructor will use the ID of
334 the active study as defined by :func:`salome.kernel.studyedit.getActiveStudyId`
335 function. Structural elements are normally created by the class
336 :class:`StructuralElementManager`, so this class should not be
337 instantiated directly in the general case.
341 MAIN_FOLDER_NAME = "Structural Elements"
343 def __init__(self, studyId = None):
344 # _parts is the dictionary mapping group name to structural element
345 # part. _shapeDict is the dictionary mapping SubShapeID objects to
346 # structural element parts. Both are used to avoid duplicate shapes
347 # in structural elements.
350 self._id = StructuralElement._counter
351 StructuralElement._counter += 1
352 self._studyEditor = getStudyEditor(studyId)
353 logger.debug("Creating structural element in study %s" %
354 self._studyEditor.studyId)
357 ## Find or create the study object corresponding to the structural
358 # element. This object is a Geom Folder named "SE_N" where N is a
360 def _getSObject(self):
362 Find or create the study object corresponding to the structural
363 element. This object is a Geom Folder named "SE_N" where N is a
366 if self._SObject is None:
367 geompy = getGeompy(self._studyEditor.studyId)
368 geomComponent = self._studyEditor.study.FindComponent("GEOM")
369 mainFolder = self._studyEditor.findItem(geomComponent,
370 name = StructuralElement.MAIN_FOLDER_NAME,
372 if mainFolder is None:
373 mainFolder = geompy.NewFolder(StructuralElement.MAIN_FOLDER_NAME)
374 self._SObject = geompy.NewFolder("SE_" + str(self._id), mainFolder)
377 ## Add a part to the structural element.
379 # \param newpart (StructuralElementPart) the part to add to the structural element.
380 def addPart(self, newpart):
382 Add a part to the structural element.
384 :type newpart: :class:`~parts.StructuralElementPart`
385 :param newpart: the part to add to the structural element.
388 newshapes = newpart.baseShapesSet
390 # Check duplicate groups
391 if self._parts.has_key(newpart.groupName):
392 logger.warning('Mesh group "%s" is used several times in the '
393 'structural element. Only the last definition '
394 'will be used.' % newpart.groupName)
396 # Check duplicate shapes
397 intersect = newshapes.intersection(self._shapeDict.keys())
398 while len(intersect) > 0:
400 oldpartwithshape = self._shapeDict[shape]
401 oldpartshapes = oldpartwithshape.baseShapesSet
402 intersectwitholdpart = intersect.intersection(oldpartshapes)
403 logger.warning('Some shapes are common to groups "%s" and '
404 '"%s". For those, the parameters defined for '
405 '"%s" will be used.' %
406 (oldpartwithshape.groupName, newpart.groupName,
408 oldpartwithshape.baseShapesSet = \
409 oldpartshapes.difference(intersectwitholdpart)
410 intersect = intersect.difference(intersectwitholdpart)
412 # Finally add the new part in the structural element
413 self._parts[newpart.groupName] = newpart
414 for shape in newshapes:
415 self._shapeDict[shape] = newpart
417 ## Add orientation information to a part in the structural element. This
418 # information will be used to build the corresponding markers.
420 # \param meshGroup (string) the name of a geometrical primitive. The orientation
421 # information will apply to the structural element part built along this primitive.
422 # \param orientParams (dictionary) parameters defining the orientation of the
423 # structural element part. Those parameters are detailed in class orientation.Orientation1D.
424 def addOrientation(self, meshGroup, orientParams):
426 Add orientation information to a part in the structural element. This
427 information will be used to build the corresponding markers.
429 :type meshGroup: string
430 :param meshGroup: the name of a geometrical primitive. The orientation
431 information will apply to the structural element
432 part built along this primitive.
434 :type orientParams: dictionary
435 :param orientParams: parameters defining the orientation of the
436 structural element part. Those parameters are
438 :class:`~orientation.Orientation1D`.
441 if self._parts.has_key(meshGroup):
442 self._parts[meshGroup].addOrientation(orientParams)
444 logger.warning('Mesh group "%s" not found in structural element, '
445 'cannot set orientation.' % meshGroup)
447 ## Build the geometric shapes and the markers corresponding to the
448 # different parts of the structural element, and add them to the study.
451 Build the geometric shapes and the markers corresponding to the
452 different parts of the structural element, and add them to the study.
454 gg = salome.ImportComponentGUI("GEOM")
455 geompy = getGeompy(self._studyEditor.studyId)
456 for part in self._parts.itervalues():
457 # Build the structural element part
458 logger.debug("Building %s" % part)
460 (shape, markers) = part.build()
462 logger.error("Part %s has not been built" % part)
465 logger.exception("Couldn't build part %s" % part)
468 # Add the new objects to the study
469 shapeSObjName = part.name + "_" + part.groupName
470 geompy.addToStudy(shape, shapeSObjName)
471 geompy.PutToFolder(shape, self._getSObject())
473 if markers is not None and len(markers) > 0:
474 for i, marker in enumerate(markers, start = 1):
475 markerSObjName = "Orient_" + shapeSObjName
477 markerSObjName += "_%d" % i
478 geompy.addToStudy(marker, markerSObjName)
479 geompy.PutToFolder(marker, self._getSObject())
481 ## Display the structural element in the geom view.
484 Display the structural element in the geom view.
486 StructuralElement.showElement(self._SObject)
489 ## Display the structural element corresponding to the study object \b theSObject
490 def showElement(theSObject):
492 Display the structural element corresponding to the study object
495 if theSObject is not None:
496 gg = salome.ImportComponentGUI("GEOM")
497 aStudy = theSObject.GetStudy()
498 useCaseBuilder = aStudy.GetUseCaseBuilder()
499 editor = getStudyEditor(aStudy._get_StudyId())
500 aIterator = useCaseBuilder.GetUseCaseIterator(theSObject)
501 aIterator.Init(False)
502 while aIterator.More():
503 sobj = aIterator.Value()
504 icon = editor.getIcon(sobj)
505 if icon != "ICON_OBJBROWSER_LCS":
506 entry = aIterator.Value().GetID()
507 gg.createAndDisplayGO(entry)
508 gg.setDisplayMode(entry, 2) # Shading + edges
512 def TEST_CreateGeometry():
516 from salome.geom import geomBuilder
517 geompy = geomBuilder.New(salome.myStudy)
519 geompy.init_geom(salome.myStudy)
520 Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200)
521 edges = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["EDGE"])
522 geompy.addToStudy(Box_1, "Box_1")
523 for i in range(len(edges)):
524 geompy.addToStudyInFather(Box_1, edges[i], "Edge_%d" % i)
525 faces = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["FACE"])
526 faces[3].SetColor(SALOMEDS.Color(1.0,0.5,0.0))
527 faces[4].SetColor(SALOMEDS.Color(0.0,1.0,0.5))
528 for i in range(len(faces)):
529 geompy.addToStudyInFather(Box_1, faces[i], "Face_%d" % i)
530 Cylinder_1 = geompy.MakeCylinderRH(50, 200)
531 geompy.TranslateDXDYDZ(Cylinder_1, 300, 300, 0)
532 cyl_faces = geompy.SubShapeAllSorted(Cylinder_1, geompy.ShapeType["FACE"])
533 geompy.addToStudy(Cylinder_1, "Cylinder_1")
534 for i in range(len(cyl_faces)):
535 geompy.addToStudyInFather(Cylinder_1, cyl_faces[i], "CylFace_%d" % i)
536 Cylinder_2 = geompy.MakeTranslation(Cylinder_1, 100, 100, 0)
537 cyl_faces2 = geompy.SubShapeAllSorted(Cylinder_2,
538 geompy.ShapeType["FACE"])
539 geompy.addToStudy(Cylinder_2, "Cylinder_2")
540 for i in range(len(cyl_faces2)):
541 geompy.addToStudyInFather(Cylinder_2, cyl_faces2[i],
545 def TEST_StructuralElement():
547 TEST_CreateGeometry()
548 liste_commandes = [('Orientation', {'MeshGroups': 'Edge_4',
549 'VECT_Y': (1.0, 0.0, 1.0)}),
550 ('Orientation', {'MeshGroups': 'Edge_5',
552 ('GeneralBeam', {'MeshGroups': ['Edge_1', 'Edge_7'],
553 'A': 1, 'IY1': 20, 'IY2': 40,
554 'IZ1': 60, 'IZ2': 30}),
555 ('VisuPoutreCercle', {'MeshGroups': ['Edge_6'],
556 'R1': 30, 'R2': 20}),
557 ('CircularBeam', {'MeshGroups': ['Edge_2', 'Edge_3'],
559 ('RectangularBeam', {'MeshGroups': ['Edge_4', 'Edge_5'],
560 'HZ1': 60, 'HY1': 40,
561 'EPZ1': 15, 'EPY1': 10,
562 'HZ2': 40, 'HY2': 60,
563 'EPZ2': 10, 'EPY2': 15}),
564 ('VisuCable', {'MeshGroups': 'Edge_7', 'R': 5}),
565 ('VisuCoque', {'MeshGroups': 'Face_4',
566 'Epais': 10, 'Excentre': 5,
567 'angleAlpha': 45, 'angleBeta': 60}),
568 ('VisuCoque', {'MeshGroups': 'CylFace_2', 'Epais': 5}),
569 ('VisuGrille', {'MeshGroups': 'Face_5', 'Excentre': 5,
570 'angleAlpha': 45, 'angleBeta': 60}),
571 ('VisuGrille', {'MeshGroups': 'CylFace2_2',
572 'Excentre': 5, 'origAxeX': 400,
573 'origAxeY': 400, 'origAxeZ': 0,
574 'axeX': 0, 'axeY': 0, 'axeZ': 100}),
577 structElemManager = StructuralElementManager()
578 elem = structElemManager.createElement(liste_commandes)
579 if salome.hasDesktop():
581 salome.sg.updateObjBrowser(True)
584 # Main function only used to test the module
585 if __name__ == "__main__":
586 TEST_StructuralElement()