1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2007-2011 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.
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.structelem import parts
70 from salome.geom.structelem.parts import InvalidParameterError
72 class StructuralElementManager:
74 This class manages the structural elements in the study. It is used to
75 create a new structural element from a list of commands. The parameter
76 `studyId` defines the ID of the study in which the manager will create
77 structural elements. If it is :const:`None` or not specified, it will use
78 the ID of the current study as defined by
79 :func:`salome.kernel.studyedit.getActiveStudyId` function.
81 def __init__(self, studyId = None):
82 self._studyEditor = getStudyEditor(studyId)
84 def createElement(self, commandList):
86 Create a structural element from the list of commands `commandList`.
87 Each command in this list represent a part of the structural element,
88 that is a specific kind of shape (circular beam, grid, etc.)
89 associated with one or several geometrical primitives. A command must
90 be a tuple. The first element is the structural element part class
91 name or alias name. The second element is a dictionary containing the
92 parameters describing the part. Valid class names are all the classes
93 defined in the module :mod:`~salome.geom.structelem.parts` and inheriting
94 class :class:`~parts.StructuralElementPart`. There are also several
95 aliases for backward compatibility. Here is the complete list:
97 * :class:`~parts.GeneralBeam`
98 * :class:`~parts.CircularBeam`
99 * :class:`~parts.RectangularBeam`
100 * :class:`~parts.ThickShell`
101 * :class:`~parts.Grid`
103 * :func:`~parts.VisuPoutreGenerale` (alias for
104 :class:`~parts.GeneralBeam`)
105 * :func:`~parts.VisuPoutreCercle` (alias for
106 :class:`~parts.CircularBeam`)
107 * :func:`~parts.VisuPoutreRectangle` (alias for
108 :class:`~parts.RectangularBeam`)
109 * :func:`~parts.VisuBarreGenerale` (alias for
110 :class:`~parts.GeneralBeam`)
111 * :func:`~parts.VisuBarreRectangle` (alias for
112 :class:`~parts.RectangularBeam`)
113 * :func:`~parts.VisuBarreCercle` (alias for
114 :class:`~parts.CircularBeam`)
115 * :func:`~parts.VisuCable` (alias for :class:`~parts.CircularBeam`)
116 * :func:`~parts.VisuCoque` (alias for :class:`~parts.ThickShell`)
117 * :func:`~parts.VisuGrille` (alias for :class:`~parts.Grid`)
119 * ``Orientation``: This identifier is used to specify the orientation
120 of one or several 1D structural element parts (i.e. beams). The
121 parameters are described in class
122 :class:`~orientation.Orientation1D`.
124 The valid parameters in the dictionary depend on the type of the
125 structural element part, and are detailed in the documentation of
126 the corresponding class. The only parameter that is common to all the
127 classes is "MeshGroups" (that can also be named "Group_Maille"). It
128 defines the name of the geometrical object(s) in the study that will
129 be used as primitives to build the structural element part. This
130 parameter can be either a list of strings or a single string with
131 comma separated names.
133 logger.debug("StructuralElementManager.createElement: START")
134 logger.debug("Command list: %s" % commandList)
136 element = StructuralElement(self._studyEditor.studyId)
137 orientationCmdList = []
138 for command in commandList:
139 (parttype, parameters) = command
140 if parttype == "Orientation":
141 orientationCmdList += [command]
142 elif parttype not in dir(parts):
143 logger.warning('Invalid structural element part name "%s"'
144 ' in command %s, this command will be '
145 'ignored.' % (parttype, command))
147 (meshGroupList, newparams) = self._extractMeshGroups(command)
148 for meshGroup in meshGroupList:
149 # Get the geometrical primitive object
150 groupSObj = self._studyEditor.study.FindObject(meshGroup)
152 if groupSObj is not None:
154 self._studyEditor.getOrLoadObject(groupSObj)
155 if groupGeomObj is None:
156 logger.error("Can't get geom object corresponding to "
157 'mesh group "%s", structural element '
158 "part %s will not be built." %
164 part = parts.__dict__[parttype](
165 self._studyEditor.studyId, meshGroup,
166 groupGeomObj, newparams)
167 element.addPart(part)
168 except InvalidParameterError, e:
169 logger.error("Invalid parameter error: %s" % e)
172 logger.exception("Can't create structural element"
173 " part with command %s." %
176 # Orientations are parsed after the parts because they must be
177 # associated with existing parts.
178 for command in orientationCmdList:
179 (parttype, parameters) = command
180 (meshGroupList, orientParams) = self._extractMeshGroups(command)
181 for meshGroup in meshGroupList:
182 element.addOrientation(meshGroup, orientParams)
185 logger.debug("StructuralElementManager.createElement: END")
188 def _extractMeshGroups(self, command):
190 This method extracts the names of the mesh groups (i.e. the
191 geometrical objects used to build the structural element part) in the
192 command in parameter. It returns a tuple containing the mesh groups as
193 a list of strings and the other parameters of the command as a new
196 (parttype, parameters) = command
197 newparams = parameters.copy()
198 groupMailleParam = newparams.pop("Group_Maille", None)
199 meshGroupParam = newparams.pop("MeshGroups", None)
200 if groupMailleParam is None and meshGroupParam is None:
201 logger.warning("No mesh group specified in command %s, this "
202 "command will be ignored." % command)
203 return ([], newparams)
204 elif groupMailleParam is not None and meshGroupParam is not None:
205 logger.warning('Both "MeshGroups" and "Group_Maille" specified in'
206 ' command %s, only "MeshGroups" will be used.' %
208 elif groupMailleParam is not None and meshGroupParam is None:
209 meshGroupParam = groupMailleParam
212 if type(meshGroupParam) == types.StringType:
213 meshGroupList = self._getMeshGroupListFromString(meshGroupParam)
215 for item in meshGroupParam:
216 meshGroupList += self._getMeshGroupListFromString(item)
218 if len(meshGroupList) == 0:
219 logger.warning("Mesh group list is empty in command %s, this "
220 "command will be ignored." % command)
222 return (meshGroupList, newparams)
224 def _getMeshGroupListFromString(self, meshString):
226 This method splits the string in parameter to extract comma separated
227 names. Those names are returned as a list of strings.
230 list = meshString.split(",")
232 strippedItem = item.strip()
233 if len(strippedItem) > 0:
234 meshGroupList.append(strippedItem)
238 class StructuralElement:
240 This class represents a structural element, i.e. a set of geometrical
241 objects built along geometrical primitives. The parameter `studyId`
242 defines the ID of the study that will contain the structural element. If
243 it is :const:`None` or not specified, the constructor will use the ID of
244 the active study as defined by :func:`salome.kernel.studyedit.getActiveStudyId`
245 function. Structural elements are normally created by the class
246 :class:`StructuralElementManager`, so this class should not be
247 instantiated directly in the general case.
250 _mainFolderTag = 14725
252 def __init__(self, studyId = None):
253 # _parts is the dictionary mapping group name to structural element
254 # part. _shapeDict is the dictionary mapping SubShapeID objects to
255 # structural element parts. Both are used to avoid duplicate shapes
256 # in structural elements.
259 self._id = StructuralElement._counter
260 StructuralElement._counter += 1
261 self._studyEditor = getStudyEditor(studyId)
262 logger.debug("Creating structural element in study %s" %
263 self._studyEditor.studyId)
266 def _getSObject(self):
268 Find or create the study object corresponding to the structural
269 element. This object is named "SE_N" where N is a numerical ID.
271 if self._SObject is None:
272 geomComponent = self._studyEditor.study.FindComponent("GEOM")
273 mainFolder = self._studyEditor.setItemAtTag(geomComponent,
274 StructuralElement._mainFolderTag,
275 name = "Structural Elements")
276 self._SObject = self._studyEditor.findOrCreateItem(mainFolder,
277 name = "SE_" + str(self._id))
280 def addPart(self, newpart):
282 Add a part to the structural element.
284 :type newpart: :class:`~parts.StructuralElementPart`
285 :param newpart: the part to add to the structural element.
288 newshapes = newpart.baseShapesSet
290 # Check duplicate groups
291 if self._parts.has_key(newpart.groupName):
292 logger.warning('Mesh group "%s" is used several times in the '
293 'structural element. Only the last definition '
294 'will be used.' % newpart.groupName)
296 # Check duplicate shapes
297 intersect = newshapes.intersection(self._shapeDict.keys())
298 while len(intersect) > 0:
300 oldpartwithshape = self._shapeDict[shape]
301 oldpartshapes = oldpartwithshape.baseShapesSet
302 intersectwitholdpart = intersect.intersection(oldpartshapes)
303 logger.warning('Some shapes are common to groups "%s" and '
304 '"%s". For those, the parameters defined for '
305 '"%s" will be used.' %
306 (oldpartwithshape.groupName, newpart.groupName,
308 oldpartwithshape.baseShapesSet = \
309 oldpartshapes.difference(intersectwitholdpart)
310 intersect = intersect.difference(intersectwitholdpart)
312 # Finally add the new part in the structural element
313 self._parts[newpart.groupName] = newpart
314 for shape in newshapes:
315 self._shapeDict[shape] = newpart
317 def addOrientation(self, meshGroup, orientParams):
319 Add orientation information to a part in the structural element. This
320 information will be used to build the corresponding markers.
322 :type meshGroup: string
323 :param meshGroup: the name of a geometrical primitive. The orientation
324 information will apply to the structural element
325 part built along this primitive.
327 :type orientParams: dictionary
328 :param orientParams: parameters defining the orientation of the
329 structural element part. Those parameters are
331 :class:`~orientation.Orientation1D`.
334 if self._parts.has_key(meshGroup):
335 self._parts[meshGroup].addOrientation(orientParams)
337 logger.warning('Mesh group "%s" not found in structural element, '
338 'cannot set orientation.' % meshGroup)
342 Build the geometric shapes and the markers corresponding to the
343 different parts of the structural element, and add them to the study.
345 gg = salome.ImportComponentGUI("GEOM")
346 for part in self._parts.itervalues():
347 # Build the structural element part
348 logger.debug("Building %s" % part)
350 (shape, markers) = part.build()
352 logger.error("Part %s has not been built" % part)
355 logger.exception("Couldn't build part %s" % part)
358 # Add the new objects to the study
359 IOR = self._studyEditor.study.ConvertObjectToIOR(shape)
360 shapeSObjName = part.name + "_" + part.groupName
362 if salome.hasDesktop():
363 icon = gg.getShapeTypeIcon(IOR)
364 shapeSObj = self._studyEditor.createItem(self._getSObject(),
365 name = shapeSObjName, IOR = IOR,
367 if markers is not None and len(markers) > 0:
369 for marker in markers:
371 self._studyEditor.study.ConvertObjectToIOR(marker)
372 markerSObjName = "Orient_" + shapeSObjName
374 markerSObjName += "_%d" % i
375 markerSObj = self._studyEditor.createItem(
377 name = markerSObjName,
379 icon = "ICON_OBJBROWSER_LCS")
384 Display the structural element in the geom view.
386 StructuralElement.showElement(self._SObject)
389 def showElement(theSObject):
391 Display the structural element corresponding to the study object
394 if theSObject is not None:
395 gg = salome.ImportComponentGUI("GEOM")
396 aStudy = theSObject.GetStudy()
397 editor = getStudyEditor(aStudy._get_StudyId())
398 aIterator = aStudy.NewChildIterator(theSObject)
400 while aIterator.More():
401 sobj = aIterator.Value()
402 icon = editor.getIcon(sobj)
403 if icon != "ICON_OBJBROWSER_LCS":
404 entry = aIterator.Value().GetID()
405 gg.createAndDisplayGO(entry)
406 gg.setDisplayMode(entry, 1)
410 def TEST_CreateGeometry():
413 geompy.init_geom(salome.myStudy)
414 Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200)
415 edges = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["EDGE"])
416 edges[0].SetColor(SALOMEDS.Color(1.0,0.0,0.0))
417 edges[1].SetColor(SALOMEDS.Color(0.0,1.0,0.0))
418 edges[2].SetColor(SALOMEDS.Color(0.0,0.0,1.0))
419 edges[3].SetColor(SALOMEDS.Color(1.0,0.0,1.0))
420 edges[4].SetColor(SALOMEDS.Color(0.0,1.0,1.0))
421 edges[5].SetColor(SALOMEDS.Color(0.5,0.0,0.0))
422 edges[6].SetColor(SALOMEDS.Color(0.0,0.5,0.0))
423 edges[7].SetColor(SALOMEDS.Color(0.0,0.0,0.5))
424 geompy.addToStudy(Box_1, "Box_1")
425 for i in range(len(edges)):
426 geompy.addToStudyInFather(Box_1, edges[i], "Edge_%d" % i)
427 faces = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["FACE"])
428 faces[3].SetColor(SALOMEDS.Color(1.0,0.5,0.0))
429 faces[4].SetColor(SALOMEDS.Color(0.0,1.0,0.5))
430 for i in range(len(faces)):
431 geompy.addToStudyInFather(Box_1, faces[i], "Face_%d" % i)
432 Cylinder_1 = geompy.MakeCylinderRH(50, 200)
433 geompy.TranslateDXDYDZ(Cylinder_1, 300, 300, 0)
434 cyl_faces = geompy.SubShapeAllSorted(Cylinder_1, geompy.ShapeType["FACE"])
435 geompy.addToStudy(Cylinder_1, "Cylinder_1")
436 for i in range(len(cyl_faces)):
437 geompy.addToStudyInFather(Cylinder_1, cyl_faces[i], "CylFace_%d" % i)
438 Cylinder_2 = geompy.MakeTranslation(Cylinder_1, 100, 100, 0)
439 cyl_faces2 = geompy.SubShapeAllSorted(Cylinder_2,
440 geompy.ShapeType["FACE"])
441 geompy.addToStudy(Cylinder_2, "Cylinder_2")
442 for i in range(len(cyl_faces2)):
443 geompy.addToStudyInFather(Cylinder_2, cyl_faces2[i],
447 def TEST_StructuralElement():
449 TEST_CreateGeometry()
450 liste_commandes = [('Orientation', {'MeshGroups': 'Edge_4',
451 'VECT_Y': (1.0, 0.0, 1.0)}),
452 ('Orientation', {'MeshGroups': 'Edge_5',
454 ('GeneralBeam', {'MeshGroups': 'Edge_1, Edge_7'}),
455 ('VisuPoutreCercle', {'MeshGroups': ['Edge_6'],
456 'R1': 30, 'R2': 20}),
457 ('CircularBeam', {'MeshGroups': ['Edge_2', 'Edge_3'],
459 ('RectangularBeam', {'MeshGroups': 'Edge_4, Edge_5',
460 'HZ1': 60, 'HY1': 40,
461 'EPZ1': 15, 'EPY1': 10,
462 'HZ2': 40, 'HY2': 60,
463 'EPZ2': 10, 'EPY2': 15}),
464 ('VisuCable', {'MeshGroups': 'Edge_7', 'R': 5}),
465 ('VisuCoque', {'MeshGroups': 'Face_4',
466 'Epais': 10, 'Excentre': 5,
467 'angleAlpha': 45, 'angleBeta': 60}),
468 ('VisuCoque', {'MeshGroups': 'CylFace_2', 'Epais': 5}),
469 ('VisuGrille', {'MeshGroups': 'Face_5', 'Excentre': 5,
470 'angleAlpha': 45, 'angleBeta': 60}),
471 ('VisuGrille', {'MeshGroups': 'CylFace2_2',
472 'Excentre': 5, 'origAxeX': 400,
473 'origAxeY': 400, 'origAxeZ': 0,
474 'axeX': 0, 'axeY': 0, 'axeZ': 100}),
477 structElemManager = StructuralElementManager()
478 elem = structElemManager.createElement(liste_commandes)
479 if salome.hasDesktop():
481 salome.sg.updateObjBrowser(True)
484 # Main function only used to test the module
485 if __name__ == "__main__":
486 TEST_StructuralElement()