1 # -*- coding: utf-8 -*-
3 # Copyright (C) 2007-2013 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 if meshGroup.startswith('/'):
151 groupSObj = self._studyEditor.study.FindObjectByPath(meshGroup)
152 meshGroup = meshGroup.split('/')[-1]
155 groupSObj = self._studyEditor.study.FindObject(meshGroup)
158 if groupSObj is not None:
160 self._studyEditor.getOrLoadObject(groupSObj)
161 if groupGeomObj is None:
162 logger.error("Can't get geom object corresponding to "
163 'mesh group "%s", structural element '
164 "part %s will not be built." %
170 part = parts.__dict__[parttype](
171 self._studyEditor.studyId, meshGroup,
172 groupGeomObj, newparams)
173 element.addPart(part)
174 except InvalidParameterError, e:
175 logger.error("Invalid parameter error: %s" % e)
178 logger.exception("Can't create structural element"
179 " part with command %s." %
182 # Orientations are parsed after the parts because they must be
183 # associated with existing parts.
184 for command in orientationCmdList:
185 (parttype, parameters) = command
186 (meshGroupList, orientParams) = self._extractMeshGroups(command)
187 for meshGroup in meshGroupList:
188 element.addOrientation(meshGroup, orientParams)
191 logger.debug("StructuralElementManager.createElement: END")
194 def _extractMeshGroups(self, command):
196 This method extracts the names of the mesh groups (i.e. the
197 geometrical objects used to build the structural element part) in the
198 command in parameter. It returns a tuple containing the mesh groups as
199 a list of strings and the other parameters of the command as a new
202 (parttype, parameters) = command
203 newparams = parameters.copy()
204 groupMailleParam = newparams.pop("Group_Maille", None)
205 meshGroupParam = newparams.pop("MeshGroups", None)
206 if groupMailleParam is None and meshGroupParam is None:
207 logger.warning("No mesh group specified in command %s, this "
208 "command will be ignored." % command)
209 return ([], newparams)
210 elif groupMailleParam is not None and meshGroupParam is not None:
211 logger.warning('Both "MeshGroups" and "Group_Maille" specified in'
212 ' command %s, only "MeshGroups" will be used.' %
214 elif groupMailleParam is not None and meshGroupParam is None:
215 meshGroupParam = groupMailleParam
218 if type(meshGroupParam) == types.StringType:
219 meshGroupList = self._getMeshGroupListFromString(meshGroupParam)
221 for item in meshGroupParam:
222 meshGroupList += self._getMeshGroupListFromString(item)
224 if len(meshGroupList) == 0:
225 logger.warning("Mesh group list is empty in command %s, this "
226 "command will be ignored." % command)
228 return (meshGroupList, newparams)
230 def _getMeshGroupListFromString(self, meshString):
232 This method splits the string in parameter to extract comma separated
233 names. Those names are returned as a list of strings.
236 list = meshString.split(",")
238 strippedItem = item.strip()
239 if len(strippedItem) > 0:
240 meshGroupList.append(strippedItem)
244 class StructuralElement:
246 This class represents a structural element, i.e. a set of geometrical
247 objects built along geometrical primitives. The parameter `studyId`
248 defines the ID of the study that will contain the structural element. If
249 it is :const:`None` or not specified, the constructor will use the ID of
250 the active study as defined by :func:`salome.kernel.studyedit.getActiveStudyId`
251 function. Structural elements are normally created by the class
252 :class:`StructuralElementManager`, so this class should not be
253 instantiated directly in the general case.
256 _mainFolderTag = 14725
258 def __init__(self, studyId = None):
259 # _parts is the dictionary mapping group name to structural element
260 # part. _shapeDict is the dictionary mapping SubShapeID objects to
261 # structural element parts. Both are used to avoid duplicate shapes
262 # in structural elements.
265 self._id = StructuralElement._counter
266 StructuralElement._counter += 1
267 self._studyEditor = getStudyEditor(studyId)
268 logger.debug("Creating structural element in study %s" %
269 self._studyEditor.studyId)
272 def _getSObject(self):
274 Find or create the study object corresponding to the structural
275 element. This object is named "SE_N" where N is a numerical ID.
277 if self._SObject is None:
278 geomComponent = self._studyEditor.study.FindComponent("GEOM")
279 mainFolder = self._studyEditor.setItemAtTag(geomComponent,
280 StructuralElement._mainFolderTag,
281 name = "Structural Elements")
282 self._SObject = self._studyEditor.findOrCreateItem(mainFolder,
283 name = "SE_" + str(self._id))
286 def addPart(self, newpart):
288 Add a part to the structural element.
290 :type newpart: :class:`~parts.StructuralElementPart`
291 :param newpart: the part to add to the structural element.
294 newshapes = newpart.baseShapesSet
296 # Check duplicate groups
297 if self._parts.has_key(newpart.groupName):
298 logger.warning('Mesh group "%s" is used several times in the '
299 'structural element. Only the last definition '
300 'will be used.' % newpart.groupName)
302 # Check duplicate shapes
303 intersect = newshapes.intersection(self._shapeDict.keys())
304 while len(intersect) > 0:
306 oldpartwithshape = self._shapeDict[shape]
307 oldpartshapes = oldpartwithshape.baseShapesSet
308 intersectwitholdpart = intersect.intersection(oldpartshapes)
309 logger.warning('Some shapes are common to groups "%s" and '
310 '"%s". For those, the parameters defined for '
311 '"%s" will be used.' %
312 (oldpartwithshape.groupName, newpart.groupName,
314 oldpartwithshape.baseShapesSet = \
315 oldpartshapes.difference(intersectwitholdpart)
316 intersect = intersect.difference(intersectwitholdpart)
318 # Finally add the new part in the structural element
319 self._parts[newpart.groupName] = newpart
320 for shape in newshapes:
321 self._shapeDict[shape] = newpart
323 def addOrientation(self, meshGroup, orientParams):
325 Add orientation information to a part in the structural element. This
326 information will be used to build the corresponding markers.
328 :type meshGroup: string
329 :param meshGroup: the name of a geometrical primitive. The orientation
330 information will apply to the structural element
331 part built along this primitive.
333 :type orientParams: dictionary
334 :param orientParams: parameters defining the orientation of the
335 structural element part. Those parameters are
337 :class:`~orientation.Orientation1D`.
340 if self._parts.has_key(meshGroup):
341 self._parts[meshGroup].addOrientation(orientParams)
343 logger.warning('Mesh group "%s" not found in structural element, '
344 'cannot set orientation.' % meshGroup)
348 Build the geometric shapes and the markers corresponding to the
349 different parts of the structural element, and add them to the study.
351 gg = salome.ImportComponentGUI("GEOM")
352 for part in self._parts.itervalues():
353 # Build the structural element part
354 logger.debug("Building %s" % part)
356 (shape, markers) = part.build()
358 logger.error("Part %s has not been built" % part)
361 logger.exception("Couldn't build part %s" % part)
364 # Add the new objects to the study
365 IOR = self._studyEditor.study.ConvertObjectToIOR(shape)
366 shapeSObjName = part.name + "_" + part.groupName
368 if salome.hasDesktop():
369 icon = gg.getShapeTypeIcon(IOR)
370 shapeSObj = self._studyEditor.createItem(self._getSObject(),
371 name = shapeSObjName, IOR = IOR,
373 if markers is not None and len(markers) > 0:
375 for marker in markers:
377 self._studyEditor.study.ConvertObjectToIOR(marker)
378 markerSObjName = "Orient_" + shapeSObjName
380 markerSObjName += "_%d" % i
381 markerSObj = self._studyEditor.createItem(
383 name = markerSObjName,
385 icon = "ICON_OBJBROWSER_LCS")
390 Display the structural element in the geom view.
392 StructuralElement.showElement(self._SObject)
395 def showElement(theSObject):
397 Display the structural element corresponding to the study object
400 if theSObject is not None:
401 gg = salome.ImportComponentGUI("GEOM")
402 aStudy = theSObject.GetStudy()
403 editor = getStudyEditor(aStudy._get_StudyId())
404 aIterator = aStudy.NewChildIterator(theSObject)
406 while aIterator.More():
407 sobj = aIterator.Value()
408 icon = editor.getIcon(sobj)
409 if icon != "ICON_OBJBROWSER_LCS":
410 entry = aIterator.Value().GetID()
411 gg.createAndDisplayGO(entry)
412 gg.setDisplayMode(entry, 1)
416 def TEST_CreateGeometry():
420 from salome.geom import geomBuilder
421 geompy = geomBuilder.New(salome.myStudy)
423 geompy.init_geom(salome.myStudy)
424 Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200)
425 edges = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["EDGE"])
426 geompy.addToStudy(Box_1, "Box_1")
427 for i in range(len(edges)):
428 geompy.addToStudyInFather(Box_1, edges[i], "Edge_%d" % i)
429 faces = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["FACE"])
430 faces[3].SetColor(SALOMEDS.Color(1.0,0.5,0.0))
431 faces[4].SetColor(SALOMEDS.Color(0.0,1.0,0.5))
432 for i in range(len(faces)):
433 geompy.addToStudyInFather(Box_1, faces[i], "Face_%d" % i)
434 Cylinder_1 = geompy.MakeCylinderRH(50, 200)
435 geompy.TranslateDXDYDZ(Cylinder_1, 300, 300, 0)
436 cyl_faces = geompy.SubShapeAllSorted(Cylinder_1, geompy.ShapeType["FACE"])
437 geompy.addToStudy(Cylinder_1, "Cylinder_1")
438 for i in range(len(cyl_faces)):
439 geompy.addToStudyInFather(Cylinder_1, cyl_faces[i], "CylFace_%d" % i)
440 Cylinder_2 = geompy.MakeTranslation(Cylinder_1, 100, 100, 0)
441 cyl_faces2 = geompy.SubShapeAllSorted(Cylinder_2,
442 geompy.ShapeType["FACE"])
443 geompy.addToStudy(Cylinder_2, "Cylinder_2")
444 for i in range(len(cyl_faces2)):
445 geompy.addToStudyInFather(Cylinder_2, cyl_faces2[i],
449 def TEST_StructuralElement():
451 TEST_CreateGeometry()
452 liste_commandes = [('Orientation', {'MeshGroups': 'Edge_4',
453 'VECT_Y': (1.0, 0.0, 1.0)}),
454 ('Orientation', {'MeshGroups': 'Edge_5',
456 ('GeneralBeam', {'MeshGroups': 'Edge_1, Edge_7',
457 'A': 1, 'IY1': 20, 'IY2': 40,
458 'IZ1': 60, 'IZ2': 30}),
459 ('VisuPoutreCercle', {'MeshGroups': ['Edge_6'],
460 'R1': 30, 'R2': 20}),
461 ('CircularBeam', {'MeshGroups': ['Edge_2', 'Edge_3'],
463 ('RectangularBeam', {'MeshGroups': 'Edge_4, Edge_5',
464 'HZ1': 60, 'HY1': 40,
465 'EPZ1': 15, 'EPY1': 10,
466 'HZ2': 40, 'HY2': 60,
467 'EPZ2': 10, 'EPY2': 15}),
468 ('VisuCable', {'MeshGroups': 'Edge_7', 'R': 5}),
469 ('VisuCoque', {'MeshGroups': 'Face_4',
470 'Epais': 10, 'Excentre': 5,
471 'angleAlpha': 45, 'angleBeta': 60}),
472 ('VisuCoque', {'MeshGroups': 'CylFace_2', 'Epais': 5}),
473 ('VisuGrille', {'MeshGroups': 'Face_5', 'Excentre': 5,
474 'angleAlpha': 45, 'angleBeta': 60}),
475 ('VisuGrille', {'MeshGroups': 'CylFace2_2',
476 'Excentre': 5, 'origAxeX': 400,
477 'origAxeY': 400, 'origAxeZ': 0,
478 'axeX': 0, 'axeY': 0, 'axeZ': 100}),
481 structElemManager = StructuralElementManager()
482 elem = structElemManager.createElement(liste_commandes)
483 if salome.hasDesktop():
485 salome.sg.updateObjBrowser(True)
488 # Main function only used to test the module
489 if __name__ == "__main__":
490 TEST_StructuralElement()