]> SALOME platform Git repositories - modules/geom.git/blob - src/GEOM_PY/structelem/__init__.py
Salome HOME
cf5ef97aabee87e9e9b2794ea2a11adcb8cb65e5
[modules/geom.git] / src / GEOM_PY / structelem / __init__.py
1 # -*- coding: utf-8 -*-
2 #
3 # Copyright (C) 2007-2016  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 ## \defgroup structelem structelem - Structural elements package
23 #  \{ 
24 #  \details
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.
36 #
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
40 #  the geom viewer.
41 #  \n Structural elements are generally created by the StructuralElementManager class, 
42 #  from a list of commands describing the element to create.
43 #
44 #  Example:
45 #  \code
46 #  commandList = [('VisuPoutreGenerale', {'Group_Maille': 'Edge_1'}),
47 #                 ('VisuBarreCercle', {'R': 30, 'Group_Maille': 'Edge_1', 'EP': 15}),
48 #                ]
49 #
50 #  structElemManager = StructuralElementManager()
51 #  elem = structElemManager.createElement(commandList)
52 #  elem.display()
53 #  salome.sg.updateObjBrowser()
54 #  \endcode
55 #
56 #  \defgroup orientation
57 #  \defgroup parts
58 #  \}
59
60 """
61 This package is used to create and visualize structural elements. It contains
62 three modules:
63
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.
73
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
77 the geom viewer.
78
79 Structural elements are generally created by the
80 :class:`StructuralElementManager` class, from a list of commands describing
81 the element to create.
82
83 Example::
84
85     commandList = [('VisuPoutreGenerale', {'Group_Maille': 'Edge_1'}),
86                    ('VisuBarreCercle',
87                     {'R': 30, 'Group_Maille': 'Edge_1', 'EP': 15}),
88                   ]
89
90     structElemManager = StructuralElementManager()
91     elem = structElemManager.createElement(commandList)
92     elem.display()
93     salome.sg.updateObjBrowser()
94
95 """
96
97 import types
98
99 import salome
100
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
105
106 __all__ = ["parts", "orientation"]
107
108 from salome.geom.geomtools import getGeompy
109 from salome.geom.structelem import parts
110 from salome.geom.structelem.parts import InvalidParameterError
111
112 import GEOM
113
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.
116 #  \ingroup structelem
117 class StructuralElementManager:
118     """
119     This class manages the structural elements in the study. It is used to
120     create a new structural element from a list of commands.
121     """
122     def __init__(self):
123         self._studyEditor = getStudyEditor()
124
125     ## Create a structural element from the list of commands \em commandList.
126     #  Each command in this list represent a part of the structural element,
127     #  that is a specific kind of shape (circular beam, grid, etc.)
128     #  associated with one or several geometrical primitives. A command must
129     #  be a tuple. The first element is the structural element part class
130     #  name or alias name. The second element is a dictionary containing the
131     #  parameters describing the part. Valid class names are all the classes
132     #  defined in the module salome.geom.structelem.parts and inheriting
133     #  parts.StructuralElementPart. There are also several
134     #  aliases for backward compatibility. Here is the complete list:        
135     #  - parts.GeneralBeam
136     #  - parts.CircularBeam
137     #  - parts.RectangularBeam
138     #  - parts.ThickShell
139     #  - parts.Grid
140     #  - parts.VisuPoutreGenerale() (alias for parts.GeneralBeam)
141     #  - parts.VisuPoutreCercle() (alias for parts.CircularBeam)
142     #  - parts.VisuPoutreRectangle() (alias for parts.RectangularBeam)
143     #  - parts.VisuBarreGenerale() (alias for parts.GeneralBeam)
144     #  - parts.VisuBarreRectangle() (alias for parts.RectangularBeam)
145     #  - parts.VisuBarreCercle() (alias for parts.CircularBeam)
146     #  - parts.VisuCable() (alias for parts.CircularBeam)
147     #  - parts.VisuCoque() (alias for parts.ThickShell)
148     #  - parts.VisuGrille() (alias for parts.Grid)
149     #  - \b Orientation: This identifier is used to specify the orientation
150     #  of one or several 1D structural element parts (i.e. beams). The
151     #  parameters are described in class orientation.Orientation1D.
152     #
153     #  The valid parameters in the dictionary depend on the type of the
154     #  structural element part, and are detailed in the documentation of
155     #  the corresponding class. The only parameter that is common to all the
156     #  classes is "MeshGroups" (that can also be named "Group_Maille"). It
157     #  defines the name of the geometrical object(s) in the study that will
158     #  be used as primitives to build the structural element part. This
159     #  parameter can be either a list of strings or a single string with
160     #  comma separated names.
161     def createElement(self, commandList):
162         """
163         Create a structural element from the list of commands `commandList`.
164         Each command in this list represent a part of the structural element,
165         that is a specific kind of shape (circular beam, grid, etc.)
166         associated with one or several geometrical primitives. A command must
167         be a tuple. The first element is the structural element part class
168         name or alias name. The second element is a dictionary containing the
169         parameters describing the part. Valid class names are all the classes
170         defined in the module :mod:`~salome.geom.structelem.parts` and inheriting
171         class :class:`~parts.StructuralElementPart`. There are also several
172         aliases for backward compatibility. Here is the complete list:
173         
174         * :class:`~parts.GeneralBeam`
175         * :class:`~parts.CircularBeam`
176         * :class:`~parts.RectangularBeam`
177         * :class:`~parts.ThickShell`
178         * :class:`~parts.Grid`
179
180         * :func:`~parts.VisuPoutreGenerale` (alias for
181           :class:`~parts.GeneralBeam`)
182         * :func:`~parts.VisuPoutreCercle` (alias for
183           :class:`~parts.CircularBeam`)
184         * :func:`~parts.VisuPoutreRectangle` (alias for
185           :class:`~parts.RectangularBeam`)
186         * :func:`~parts.VisuBarreGenerale` (alias for
187           :class:`~parts.GeneralBeam`)
188         * :func:`~parts.VisuBarreRectangle` (alias for
189           :class:`~parts.RectangularBeam`)
190         * :func:`~parts.VisuBarreCercle` (alias for
191           :class:`~parts.CircularBeam`)
192         * :func:`~parts.VisuCable` (alias for :class:`~parts.CircularBeam`)
193         * :func:`~parts.VisuCoque` (alias for :class:`~parts.ThickShell`)
194         * :func:`~parts.VisuGrille` (alias for :class:`~parts.Grid`)
195         
196         * ``Orientation``: This identifier is used to specify the orientation
197           of one or several 1D structural element parts (i.e. beams). The
198           parameters are described in class
199           :class:`~orientation.Orientation1D`.
200
201         The valid parameters in the dictionary depend on the type of the
202         structural element part, and are detailed in the documentation of
203         the corresponding class. The only parameter that is common to all the
204         classes is "MeshGroups" (that can also be named "Group_Maille"). It
205         defines the name of the geometrical object(s) in the study that will
206         be used as primitives to build the structural element part. This
207         parameter can be either a list of strings or a single string with
208         comma separated names.
209         """
210         logger.debug("StructuralElementManager.createElement: START")
211         logger.debug("Command list: %s" % commandList)
212
213         element = StructuralElement()
214         orientationCmdList = []
215         for command in commandList:
216             (parttype, parameters) = command
217             if parttype == "Orientation":
218                 orientationCmdList += [command]
219             elif parttype not in dir(parts):
220                 logger.warning('Invalid structural element part name "%s"'
221                                ' in command %s, this command will be '
222                                'ignored.' % (parttype, command))
223             else:
224                 (meshGroupList, newparams) = self._extractMeshGroups(command)
225                 for meshGroup in meshGroupList:
226                     # Get the geometrical primitive object
227                     if meshGroup.startswith('/'):
228                         groupSObj = self._studyEditor.study.FindObjectByPath(meshGroup)
229                         meshGroup = meshGroup.split('/')[-1]
230                         pass
231                     else:
232                         groupSObj = self._studyEditor.study.FindObject(meshGroup)
233                         pass
234                     groupGeomObj = None
235                     if groupSObj is not None:
236                         groupGeomObj = \
237                                 self._studyEditor.getOrLoadObject(groupSObj)
238                     if groupGeomObj is None:
239                         logger.error("Can't get geom object corresponding to "
240                                      'mesh group "%s", structural element '
241                                      "part %s will not be built." %
242                                      (meshGroup, command))
243                         continue
244                     
245                     # Create the part
246                     try:
247                         part = parts.__dict__[parttype](meshGroup,
248                                                         groupGeomObj, newparams)
249                         element.addPart(part)
250                     except InvalidParameterError, e:
251                         logger.error("Invalid parameter error: %s" % e)
252                         raise
253                     except:
254                         logger.exception("Can't create structural element"
255                                          " part with command %s." %
256                                          str(command))
257
258         # Orientations are parsed after the parts because they must be
259         # associated with existing parts.
260         for command in orientationCmdList:
261             (parttype, parameters) = command
262             (meshGroupList, orientParams) = self._extractMeshGroups(command)
263             for meshGroup in meshGroupList:
264                 element.addOrientation(meshGroup, orientParams)
265
266         element.build()
267         logger.debug("StructuralElementManager.createElement: END")
268         return element
269    
270     ## This method extracts the names of the mesh groups (i.e. the
271     #  geometrical objects used to build the structural element part) in the
272     #  command in parameter. It returns a tuple containing the mesh groups as
273     #  a list of strings and the other parameters of the command as a new
274     #  dictionary. 
275     def _extractMeshGroups(self, command):
276         """
277         This method extracts the names of the mesh groups (i.e. the
278         geometrical objects used to build the structural element part) in the
279         command in parameter. It returns a tuple containing the mesh groups as
280         a list of strings and the other parameters of the command as a new
281         dictionary.
282         """
283         (parttype, parameters) = command
284         newparams = parameters.copy()
285         groupMailleParam = newparams.pop("Group_Maille", None)
286         meshGroupParam = newparams.pop("MeshGroups", None)
287         if groupMailleParam is None and meshGroupParam is None:
288             logger.warning("No mesh group specified in command %s, this "
289                            "command will be ignored." % command)
290             return ([], newparams)
291         elif groupMailleParam is not None and meshGroupParam is not None:
292             logger.warning('Both "MeshGroups" and "Group_Maille" specified in'
293                            ' command %s, only "MeshGroups" will be used.' %
294                            command)
295         elif groupMailleParam is not None and meshGroupParam is None:
296             meshGroupParam = groupMailleParam
297         
298         if isinstance(meshGroupParam, types.StringTypes):
299             meshGroupList = [meshGroupParam]
300         else:
301             meshGroupList = meshGroupParam
302         
303         if len(meshGroupList) == 0:
304             logger.warning("Mesh group list is empty in command %s, this "
305                            "command will be ignored." % command)
306
307         return (meshGroupList, newparams)
308
309
310 ## This class represents a structural element, i.e. a set of geometrical
311 #  objects built along geometrical primitives. Structural elements are 
312 #  normally created by the class StructuralElementManager, so this class 
313 #  should not be instantiated directly in the general case.
314 #  \ingroup structelem
315 class StructuralElement:
316     """
317     This class represents a structural element, i.e. a set of geometrical
318     objects built along geometrical primitives. Structural elements 
319     are normally created by the class :class:`StructuralElementManager`, 
320     so this class should not be instantiated directly in the general case.
321     """
322     _counter = 1
323
324     MAIN_FOLDER_NAME = "Structural Elements"
325
326     def __init__(self):
327         # _parts is the dictionary mapping group name to structural element
328         # part. _shapeDict is the dictionary mapping SubShapeID objects to
329         # structural element parts. Both are used to avoid duplicate shapes
330         # in structural elements.
331         self._parts = {}
332         self._shapeDict = {}
333         self._id = StructuralElement._counter
334         StructuralElement._counter += 1
335         self._studyEditor = getStudyEditor()
336         logger.debug("Creating structural element in study")
337         self._SObject = None
338
339     ## Find or create the study object corresponding to the structural
340     #  element. This object is a Geom Folder named "SE_N" where N is a
341     #  numerical ID. 
342     def _getSObject(self):
343         """
344         Find or create the study object corresponding to the structural
345         element. This object is a Geom Folder named "SE_N" where N is a
346         numerical ID. 
347         """
348         if self._SObject is None:
349             geompy = getGeompy()
350             geomComponent = self._studyEditor.study.FindComponent("GEOM")
351             mainFolder = self._studyEditor.findItem(geomComponent,
352                                                     name = StructuralElement.MAIN_FOLDER_NAME,
353                                                     typeId=999)
354             if mainFolder is None:
355                 mainFolder = geompy.NewFolder(StructuralElement.MAIN_FOLDER_NAME)
356             self._SObject = geompy.NewFolder("SE_" + str(self._id), mainFolder)
357         return self._SObject
358
359     ## Add a part to the structural element.
360     #
361     #  \param newpart (StructuralElementPart) the part to add to the structural element.
362     def addPart(self, newpart):
363         """
364         Add a part to the structural element.
365
366         :type  newpart: :class:`~parts.StructuralElementPart`
367         :param newpart: the part to add to the structural element.
368
369         """
370         newshapes = newpart.baseShapesSet
371
372         # Check duplicate groups
373         if self._parts.has_key(newpart.groupName):
374             logger.warning('Mesh group "%s" is used several times in the '
375                            'structural element. Only the last definition '
376                            'will be used.' % newpart.groupName)
377         else:
378             # Check duplicate shapes
379             intersect = newshapes.intersection(self._shapeDict.keys())
380             while len(intersect) > 0:
381                 shape, = intersect
382                 oldpartwithshape = self._shapeDict[shape]
383                 oldpartshapes = oldpartwithshape.baseShapesSet
384                 intersectwitholdpart = intersect.intersection(oldpartshapes)
385                 logger.warning('Some shapes are common to groups "%s" and '
386                                '"%s". For those, the parameters defined for '
387                                '"%s" will be used.' %
388                                (oldpartwithshape.groupName, newpart.groupName,
389                                 newpart.groupName))
390                 oldpartwithshape.baseShapesSet = \
391                                 oldpartshapes.difference(intersectwitholdpart)
392                 intersect = intersect.difference(intersectwitholdpart)
393
394         # Finally add the new part in the structural element
395         self._parts[newpart.groupName] = newpart
396         for shape in newshapes:
397             self._shapeDict[shape] = newpart
398
399     ## Add orientation information to a part in the structural element. This
400     #  information will be used to build the corresponding markers.
401     #
402     #  \param meshGroup (string) the name of a geometrical primitive. The orientation
403     #  information will apply to the structural element part built along this primitive.
404     #  \param orientParams (dictionary) parameters defining the orientation of the
405     #  structural element part. Those parameters are detailed in class orientation.Orientation1D.
406     def addOrientation(self, meshGroup, orientParams):
407         """
408         Add orientation information to a part in the structural element. This
409         information will be used to build the corresponding markers.
410
411         :type  meshGroup: string
412         :param meshGroup: the name of a geometrical primitive. The orientation
413                           information will apply to the structural element
414                           part built along this primitive.
415
416         :type  orientParams: dictionary
417         :param orientParams: parameters defining the orientation of the
418                              structural element part. Those parameters are
419                              detailed in class
420                              :class:`~orientation.Orientation1D`.
421
422         """
423         if self._parts.has_key(meshGroup):
424             self._parts[meshGroup].addOrientation(orientParams)
425         else:
426             logger.warning('Mesh group "%s" not found in structural element, '
427                            'cannot set orientation.' % meshGroup)
428
429     ## Build the geometric shapes and the markers corresponding to the
430     #  different parts of the structural element, and add them to the study.
431     def build(self):
432         """
433         Build the geometric shapes and the markers corresponding to the
434         different parts of the structural element, and add them to the study.
435         """
436         gg = salome.ImportComponentGUI("GEOM")
437         geompy = getGeompy()
438         for part in self._parts.itervalues():
439             # Build the structural element part
440             logger.debug("Building %s" % part)
441             try:
442                 (shape, markers) = part.build()
443                 if shape is None:
444                     logger.error("Part %s has not been built" % part)
445                     continue
446             except:
447                 logger.exception("Couldn't build part %s" % part)
448                 continue
449             
450             # Add the new objects to the study
451             shapeSObjName = part.name + "_" + part.groupName
452             geompy.addToStudy(shape, shapeSObjName)
453             geompy.PutToFolder(shape, self._getSObject())
454
455             if markers is not None and len(markers) > 0:
456                 for i, marker in enumerate(markers, start = 1):
457                     markerSObjName = "Orient_" + shapeSObjName
458                     if len(markers) > 1:
459                         markerSObjName += "_%d" % i
460                     geompy.addToStudy(marker, markerSObjName)
461                     geompy.PutToFolder(marker, self._getSObject())
462
463     ## Display the structural element in the geom view.
464     def display(self):
465         """
466         Display the structural element in the geom view.
467         """
468         StructuralElement.showElement(self._SObject)
469
470     @staticmethod
471     ## Display the structural element corresponding to the study object \b theSObject
472     def showElement(theSObject):
473         """
474         Display the structural element corresponding to the study object
475         `theSObject`
476         """
477         if theSObject is not None:
478             gg = salome.ImportComponentGUI("GEOM")
479             useCaseBuilder = salome.myStudy.GetUseCaseBuilder()
480             editor = getStudyEditor()
481             aIterator = useCaseBuilder.GetUseCaseIterator(theSObject)
482             aIterator.Init(False)
483             while aIterator.More():
484                 sobj = aIterator.Value()
485                 icon = editor.getIcon(sobj)
486                 if icon != "ICON_OBJBROWSER_LCS":
487                     entry = aIterator.Value().GetID()
488                     gg.createAndDisplayGO(entry)
489                     gg.setDisplayMode(entry, 2) # Shading + edges
490                 aIterator.Next()
491
492
493 def TEST_CreateGeometry():
494     import salome
495     salome.salome_init()
496     import GEOM
497     from salome.geom import geomBuilder
498     geompy = geomBuilder.New()
499     import SALOMEDS
500     geompy.init_geom()
501     Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200)
502     edges = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["EDGE"])
503     geompy.addToStudy(Box_1, "Box_1")
504     for i in range(len(edges)):
505         geompy.addToStudyInFather(Box_1, edges[i], "Edge_%d" % i)
506     faces = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["FACE"])
507     faces[3].SetColor(SALOMEDS.Color(1.0,0.5,0.0))
508     faces[4].SetColor(SALOMEDS.Color(0.0,1.0,0.5))
509     for i in range(len(faces)):
510         geompy.addToStudyInFather(Box_1, faces[i], "Face_%d" % i)
511     Cylinder_1 = geompy.MakeCylinderRH(50, 200)
512     geompy.TranslateDXDYDZ(Cylinder_1, 300, 300, 0)
513     cyl_faces = geompy.SubShapeAllSorted(Cylinder_1, geompy.ShapeType["FACE"])
514     geompy.addToStudy(Cylinder_1, "Cylinder_1")
515     for i in range(len(cyl_faces)):
516         geompy.addToStudyInFather(Cylinder_1, cyl_faces[i], "CylFace_%d" % i)
517     Cylinder_2 = geompy.MakeTranslation(Cylinder_1, 100, 100, 0)
518     cyl_faces2 = geompy.SubShapeAllSorted(Cylinder_2,
519                                           geompy.ShapeType["FACE"])
520     geompy.addToStudy(Cylinder_2, "Cylinder_2")
521     for i in range(len(cyl_faces2)):
522         geompy.addToStudyInFather(Cylinder_2, cyl_faces2[i],
523                                   "CylFace2_%d" % i)
524
525
526 def TEST_StructuralElement():
527     salome.salome_init()
528     TEST_CreateGeometry()
529     liste_commandes = [('Orientation', {'MeshGroups': 'Edge_4',
530                                         'VECT_Y': (1.0, 0.0, 1.0)}),
531                        ('Orientation', {'MeshGroups': 'Edge_5',
532                                         'ANGL_VRIL': 45.0}),
533                        ('GeneralBeam', {'MeshGroups': ['Edge_1', 'Edge_7'],
534                                         'A': 1, 'IY1': 20, 'IY2': 40,
535                                         'IZ1': 60, 'IZ2': 30}),
536                        ('VisuPoutreCercle', {'MeshGroups': ['Edge_6'],
537                                              'R1': 30, 'R2': 20}),
538                        ('CircularBeam', {'MeshGroups': ['Edge_2', 'Edge_3'],
539                                          'R': 40, 'EP': 20}),
540                        ('RectangularBeam', {'MeshGroups': ['Edge_4', 'Edge_5'],
541                                             'HZ1': 60, 'HY1': 40,
542                                             'EPZ1': 15, 'EPY1': 10,
543                                             'HZ2': 40, 'HY2': 60,
544                                             'EPZ2': 10, 'EPY2': 15}),
545                        ('VisuCable', {'MeshGroups': 'Edge_7', 'R': 5}),
546                        ('VisuCoque', {'MeshGroups': 'Face_4',
547                                       'Epais': 10, 'Excentre': 5,
548                                       'angleAlpha': 45, 'angleBeta': 60}),
549                        ('VisuCoque', {'MeshGroups': 'CylFace_2', 'Epais': 5}),
550                        ('VisuGrille', {'MeshGroups': 'Face_5', 'Excentre': 5,
551                                        'angleAlpha': 45, 'angleBeta': 60}),
552                        ('VisuGrille', {'MeshGroups': 'CylFace2_2',
553                                        'Excentre': 5, 'origAxeX': 400,
554                                        'origAxeY': 400, 'origAxeZ': 0,
555                                        'axeX': 0, 'axeY': 0, 'axeZ': 100}),
556                       ]
557
558     structElemManager = StructuralElementManager()
559     elem = structElemManager.createElement(liste_commandes)
560     if salome.hasDesktop():
561         elem.display()
562         salome.sg.updateObjBrowser()
563
564
565 # Main function only used to test the module
566 if __name__ == "__main__":
567     TEST_StructuralElement()