Salome HOME
Update copyrights 2014.
[modules/geom.git] / src / GEOM_PY / structelem / __init__.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2007-2014  CEA/DEN, EDF R&D, OPEN CASCADE
4 #
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.
9 #
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.
14 #
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
18 #
19 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
20 #
21 """
22 This package is used to create and visualize structural elements. It contains
23 three modules:
24
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.
34
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
38 the geom viewer.
39
40 Structural elements are generally created by the
41 :class:`StructuralElementManager` class, from a list of commands describing
42 the element to create.
43
44 Example::
45
46     commandList = [('VisuPoutreGenerale', {'Group_Maille': 'Edge_1'}),
47                    ('VisuBarreCercle',
48                     {'R': 30, 'Group_Maille': 'Edge_1', 'EP': 15}),
49                   ]
50
51     structElemManager = StructuralElementManager()
52     elem = structElemManager.createElement(commandList)
53     elem.display()
54     salome.sg.updateObjBrowser(True)
55
56 """
57
58 import types
59
60 import salome
61
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
66
67 __all__ = ["parts", "orientation"]
68
69 from salome.geom.geomtools import getGeompy
70 from salome.geom.structelem import parts
71 from salome.geom.structelem.parts import InvalidParameterError
72
73 class StructuralElementManager:
74     """
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.
81     """
82     def __init__(self, studyId = None):
83         self._studyEditor = getStudyEditor(studyId)
84
85     def createElement(self, commandList):
86         """
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:
97         
98         * :class:`~parts.GeneralBeam`
99         * :class:`~parts.CircularBeam`
100         * :class:`~parts.RectangularBeam`
101         * :class:`~parts.ThickShell`
102         * :class:`~parts.Grid`
103
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`)
119         
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`.
124
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.
133         """
134         logger.debug("StructuralElementManager.createElement: START")
135         logger.debug("Command list: %s" % commandList)
136
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))
147             else:
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]
154                         pass
155                     else:
156                         groupSObj = self._studyEditor.study.FindObject(meshGroup)
157                         pass
158                     groupGeomObj = None
159                     if groupSObj is not None:
160                         groupGeomObj = \
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))
167                         continue
168                     
169                     # Create the part
170                     try:
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)
177                         raise
178                     except:
179                         logger.exception("Can't create structural element"
180                                          " part with command %s." %
181                                          str(command))
182
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)
190
191         element.build()
192         logger.debug("StructuralElementManager.createElement: END")
193         return element
194     
195     def _extractMeshGroups(self, command):
196         """
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
201         dictionary.
202         """
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.' %
214                            command)
215         elif groupMailleParam is not None and meshGroupParam is None:
216             meshGroupParam = groupMailleParam
217         
218         if isinstance(meshGroupParam, types.StringTypes):
219             meshGroupList = [meshGroupParam]
220         else:
221             meshGroupList = meshGroupParam
222         
223         if len(meshGroupList) == 0:
224             logger.warning("Mesh group list is empty in command %s, this "
225                            "command will be ignored." % command)
226
227         return (meshGroupList, newparams)
228
229
230 class StructuralElement:
231     """
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.
240     """
241     _counter = 1
242
243     MAIN_FOLDER_NAME = "Structural Elements"
244
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.
250         self._parts = {}
251         self._shapeDict = {}
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)
257         self._SObject = None
258
259     def _getSObject(self):
260         """
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
263         numerical ID. 
264         """
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,
270                                                     typeId=999)
271             if mainFolder is None:
272                 mainFolder = geompy.NewFolder(StructuralElement.MAIN_FOLDER_NAME)
273             self._SObject = geompy.NewFolder("SE_" + str(self._id), mainFolder)
274         return self._SObject
275
276     def addPart(self, newpart):
277         """
278         Add a part to the structural element.
279
280         :type  newpart: :class:`~parts.StructuralElementPart`
281         :param newpart: the part to add to the structural element.
282
283         """
284         newshapes = newpart.baseShapesSet
285
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)
291         else:
292             # Check duplicate shapes
293             intersect = newshapes.intersection(self._shapeDict.keys())
294             while len(intersect) > 0:
295                 shape, = intersect
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,
303                                 newpart.groupName))
304                 oldpartwithshape.baseShapesSet = \
305                                 oldpartshapes.difference(intersectwitholdpart)
306                 intersect = intersect.difference(intersectwitholdpart)
307
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
312
313     def addOrientation(self, meshGroup, orientParams):
314         """
315         Add orientation information to a part in the structural element. This
316         information will be used to build the corresponding markers.
317
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.
322
323         :type  orientParams: dictionary
324         :param orientParams: parameters defining the orientation of the
325                              structural element part. Those parameters are
326                              detailed in class
327                              :class:`~orientation.Orientation1D`.
328
329         """
330         if self._parts.has_key(meshGroup):
331             self._parts[meshGroup].addOrientation(orientParams)
332         else:
333             logger.warning('Mesh group "%s" not found in structural element, '
334                            'cannot set orientation.' % meshGroup)
335
336     def build(self):
337         """
338         Build the geometric shapes and the markers corresponding to the
339         different parts of the structural element, and add them to the study.
340         """
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)
346             try:
347                 (shape, markers) = part.build()
348                 if shape is None:
349                     logger.error("Part %s has not been built" % part)
350                     continue
351             except:
352                 logger.exception("Couldn't build part %s" % part)
353                 continue
354             
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())
359
360             if markers is not None and len(markers) > 0:
361                 for i, marker in enumerate(markers, start = 1):
362                     markerSObjName = "Orient_" + shapeSObjName
363                     if len(markers) > 1:
364                         markerSObjName += "_%d" % i
365                     geompy.addToStudy(marker, markerSObjName)
366                     geompy.PutToFolder(marker, self._getSObject())
367
368     def display(self):
369         """
370         Display the structural element in the geom view.
371         """
372         StructuralElement.showElement(self._SObject)
373
374     @staticmethod
375     def showElement(theSObject):
376         """
377         Display the structural element corresponding to the study object
378         `theSObject`
379         """
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
394                 aIterator.Next()
395
396
397 def TEST_CreateGeometry():
398     import salome
399     salome.salome_init()
400     import GEOM
401     from salome.geom import geomBuilder
402     geompy = geomBuilder.New(salome.myStudy)
403     import SALOMEDS
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],
427                                   "CylFace2_%d" % i)
428
429
430 def TEST_StructuralElement():
431     salome.salome_init()
432     TEST_CreateGeometry()
433     liste_commandes = [('Orientation', {'MeshGroups': 'Edge_4',
434                                         'VECT_Y': (1.0, 0.0, 1.0)}),
435                        ('Orientation', {'MeshGroups': 'Edge_5',
436                                         'ANGL_VRIL': 45.0}),
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'],
443                                          'R': 40, 'EP': 20}),
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}),
460                       ]
461
462     structElemManager = StructuralElementManager()
463     elem = structElemManager.createElement(liste_commandes)
464     if salome.hasDesktop():
465         elem.display()
466         salome.sg.updateObjBrowser(True)
467
468
469 # Main function only used to test the module
470 if __name__ == "__main__":
471     TEST_StructuralElement()