1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2007-2014 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 This package is used to create and visualize structural elements. It contains
25 * This module :mod:`salome.geom.structelem` defines the main classes
26 :class:`StructuralElement` and :class:`StructuralElementManager` that can be
27 directly used to build structural elements.
28 * The module :mod:`salome.geom.structelem.parts` defines the classes corresponding to
29 the different parts (beams, grids, etc.) that make up a structural element.
30 It is used to build the geometric shapes in the structural element.
31 * The module :mod:`salome.geom.structelem.orientation` defines the classes that are
32 used to compute the orientation of the structural element parts and to build
33 the corresponding markers.
35 A structural element is a set of geometric shapes (beams, grids, etc.) that
36 are built semi-automatically along a set of geometric primitives (edges for
37 instance). They are visualized with the same color as their base primitives in
40 Structural elements are generally created by the
41 :class:`StructuralElementManager` class, from a list of commands describing
42 the element to create.
46 commandList = [('VisuPoutreGenerale', {'Group_Maille': 'Edge_1'}),
48 {'R': 30, 'Group_Maille': 'Edge_1', 'EP': 15}),
51 structElemManager = StructuralElementManager()
52 elem = structElemManager.createElement(commandList)
54 salome.sg.updateObjBrowser(True)
62 from salome.kernel.logger import Logger
63 from salome.kernel import termcolor
64 logger = Logger("salome.geom.structelem", color = termcolor.RED)
65 from salome.kernel.studyedit import getStudyEditor
67 __all__ = ["parts", "orientation"]
69 from salome.geom.geomtools import getGeompy
70 from salome.geom.structelem import parts
71 from salome.geom.structelem.parts import InvalidParameterError
73 class StructuralElementManager:
75 This class manages the structural elements in the study. It is used to
76 create a new structural element from a list of commands. The parameter
77 `studyId` defines the ID of the study in which the manager will create
78 structural elements. If it is :const:`None` or not specified, it will use
79 the ID of the current study as defined by
80 :func:`salome.kernel.studyedit.getActiveStudyId` function.
82 def __init__(self, studyId = None):
83 self._studyEditor = getStudyEditor(studyId)
85 def createElement(self, commandList):
87 Create a structural element from the list of commands `commandList`.
88 Each command in this list represent a part of the structural element,
89 that is a specific kind of shape (circular beam, grid, etc.)
90 associated with one or several geometrical primitives. A command must
91 be a tuple. The first element is the structural element part class
92 name or alias name. The second element is a dictionary containing the
93 parameters describing the part. Valid class names are all the classes
94 defined in the module :mod:`~salome.geom.structelem.parts` and inheriting
95 class :class:`~parts.StructuralElementPart`. There are also several
96 aliases for backward compatibility. Here is the complete list:
98 * :class:`~parts.GeneralBeam`
99 * :class:`~parts.CircularBeam`
100 * :class:`~parts.RectangularBeam`
101 * :class:`~parts.ThickShell`
102 * :class:`~parts.Grid`
104 * :func:`~parts.VisuPoutreGenerale` (alias for
105 :class:`~parts.GeneralBeam`)
106 * :func:`~parts.VisuPoutreCercle` (alias for
107 :class:`~parts.CircularBeam`)
108 * :func:`~parts.VisuPoutreRectangle` (alias for
109 :class:`~parts.RectangularBeam`)
110 * :func:`~parts.VisuBarreGenerale` (alias for
111 :class:`~parts.GeneralBeam`)
112 * :func:`~parts.VisuBarreRectangle` (alias for
113 :class:`~parts.RectangularBeam`)
114 * :func:`~parts.VisuBarreCercle` (alias for
115 :class:`~parts.CircularBeam`)
116 * :func:`~parts.VisuCable` (alias for :class:`~parts.CircularBeam`)
117 * :func:`~parts.VisuCoque` (alias for :class:`~parts.ThickShell`)
118 * :func:`~parts.VisuGrille` (alias for :class:`~parts.Grid`)
120 * ``Orientation``: This identifier is used to specify the orientation
121 of one or several 1D structural element parts (i.e. beams). The
122 parameters are described in class
123 :class:`~orientation.Orientation1D`.
125 The valid parameters in the dictionary depend on the type of the
126 structural element part, and are detailed in the documentation of
127 the corresponding class. The only parameter that is common to all the
128 classes is "MeshGroups" (that can also be named "Group_Maille"). It
129 defines the name of the geometrical object(s) in the study that will
130 be used as primitives to build the structural element part. This
131 parameter can be either a list of strings or a single string with
132 comma separated names.
134 logger.debug("StructuralElementManager.createElement: START")
135 logger.debug("Command list: %s" % commandList)
137 element = StructuralElement(self._studyEditor.studyId)
138 orientationCmdList = []
139 for command in commandList:
140 (parttype, parameters) = command
141 if parttype == "Orientation":
142 orientationCmdList += [command]
143 elif parttype not in dir(parts):
144 logger.warning('Invalid structural element part name "%s"'
145 ' in command %s, this command will be '
146 'ignored.' % (parttype, command))
148 (meshGroupList, newparams) = self._extractMeshGroups(command)
149 for meshGroup in meshGroupList:
150 # Get the geometrical primitive object
151 if meshGroup.startswith('/'):
152 groupSObj = self._studyEditor.study.FindObjectByPath(meshGroup)
153 meshGroup = meshGroup.split('/')[-1]
156 groupSObj = self._studyEditor.study.FindObject(meshGroup)
159 if groupSObj is not None:
161 self._studyEditor.getOrLoadObject(groupSObj)
162 if groupGeomObj is None:
163 logger.error("Can't get geom object corresponding to "
164 'mesh group "%s", structural element '
165 "part %s will not be built." %
166 (meshGroup, command))
171 part = parts.__dict__[parttype](
172 self._studyEditor.studyId, meshGroup,
173 groupGeomObj, newparams)
174 element.addPart(part)
175 except InvalidParameterError, e:
176 logger.error("Invalid parameter error: %s" % e)
179 logger.exception("Can't create structural element"
180 " part with command %s." %
183 # Orientations are parsed after the parts because they must be
184 # associated with existing parts.
185 for command in orientationCmdList:
186 (parttype, parameters) = command
187 (meshGroupList, orientParams) = self._extractMeshGroups(command)
188 for meshGroup in meshGroupList:
189 element.addOrientation(meshGroup, orientParams)
192 logger.debug("StructuralElementManager.createElement: END")
195 def _extractMeshGroups(self, command):
197 This method extracts the names of the mesh groups (i.e. the
198 geometrical objects used to build the structural element part) in the
199 command in parameter. It returns a tuple containing the mesh groups as
200 a list of strings and the other parameters of the command as a new
203 (parttype, parameters) = command
204 newparams = parameters.copy()
205 groupMailleParam = newparams.pop("Group_Maille", None)
206 meshGroupParam = newparams.pop("MeshGroups", None)
207 if groupMailleParam is None and meshGroupParam is None:
208 logger.warning("No mesh group specified in command %s, this "
209 "command will be ignored." % command)
210 return ([], newparams)
211 elif groupMailleParam is not None and meshGroupParam is not None:
212 logger.warning('Both "MeshGroups" and "Group_Maille" specified in'
213 ' command %s, only "MeshGroups" will be used.' %
215 elif groupMailleParam is not None and meshGroupParam is None:
216 meshGroupParam = groupMailleParam
218 if isinstance(meshGroupParam, types.StringTypes):
219 meshGroupList = [meshGroupParam]
221 meshGroupList = meshGroupParam
223 if len(meshGroupList) == 0:
224 logger.warning("Mesh group list is empty in command %s, this "
225 "command will be ignored." % command)
227 return (meshGroupList, newparams)
230 class StructuralElement:
232 This class represents a structural element, i.e. a set of geometrical
233 objects built along geometrical primitives. The parameter `studyId`
234 defines the ID of the study that will contain the structural element. If
235 it is :const:`None` or not specified, the constructor will use the ID of
236 the active study as defined by :func:`salome.kernel.studyedit.getActiveStudyId`
237 function. Structural elements are normally created by the class
238 :class:`StructuralElementManager`, so this class should not be
239 instantiated directly in the general case.
243 MAIN_FOLDER_NAME = "Structural Elements"
245 def __init__(self, studyId = None):
246 # _parts is the dictionary mapping group name to structural element
247 # part. _shapeDict is the dictionary mapping SubShapeID objects to
248 # structural element parts. Both are used to avoid duplicate shapes
249 # in structural elements.
252 self._id = StructuralElement._counter
253 StructuralElement._counter += 1
254 self._studyEditor = getStudyEditor(studyId)
255 logger.debug("Creating structural element in study %s" %
256 self._studyEditor.studyId)
259 def _getSObject(self):
261 Find or create the study object corresponding to the structural
262 element. This object is a Geom Folder named "SE_N" where N is a
265 if self._SObject is None:
266 geompy = getGeompy(self._studyEditor.studyId)
267 geomComponent = self._studyEditor.study.FindComponent("GEOM")
268 mainFolder = self._studyEditor.findItem(geomComponent,
269 name = StructuralElement.MAIN_FOLDER_NAME,
271 if mainFolder is None:
272 mainFolder = geompy.NewFolder(StructuralElement.MAIN_FOLDER_NAME)
273 self._SObject = geompy.NewFolder("SE_" + str(self._id), mainFolder)
276 def addPart(self, newpart):
278 Add a part to the structural element.
280 :type newpart: :class:`~parts.StructuralElementPart`
281 :param newpart: the part to add to the structural element.
284 newshapes = newpart.baseShapesSet
286 # Check duplicate groups
287 if self._parts.has_key(newpart.groupName):
288 logger.warning('Mesh group "%s" is used several times in the '
289 'structural element. Only the last definition '
290 'will be used.' % newpart.groupName)
292 # Check duplicate shapes
293 intersect = newshapes.intersection(self._shapeDict.keys())
294 while len(intersect) > 0:
296 oldpartwithshape = self._shapeDict[shape]
297 oldpartshapes = oldpartwithshape.baseShapesSet
298 intersectwitholdpart = intersect.intersection(oldpartshapes)
299 logger.warning('Some shapes are common to groups "%s" and '
300 '"%s". For those, the parameters defined for '
301 '"%s" will be used.' %
302 (oldpartwithshape.groupName, newpart.groupName,
304 oldpartwithshape.baseShapesSet = \
305 oldpartshapes.difference(intersectwitholdpart)
306 intersect = intersect.difference(intersectwitholdpart)
308 # Finally add the new part in the structural element
309 self._parts[newpart.groupName] = newpart
310 for shape in newshapes:
311 self._shapeDict[shape] = newpart
313 def addOrientation(self, meshGroup, orientParams):
315 Add orientation information to a part in the structural element. This
316 information will be used to build the corresponding markers.
318 :type meshGroup: string
319 :param meshGroup: the name of a geometrical primitive. The orientation
320 information will apply to the structural element
321 part built along this primitive.
323 :type orientParams: dictionary
324 :param orientParams: parameters defining the orientation of the
325 structural element part. Those parameters are
327 :class:`~orientation.Orientation1D`.
330 if self._parts.has_key(meshGroup):
331 self._parts[meshGroup].addOrientation(orientParams)
333 logger.warning('Mesh group "%s" not found in structural element, '
334 'cannot set orientation.' % meshGroup)
338 Build the geometric shapes and the markers corresponding to the
339 different parts of the structural element, and add them to the study.
341 gg = salome.ImportComponentGUI("GEOM")
342 geompy = getGeompy(self._studyEditor.studyId)
343 for part in self._parts.itervalues():
344 # Build the structural element part
345 logger.debug("Building %s" % part)
347 (shape, markers) = part.build()
349 logger.error("Part %s has not been built" % part)
352 logger.exception("Couldn't build part %s" % part)
355 # Add the new objects to the study
356 shapeSObjName = part.name + "_" + part.groupName
357 geompy.addToStudy(shape, shapeSObjName)
358 geompy.PutToFolder(shape, self._getSObject())
360 if markers is not None and len(markers) > 0:
361 for i, marker in enumerate(markers, start = 1):
362 markerSObjName = "Orient_" + shapeSObjName
364 markerSObjName += "_%d" % i
365 geompy.addToStudy(marker, markerSObjName)
366 geompy.PutToFolder(marker, self._getSObject())
370 Display the structural element in the geom view.
372 StructuralElement.showElement(self._SObject)
375 def showElement(theSObject):
377 Display the structural element corresponding to the study object
380 if theSObject is not None:
381 gg = salome.ImportComponentGUI("GEOM")
382 aStudy = theSObject.GetStudy()
383 useCaseBuilder = aStudy.GetUseCaseBuilder()
384 editor = getStudyEditor(aStudy._get_StudyId())
385 aIterator = useCaseBuilder.GetUseCaseIterator(theSObject)
386 aIterator.Init(False)
387 while aIterator.More():
388 sobj = aIterator.Value()
389 icon = editor.getIcon(sobj)
390 if icon != "ICON_OBJBROWSER_LCS":
391 entry = aIterator.Value().GetID()
392 gg.createAndDisplayGO(entry)
393 gg.setDisplayMode(entry, 2) # Shading + edges
397 def TEST_CreateGeometry():
401 from salome.geom import geomBuilder
402 geompy = geomBuilder.New(salome.myStudy)
404 geompy.init_geom(salome.myStudy)
405 Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200)
406 edges = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["EDGE"])
407 geompy.addToStudy(Box_1, "Box_1")
408 for i in range(len(edges)):
409 geompy.addToStudyInFather(Box_1, edges[i], "Edge_%d" % i)
410 faces = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["FACE"])
411 faces[3].SetColor(SALOMEDS.Color(1.0,0.5,0.0))
412 faces[4].SetColor(SALOMEDS.Color(0.0,1.0,0.5))
413 for i in range(len(faces)):
414 geompy.addToStudyInFather(Box_1, faces[i], "Face_%d" % i)
415 Cylinder_1 = geompy.MakeCylinderRH(50, 200)
416 geompy.TranslateDXDYDZ(Cylinder_1, 300, 300, 0)
417 cyl_faces = geompy.SubShapeAllSorted(Cylinder_1, geompy.ShapeType["FACE"])
418 geompy.addToStudy(Cylinder_1, "Cylinder_1")
419 for i in range(len(cyl_faces)):
420 geompy.addToStudyInFather(Cylinder_1, cyl_faces[i], "CylFace_%d" % i)
421 Cylinder_2 = geompy.MakeTranslation(Cylinder_1, 100, 100, 0)
422 cyl_faces2 = geompy.SubShapeAllSorted(Cylinder_2,
423 geompy.ShapeType["FACE"])
424 geompy.addToStudy(Cylinder_2, "Cylinder_2")
425 for i in range(len(cyl_faces2)):
426 geompy.addToStudyInFather(Cylinder_2, cyl_faces2[i],
430 def TEST_StructuralElement():
432 TEST_CreateGeometry()
433 liste_commandes = [('Orientation', {'MeshGroups': 'Edge_4',
434 'VECT_Y': (1.0, 0.0, 1.0)}),
435 ('Orientation', {'MeshGroups': 'Edge_5',
437 ('GeneralBeam', {'MeshGroups': ['Edge_1', 'Edge_7'],
438 'A': 1, 'IY1': 20, 'IY2': 40,
439 'IZ1': 60, 'IZ2': 30}),
440 ('VisuPoutreCercle', {'MeshGroups': ['Edge_6'],
441 'R1': 30, 'R2': 20}),
442 ('CircularBeam', {'MeshGroups': ['Edge_2', 'Edge_3'],
444 ('RectangularBeam', {'MeshGroups': ['Edge_4', 'Edge_5'],
445 'HZ1': 60, 'HY1': 40,
446 'EPZ1': 15, 'EPY1': 10,
447 'HZ2': 40, 'HY2': 60,
448 'EPZ2': 10, 'EPY2': 15}),
449 ('VisuCable', {'MeshGroups': 'Edge_7', 'R': 5}),
450 ('VisuCoque', {'MeshGroups': 'Face_4',
451 'Epais': 10, 'Excentre': 5,
452 'angleAlpha': 45, 'angleBeta': 60}),
453 ('VisuCoque', {'MeshGroups': 'CylFace_2', 'Epais': 5}),
454 ('VisuGrille', {'MeshGroups': 'Face_5', 'Excentre': 5,
455 'angleAlpha': 45, 'angleBeta': 60}),
456 ('VisuGrille', {'MeshGroups': 'CylFace2_2',
457 'Excentre': 5, 'origAxeX': 400,
458 'origAxeY': 400, 'origAxeZ': 0,
459 'axeX': 0, 'axeY': 0, 'axeZ': 100}),
462 structElemManager = StructuralElementManager()
463 elem = structElemManager.createElement(liste_commandes)
464 if salome.hasDesktop():
466 salome.sg.updateObjBrowser(True)
469 # Main function only used to test the module
470 if __name__ == "__main__":
471 TEST_StructuralElement()