Back to index

salome-geom  6.5.0
__init__.py
Go to the documentation of this file.
00001 # -*- coding: utf-8 -*-
00002 #
00003 # Copyright (C) 2007-2012  CEA/DEN, EDF R&D, OPEN CASCADE
00004 #
00005 # This library is free software; you can redistribute it and/or
00006 # modify it under the terms of the GNU Lesser General Public
00007 # License as published by the Free Software Foundation; either
00008 # version 2.1 of the License.
00009 #
00010 # This library is distributed in the hope that it will be useful,
00011 # but WITHOUT ANY WARRANTY; without even the implied warranty of
00012 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00013 # Lesser General Public License for more details.
00014 #
00015 # You should have received a copy of the GNU Lesser General Public
00016 # License along with this library; if not, write to the Free Software
00017 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
00018 #
00019 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
00020 #
00021 """
00022 This package is used to create and visualize structural elements. It contains
00023 three modules:
00024 
00025 * This module :mod:`salome.geom.structelem` defines the main classes
00026   :class:`StructuralElement` and :class:`StructuralElementManager` that can be
00027   directly used to build structural elements.
00028 * The module :mod:`salome.geom.structelem.parts` defines the classes corresponding to
00029   the different parts (beams, grids, etc.) that make up a structural element.
00030   It is used to build the geometric shapes in the structural element.
00031 * The module :mod:`salome.geom.structelem.orientation` defines the classes that are
00032   used to compute the orientation of the structural element parts and to build
00033   the corresponding markers.
00034 
00035 A structural element is a set of geometric shapes (beams, grids, etc.) that
00036 are built semi-automatically along a set of geometric primitives (edges for
00037 instance). They are visualized with the same color as their base primitives in
00038 the geom viewer.
00039 
00040 Structural elements are generally created by the
00041 :class:`StructuralElementManager` class, from a list of commands describing
00042 the element to create.
00043 
00044 Example::
00045 
00046     commandList = [('VisuPoutreGenerale', {'Group_Maille': 'Edge_1'}),
00047                    ('VisuBarreCercle',
00048                     {'R': 30, 'Group_Maille': 'Edge_1', 'EP': 15}),
00049                   ]
00050 
00051     structElemManager = StructuralElementManager()
00052     elem = structElemManager.createElement(commandList)
00053     elem.display()
00054     salome.sg.updateObjBrowser(True)
00055 
00056 """
00057 
00058 import types
00059 
00060 import salome
00061 
00062 from salome.kernel.logger import Logger
00063 from salome.kernel import termcolor
00064 logger = Logger("salome.geom.structelem", color = termcolor.RED)
00065 from salome.kernel.studyedit import getStudyEditor
00066 
00067 __all__ = ["parts", "orientation"]
00068 
00069 from salome.geom.structelem import parts
00070 from salome.geom.structelem.parts import InvalidParameterError
00071 
00072 class StructuralElementManager:
00073     """
00074     This class manages the structural elements in the study. It is used to
00075     create a new structural element from a list of commands. The parameter
00076     `studyId` defines the ID of the study in which the manager will create
00077     structural elements. If it is :const:`None` or not specified, it will use
00078     the ID of the current study as defined by
00079     :func:`salome.kernel.studyedit.getActiveStudyId` function.
00080     """
00081     def __init__(self, studyId = None):
00082         self._studyEditor = getStudyEditor(studyId)
00083 
00084     def createElement(self, commandList):
00085         """
00086         Create a structural element from the list of commands `commandList`.
00087         Each command in this list represent a part of the structural element,
00088         that is a specific kind of shape (circular beam, grid, etc.)
00089         associated with one or several geometrical primitives. A command must
00090         be a tuple. The first element is the structural element part class
00091         name or alias name. The second element is a dictionary containing the
00092         parameters describing the part. Valid class names are all the classes
00093         defined in the module :mod:`~salome.geom.structelem.parts` and inheriting
00094         class :class:`~parts.StructuralElementPart`. There are also several
00095         aliases for backward compatibility. Here is the complete list:
00096         
00097         * :class:`~parts.GeneralBeam`
00098         * :class:`~parts.CircularBeam`
00099         * :class:`~parts.RectangularBeam`
00100         * :class:`~parts.ThickShell`
00101         * :class:`~parts.Grid`
00102 
00103         * :func:`~parts.VisuPoutreGenerale` (alias for
00104           :class:`~parts.GeneralBeam`)
00105         * :func:`~parts.VisuPoutreCercle` (alias for
00106           :class:`~parts.CircularBeam`)
00107         * :func:`~parts.VisuPoutreRectangle` (alias for
00108           :class:`~parts.RectangularBeam`)
00109         * :func:`~parts.VisuBarreGenerale` (alias for
00110           :class:`~parts.GeneralBeam`)
00111         * :func:`~parts.VisuBarreRectangle` (alias for
00112           :class:`~parts.RectangularBeam`)
00113         * :func:`~parts.VisuBarreCercle` (alias for
00114           :class:`~parts.CircularBeam`)
00115         * :func:`~parts.VisuCable` (alias for :class:`~parts.CircularBeam`)
00116         * :func:`~parts.VisuCoque` (alias for :class:`~parts.ThickShell`)
00117         * :func:`~parts.VisuGrille` (alias for :class:`~parts.Grid`)
00118         
00119         * ``Orientation``: This identifier is used to specify the orientation
00120           of one or several 1D structural element parts (i.e. beams). The
00121           parameters are described in class
00122           :class:`~orientation.Orientation1D`.
00123 
00124         The valid parameters in the dictionary depend on the type of the
00125         structural element part, and are detailed in the documentation of
00126         the corresponding class. The only parameter that is common to all the
00127         classes is "MeshGroups" (that can also be named "Group_Maille"). It
00128         defines the name of the geometrical object(s) in the study that will
00129         be used as primitives to build the structural element part. This
00130         parameter can be either a list of strings or a single string with
00131         comma separated names.
00132         """
00133         logger.debug("StructuralElementManager.createElement: START")
00134         logger.debug("Command list: %s" % commandList)
00135 
00136         element = StructuralElement(self._studyEditor.studyId)
00137         orientationCmdList = []
00138         for command in commandList:
00139             (parttype, parameters) = command
00140             if parttype == "Orientation":
00141                 orientationCmdList += [command]
00142             elif parttype not in dir(parts):
00143                 logger.warning('Invalid structural element part name "%s"'
00144                                ' in command %s, this command will be '
00145                                'ignored.' % (parttype, command))
00146             else:
00147                 (meshGroupList, newparams) = self._extractMeshGroups(command)
00148                 for meshGroup in meshGroupList:
00149                     # Get the geometrical primitive object
00150                     groupSObj = self._studyEditor.study.FindObject(meshGroup)
00151                     groupGeomObj = None
00152                     if groupSObj is not None:
00153                         groupGeomObj = \
00154                                 self._studyEditor.getOrLoadObject(groupSObj)
00155                     if groupGeomObj is None:
00156                         logger.error("Can't get geom object corresponding to "
00157                                      'mesh group "%s", structural element '
00158                                      "part %s will not be built." %
00159                                      (groupName, part))
00160                         continue
00161                     
00162                     # Create the part
00163                     try:
00164                         part = parts.__dict__[parttype](
00165                                         self._studyEditor.studyId, meshGroup,
00166                                         groupGeomObj, newparams)
00167                         element.addPart(part)
00168                     except InvalidParameterError, e:
00169                         logger.error("Invalid parameter error: %s" % e)
00170                         raise
00171                     except:
00172                         logger.exception("Can't create structural element"
00173                                          " part with command %s." %
00174                                          str(command))
00175 
00176         # Orientations are parsed after the parts because they must be
00177         # associated with existing parts.
00178         for command in orientationCmdList:
00179             (parttype, parameters) = command
00180             (meshGroupList, orientParams) = self._extractMeshGroups(command)
00181             for meshGroup in meshGroupList:
00182                 element.addOrientation(meshGroup, orientParams)
00183 
00184         element.build()
00185         logger.debug("StructuralElementManager.createElement: END")
00186         return element
00187     
00188     def _extractMeshGroups(self, command):
00189         """
00190         This method extracts the names of the mesh groups (i.e. the
00191         geometrical objects used to build the structural element part) in the
00192         command in parameter. It returns a tuple containing the mesh groups as
00193         a list of strings and the other parameters of the command as a new
00194         dictionary.
00195         """
00196         (parttype, parameters) = command
00197         newparams = parameters.copy()
00198         groupMailleParam = newparams.pop("Group_Maille", None)
00199         meshGroupParam = newparams.pop("MeshGroups", None)
00200         if groupMailleParam is None and meshGroupParam is None:
00201             logger.warning("No mesh group specified in command %s, this "
00202                            "command will be ignored." % command)
00203             return ([], newparams)
00204         elif groupMailleParam is not None and meshGroupParam is not None:
00205             logger.warning('Both "MeshGroups" and "Group_Maille" specified in'
00206                            ' command %s, only "MeshGroups" will be used.' %
00207                            command)
00208         elif groupMailleParam is not None and meshGroupParam is None:
00209             meshGroupParam = groupMailleParam
00210         
00211         meshGroupList = []
00212         if type(meshGroupParam) == types.StringType:
00213             meshGroupList = self._getMeshGroupListFromString(meshGroupParam)
00214         else:
00215             for item in meshGroupParam:
00216                 meshGroupList += self._getMeshGroupListFromString(item)
00217         
00218         if len(meshGroupList) == 0:
00219             logger.warning("Mesh group list is empty in command %s, this "
00220                            "command will be ignored." % command)
00221 
00222         return (meshGroupList, newparams)
00223     
00224     def _getMeshGroupListFromString(self, meshString):
00225         """
00226         This method splits the string in parameter to extract comma separated
00227         names. Those names are returned as a list of strings.
00228         """
00229         meshGroupList = []
00230         list = meshString.split(",")
00231         for item in list:
00232             strippedItem = item.strip()
00233             if len(strippedItem) > 0:
00234                 meshGroupList.append(strippedItem)
00235         return meshGroupList
00236 
00237 
00238 class StructuralElement:
00239     """
00240     This class represents a structural element, i.e. a set of geometrical
00241     objects built along geometrical primitives. The parameter `studyId`
00242     defines the ID of the study that will contain the structural element. If
00243     it is :const:`None` or not specified, the constructor will use the ID of
00244     the active study as defined by :func:`salome.kernel.studyedit.getActiveStudyId`
00245     function. Structural elements are normally created by the class
00246     :class:`StructuralElementManager`, so this class should not be
00247     instantiated directly in the general case.
00248     """
00249     _counter = 1
00250     _mainFolderTag = 14725
00251 
00252     def __init__(self, studyId = None):
00253         # _parts is the dictionary mapping group name to structural element
00254         # part. _shapeDict is the dictionary mapping SubShapeID objects to
00255         # structural element parts. Both are used to avoid duplicate shapes
00256         # in structural elements.
00257         self._parts = {}
00258         self._shapeDict = {}
00259         self._id = StructuralElement._counter
00260         StructuralElement._counter += 1
00261         self._studyEditor = getStudyEditor(studyId)
00262         logger.debug("Creating structural element in study %s" %
00263                      self._studyEditor.studyId)
00264         self._SObject = None
00265 
00266     def _getSObject(self):
00267         """
00268         Find or create the study object corresponding to the structural
00269         element. This object is named "SE_N" where N is a numerical ID. 
00270         """
00271         if self._SObject is None:
00272             geomComponent = self._studyEditor.study.FindComponent("GEOM")
00273             mainFolder = self._studyEditor.setItemAtTag(geomComponent,
00274                                             StructuralElement._mainFolderTag,
00275                                             name = "Structural Elements")
00276             self._SObject = self._studyEditor.findOrCreateItem(mainFolder,
00277                                             name = "SE_" + str(self._id))
00278         return self._SObject
00279 
00280     def addPart(self, newpart):
00281         """
00282         Add a part to the structural element.
00283 
00284         :type  newpart: :class:`~parts.StructuralElementPart`
00285         :param newpart: the part to add to the structural element.
00286 
00287         """
00288         newshapes = newpart.baseShapesSet
00289 
00290         # Check duplicate groups
00291         if self._parts.has_key(newpart.groupName):
00292             logger.warning('Mesh group "%s" is used several times in the '
00293                            'structural element. Only the last definition '
00294                            'will be used.' % newpart.groupName)
00295         else:
00296             # Check duplicate shapes
00297             intersect = newshapes.intersection(self._shapeDict.keys())
00298             while len(intersect) > 0:
00299                 shape, = intersect
00300                 oldpartwithshape = self._shapeDict[shape]
00301                 oldpartshapes = oldpartwithshape.baseShapesSet
00302                 intersectwitholdpart = intersect.intersection(oldpartshapes)
00303                 logger.warning('Some shapes are common to groups "%s" and '
00304                                '"%s". For those, the parameters defined for '
00305                                '"%s" will be used.' %
00306                                (oldpartwithshape.groupName, newpart.groupName,
00307                                 newpart.groupName))
00308                 oldpartwithshape.baseShapesSet = \
00309                                 oldpartshapes.difference(intersectwitholdpart)
00310                 intersect = intersect.difference(intersectwitholdpart)
00311 
00312         # Finally add the new part in the structural element
00313         self._parts[newpart.groupName] = newpart
00314         for shape in newshapes:
00315             self._shapeDict[shape] = newpart
00316 
00317     def addOrientation(self, meshGroup, orientParams):
00318         """
00319         Add orientation information to a part in the structural element. This
00320         information will be used to build the corresponding markers.
00321 
00322         :type  meshGroup: string
00323         :param meshGroup: the name of a geometrical primitive. The orientation
00324                           information will apply to the structural element
00325                           part built along this primitive.
00326 
00327         :type  orientParams: dictionary
00328         :param orientParams: parameters defining the orientation of the
00329                              structural element part. Those parameters are
00330                              detailed in class
00331                              :class:`~orientation.Orientation1D`.
00332 
00333         """
00334         if self._parts.has_key(meshGroup):
00335             self._parts[meshGroup].addOrientation(orientParams)
00336         else:
00337             logger.warning('Mesh group "%s" not found in structural element, '
00338                            'cannot set orientation.' % meshGroup)
00339 
00340     def build(self):
00341         """
00342         Build the geometric shapes and the markers corresponding to the
00343         different parts of the structural element, and add them to the study.
00344         """
00345         gg = salome.ImportComponentGUI("GEOM")
00346         for part in self._parts.itervalues():
00347             # Build the structural element part
00348             logger.debug("Building %s" % part)
00349             try:
00350                 (shape, markers) = part.build()
00351                 if shape is None:
00352                     logger.error("Part %s has not been built" % part)
00353                     continue
00354             except:
00355                 logger.exception("Couldn't build part %s" % part)
00356                 continue
00357             
00358             # Add the new objects to the study
00359             IOR = self._studyEditor.study.ConvertObjectToIOR(shape)
00360             shapeSObjName = part.name + "_" + part.groupName
00361             icon = None
00362             if salome.hasDesktop():
00363                 icon = gg.getShapeTypeIcon(IOR)
00364             shapeSObj = self._studyEditor.createItem(self._getSObject(),
00365                                             name = shapeSObjName, IOR = IOR,
00366                                             icon = icon)
00367             if markers is not None and len(markers) > 0:
00368                 i = 1
00369                 for marker in markers:
00370                     markerIOR = \
00371                             self._studyEditor.study.ConvertObjectToIOR(marker)
00372                     markerSObjName = "Orient_" + shapeSObjName
00373                     if len(markers) > 1:
00374                         markerSObjName += "_%d" % i
00375                     markerSObj = self._studyEditor.createItem(
00376                                                 self._getSObject(),
00377                                                 name = markerSObjName,
00378                                                 IOR = markerIOR,
00379                                                 icon = "ICON_OBJBROWSER_LCS")
00380                     i += 1
00381 
00382     def display(self):
00383         """
00384         Display the structural element in the geom view.
00385         """
00386         StructuralElement.showElement(self._SObject)
00387 
00388     @staticmethod
00389     def showElement(theSObject):
00390         """
00391         Display the structural element corresponding to the study object
00392         `theSObject`
00393         """
00394         if theSObject is not None:
00395             gg = salome.ImportComponentGUI("GEOM")
00396             aStudy = theSObject.GetStudy()
00397             editor = getStudyEditor(aStudy._get_StudyId())
00398             aIterator = aStudy.NewChildIterator(theSObject)
00399             aIterator.Init()
00400             while aIterator.More():
00401                 sobj = aIterator.Value()
00402                 icon = editor.getIcon(sobj)
00403                 if icon != "ICON_OBJBROWSER_LCS":
00404                     entry = aIterator.Value().GetID()
00405                     gg.createAndDisplayGO(entry)
00406                     gg.setDisplayMode(entry, 1)
00407                 aIterator.Next()
00408 
00409 
00410 def TEST_CreateGeometry():
00411     import geompy
00412     import SALOMEDS
00413     geompy.init_geom(salome.myStudy)
00414     Box_1 = geompy.MakeBoxDXDYDZ(200, 200, 200)
00415     edges = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["EDGE"])
00416     geompy.addToStudy(Box_1, "Box_1")
00417     for i in range(len(edges)):
00418         geompy.addToStudyInFather(Box_1, edges[i], "Edge_%d" % i)
00419     faces = geompy.SubShapeAllSorted(Box_1, geompy.ShapeType["FACE"])
00420     faces[3].SetColor(SALOMEDS.Color(1.0,0.5,0.0))
00421     faces[4].SetColor(SALOMEDS.Color(0.0,1.0,0.5))
00422     for i in range(len(faces)):
00423         geompy.addToStudyInFather(Box_1, faces[i], "Face_%d" % i)
00424     Cylinder_1 = geompy.MakeCylinderRH(50, 200)
00425     geompy.TranslateDXDYDZ(Cylinder_1, 300, 300, 0)
00426     cyl_faces = geompy.SubShapeAllSorted(Cylinder_1, geompy.ShapeType["FACE"])
00427     geompy.addToStudy(Cylinder_1, "Cylinder_1")
00428     for i in range(len(cyl_faces)):
00429         geompy.addToStudyInFather(Cylinder_1, cyl_faces[i], "CylFace_%d" % i)
00430     Cylinder_2 = geompy.MakeTranslation(Cylinder_1, 100, 100, 0)
00431     cyl_faces2 = geompy.SubShapeAllSorted(Cylinder_2,
00432                                           geompy.ShapeType["FACE"])
00433     geompy.addToStudy(Cylinder_2, "Cylinder_2")
00434     for i in range(len(cyl_faces2)):
00435         geompy.addToStudyInFather(Cylinder_2, cyl_faces2[i],
00436                                   "CylFace2_%d" % i)
00437 
00438 
00439 def TEST_StructuralElement():
00440     salome.salome_init()
00441     TEST_CreateGeometry()
00442     liste_commandes = [('Orientation', {'MeshGroups': 'Edge_4',
00443                                         'VECT_Y': (1.0, 0.0, 1.0)}),
00444                        ('Orientation', {'MeshGroups': 'Edge_5',
00445                                         'ANGL_VRIL': 45.0}),
00446                        ('GeneralBeam', {'MeshGroups': 'Edge_1, Edge_7',
00447                                         'A': 1, 'IY1': 20, 'IY2': 40,
00448                                         'IZ1': 60, 'IZ2': 30}),
00449                        ('VisuPoutreCercle', {'MeshGroups': ['Edge_6'],
00450                                              'R1': 30, 'R2': 20}),
00451                        ('CircularBeam', {'MeshGroups': ['Edge_2', 'Edge_3'],
00452                                          'R': 40, 'EP': 20}),
00453                        ('RectangularBeam', {'MeshGroups': 'Edge_4, Edge_5',
00454                                             'HZ1': 60, 'HY1': 40,
00455                                             'EPZ1': 15, 'EPY1': 10,
00456                                             'HZ2': 40, 'HY2': 60,
00457                                             'EPZ2': 10, 'EPY2': 15}),
00458                        ('VisuCable', {'MeshGroups': 'Edge_7', 'R': 5}),
00459                        ('VisuCoque', {'MeshGroups': 'Face_4',
00460                                       'Epais': 10, 'Excentre': 5,
00461                                       'angleAlpha': 45, 'angleBeta': 60}),
00462                        ('VisuCoque', {'MeshGroups': 'CylFace_2', 'Epais': 5}),
00463                        ('VisuGrille', {'MeshGroups': 'Face_5', 'Excentre': 5,
00464                                        'angleAlpha': 45, 'angleBeta': 60}),
00465                        ('VisuGrille', {'MeshGroups': 'CylFace2_2',
00466                                        'Excentre': 5, 'origAxeX': 400,
00467                                        'origAxeY': 400, 'origAxeZ': 0,
00468                                        'axeX': 0, 'axeY': 0, 'axeZ': 100}),
00469                       ]
00470 
00471     structElemManager = StructuralElementManager()
00472     elem = structElemManager.createElement(liste_commandes)
00473     if salome.hasDesktop():
00474         elem.display()
00475         salome.sg.updateObjBrowser(True)
00476 
00477 
00478 # Main function only used to test the module
00479 if __name__ == "__main__":
00480     TEST_StructuralElement()