Salome HOME
Merge branch 'occ/shaper2smesh'
[modules/smesh.git] / src / SMESH_SWIG / smesh_algorithm.py
1 # Copyright (C) 2007-2019  CEA/DEN, EDF R&D, OPEN CASCADE
2 #
3 # This library is free software; you can redistribute it and/or
4 # modify it under the terms of the GNU Lesser General Public
5 # License as published by the Free Software Foundation; either
6 # version 2.1 of the License, or (at your option) any later version.
7 #
8 # This library is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 # Lesser General Public License for more details.
12 #
13 # You should have received a copy of the GNU Lesser General Public
14 # License along with this library; if not, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
16 #
17 # See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
18 #
19
20 ## @package smesh_algorithm
21 #  Python API for base Mesh_Algorithm class.
22 #  This package is a part of SALOME %Mesh module Python API
23
24 import salome
25 from salome.geom import geomBuilder
26 import SMESH, StdMeshers
27
28 class Mesh_Algorithm:
29     """
30     The base class to define meshing algorithms
31
32     Note:
33         This class should not be used directly, it is supposed to be sub-classed
34         for implementing Python API for specific meshing algorithms
35
36     For each meshing algorithm, a python class inheriting from class *Mesh_Algorithm*
37     should be defined. This descendant class should have two attributes defining the way
38     it is created by class :class:`~smeshBuilder.Mesh` (see e.g. class :class:`~StdMeshersBuilder.StdMeshersBuilder_Segment`):
39
40     - :code:`meshMethod` attribute defines name of method of class :class:`~smeshBuilder.Mesh` by calling which the
41       python class of algorithm is created; this method is dynamically added to the :class:`~smeshBuilder.Mesh` class
42       in runtime. For example, if in :code:`class MyPlugin_Algorithm` this attribute is defined as::
43
44           meshMethod = "MyAlgorithm"
45
46       then an instance of :code:`MyPlugin_Algorithm` can be created by the direct invocation of the function
47       of :class:`~smeshBuilder.Mesh` class::
48     
49           my_algo = mesh.MyAlgorithm()
50     
51     - :code:`algoType` defines type of algorithm and is used mostly to discriminate
52       algorithms that are created by the same method of class :class:`~smeshBuilder.Mesh`. For example, if this attribute
53       is specified in :code:`MyPlugin_Algorithm` class as::
54     
55           algoType = "MyPLUGIN"
56
57       then it's creation code can be::
58
59           my_algo = mesh.MyAlgorithm(algo="MyPLUGIN")
60     """
61     
62     
63     def __init__(self):
64         """
65         Private constructor
66         """
67         self.mesh = None
68         self.geom = None
69         self.subm = None
70         self.algo = None
71         pass
72
73     def FindHypothesis (self, hypname, args, CompareMethod, smeshpyD):
74         """
75         Finds a hypothesis in the study by its type name and parameters.
76         Finds only the hypotheses created in smeshBuilder engine.
77
78         Returns: 
79                 :class:`~SMESH.SMESH_Hypothesis`
80         """
81         study = salome.myStudy
82         if not study: return None
83         #to do: find component by smeshpyD object, not by its data type
84         scomp = study.FindComponent(smeshpyD.ComponentDataType())
85         if scomp is not None:
86             res,hypRoot = scomp.FindSubObject(SMESH.Tag_HypothesisRoot)
87             # Check if the root label of the hypotheses exists
88             if res and hypRoot is not None:
89                 iter = study.NewChildIterator(hypRoot)
90                 # Check all published hypotheses
91                 while iter.More():
92                     hypo_so_i = iter.Value()
93                     attr = hypo_so_i.FindAttribute("AttributeIOR")[1]
94                     if attr is not None:
95                         anIOR = attr.Value()
96                         if not anIOR: continue # prevent exception in orb.string_to_object()
97                         hypo_o_i = salome.orb.string_to_object(anIOR)
98                         if hypo_o_i is not None:
99                             # Check if this is a hypothesis
100                             hypo_i = hypo_o_i._narrow(SMESH.SMESH_Hypothesis)
101                             if hypo_i is not None:
102                                 # Check if the hypothesis belongs to current engine
103                                 if smeshpyD.GetObjectId(hypo_i) > 0:
104                                     # Check if this is the required hypothesis
105                                     if hypo_i.GetName() == hypname:
106                                         # Check arguments
107                                         if CompareMethod(hypo_i, args):
108                                             # found!!!
109                                             return hypo_i
110                                         pass
111                                     pass
112                                 pass
113                             pass
114                         pass
115                     iter.Next()
116                     pass
117                 pass
118             pass
119         return None
120
121     def FindAlgorithm (self, algoname, smeshpyD):
122         """
123         Finds the algorithm in the study by its type name.
124         Finds only the algorithms, which have been created in smeshBuilder engine.
125
126         Returns:
127                 SMESH.SMESH_Algo
128         """
129         study = salome.myStudy
130         if not study: return None
131         #to do: find component by smeshpyD object, not by its data type
132         scomp = study.FindComponent(smeshpyD.ComponentDataType())
133         if scomp is not None:
134             res,hypRoot = scomp.FindSubObject(SMESH.Tag_AlgorithmsRoot)
135             # Check if the root label of the algorithms exists
136             if res and hypRoot is not None:
137                 iter = study.NewChildIterator(hypRoot)
138                 # Check all published algorithms
139                 while iter.More():
140                     algo_so_i = iter.Value()
141                     attr = algo_so_i.FindAttribute("AttributeIOR")[1]
142                     if attr is not None:
143                         anIOR = attr.Value()
144                         if not anIOR: continue # prevent exception in orb.string_to_object()
145                         algo_o_i = salome.orb.string_to_object(anIOR)
146                         if algo_o_i is not None:
147                             # Check if this is an algorithm
148                             algo_i = algo_o_i._narrow(SMESH.SMESH_Algo)
149                             if algo_i is not None:
150                                 # Checks if the algorithm belongs to the current engine
151                                 if smeshpyD.GetObjectId(algo_i) > 0:
152                                     # Check if this is the required algorithm
153                                     if algo_i.GetName() == algoname:
154                                         # found!!!
155                                         return algo_i
156                                     pass
157                                 pass
158                             pass
159                         pass
160                     iter.Next()
161                     pass
162                 pass
163             pass
164         return None
165
166     def GetSubMesh(self):
167         """
168         If the algorithm is global, returns 0; 
169         else returns the :class:`~SMESH.SMESH_subMesh` associated to this algorithm.
170         """
171         return self.subm
172
173     def GetAlgorithm(self):
174         """
175         Returns the wrapped mesher.
176         """
177         return self.algo
178
179     def GetCompatibleHypothesis(self):
180         """
181         Gets the list of hypothesis that can be used with this algorithm
182         """
183         mylist = []
184         if self.algo:
185             mylist = self.algo.GetCompatibleHypothesis()
186         return mylist
187
188     def GetName(self):
189         """
190         Gets the name of the algorithm
191         """
192         from salome.smesh.smeshBuilder import GetName
193         return GetName(self.algo)
194
195     def SetName(self, name):
196         """
197         Sets the name to the algorithm
198         """
199         self.mesh.smeshpyD.SetName(self.algo, name)
200
201     def GetId(self):
202         """
203         Gets the id of the algorithm
204         """
205         return self.algo.GetId()
206
207     def Create(self, mesh, geom, hypo, so="libStdMeshersEngine.so"):
208         """
209         Private method.
210         """
211         if geom is None and mesh.mesh.HasShapeToMesh():
212             raise RuntimeError("Attempt to create " + hypo + " algorithm on None shape")
213         algo = self.FindAlgorithm(hypo, mesh.smeshpyD)
214         if algo is None:
215             algo = mesh.smeshpyD.CreateHypothesis(hypo, so)
216             pass
217         self.Assign(algo, mesh, geom)
218         return self.algo
219
220     def Assign(self, algo, mesh, geom):
221         """
222         Private method
223         """
224         from salome.smesh.smeshBuilder import AssureGeomPublished, TreatHypoStatus, GetName
225         if geom is None and mesh.mesh.HasShapeToMesh():
226             raise RuntimeError("Attempt to create " + algo + " algorithm on None shape")
227         self.mesh = mesh
228         if not geom or geom.IsSame( mesh.geom ):
229             self.geom = mesh.geom
230         else:
231             self.geom = geom
232             AssureGeomPublished( mesh, geom )
233             self.subm = mesh.mesh.GetSubMesh(geom, algo.GetName())
234         self.algo = algo
235         status = mesh.AddHypothesis(self.algo, self.geom)
236         return
237
238     def CompareHyp (self, hyp, args):
239         print("CompareHyp is not implemented for ", self.__class__.__name__, ":", hyp.GetName())
240         return False
241
242     def CompareEqualHyp (self, hyp, args):
243         return True
244
245     def Hypothesis (self, hyp, args=[], so="libStdMeshersEngine.so",
246                     UseExisting=0, CompareMethod="", toAdd=True):
247         """
248         Private method
249         """
250         from salome.smesh.smeshBuilder import TreatHypoStatus, GetName
251         hypo = None
252         if UseExisting:
253             if CompareMethod == "": CompareMethod = self.CompareHyp
254             hypo = self.FindHypothesis(hyp, args, CompareMethod, self.mesh.smeshpyD)
255             pass
256         if hypo is None:
257             hypo = self.mesh.smeshpyD.CreateHypothesis(hyp, so)
258             a = ""
259             s = "="
260             for arg in args:
261                 argStr = str(arg)
262                 if isinstance( arg, geomBuilder.GEOM._objref_GEOM_Object ):
263                     argStr = arg.GetStudyEntry()
264                     if not argStr: argStr = "GEOM_Obj_%s", arg.GetEntry()
265                 if len( argStr ) > 10:
266                     argStr = argStr[:7]+"..."
267                     if argStr[0] == '[': argStr += ']'
268                 a = a + s + argStr
269                 s = ","
270                 pass
271             if len(a) > 50:
272                 a = a[:47]+"..."
273             self.mesh.smeshpyD.SetName(hypo, hyp + a)
274             pass
275         geomName=""
276         if self.geom:
277             geomName = GetName(self.geom)
278         if toAdd:
279             status = self.mesh.mesh.AddHypothesis(self.geom, hypo)
280             TreatHypoStatus( status, GetName(hypo), geomName, 0, self.mesh )
281         return hypo
282
283     def MainShapeEntry(self):
284         """
285         Returns entry of the shape to mesh in the study
286         """
287         if not self.mesh or not self.mesh.GetMesh(): return ""
288         if not self.mesh.GetMesh().HasShapeToMesh(): return ""
289         shape = self.mesh.GetShape()
290         return shape.GetStudyEntry()
291
292     def ViscousLayers(self, thickness, numberOfLayers, stretchFactor,
293                       faces=[], isFacesToIgnore=True,
294                       extrMethod=StdMeshers.SURF_OFFSET_SMOOTH, groupName=""):
295         """
296         Defines "ViscousLayers" hypothesis to give parameters of layers of prisms to build
297         near mesh boundary. This hypothesis can be used by several 3D algorithms:
298         NETGEN 3D, MG-Tetra, Hexahedron(i,j,k)
299
300         Parameters:
301                 thickness: total thickness of layers of prisms
302                 numberOfLayers: number of layers of prisms
303                 stretchFactor: factor (>1.0) of growth of layer thickness towards inside of mesh
304                 faces: list of geometrical faces (or their ids).
305                         Viscous layers are either generated on these faces or not, depending on
306                         the value of **isFacesToIgnore** parameter.
307                 isFacesToIgnore: if *True*, the Viscous layers are not generated on the
308                         faces specified by the previous parameter (**faces**).
309                 extrMethod: extrusion method defines how position of new nodes are found during
310                         prism construction and how creation of distorted and intersecting prisms is
311                         prevented. Possible values are:
312
313                         - StdMeshers.SURF_OFFSET_SMOOTH (default) method extrudes nodes along normal
314                                 to underlying geometrical surface. Smoothing of internal surface of
315                                 element layers can be used to avoid creation of invalid prisms.
316                         - StdMeshers.FACE_OFFSET method extrudes nodes along average normal of
317                                 surrounding mesh faces till intersection with a neighbor mesh face
318                                 translated along its own normal by the layers thickness. Thickness
319                                 of layers can be limited to avoid creation of invalid prisms.
320                         - StdMeshers.NODE_OFFSET method extrudes nodes along average normal of
321                                 surrounding mesh faces by the layers thickness. Thickness of
322                                 layers can be limited to avoid creation of invalid prisms.
323                 groupName: name of a group to contain elements of layers. If not provided,
324                            no group is created. The group is created upon mesh generation.
325                            It can be retrieved by calling
326                            ::
327
328                              group = mesh.GetGroupByName( groupName, SMESH.VOLUME )[0]
329
330         Returns:
331                 StdMeshers.StdMeshers_ViscousLayers hypothesis
332         """
333
334         if not isinstance(self.algo, SMESH._objref_SMESH_3D_Algo):
335             raise TypeError("ViscousLayers are supported by 3D algorithms only")
336         if not "ViscousLayers" in self.GetCompatibleHypothesis():
337             raise TypeError("ViscousLayers are not supported by %s"%self.algo.GetName())
338         if faces and isinstance( faces, geomBuilder.GEOM._objref_GEOM_Object ):
339             faces = [ faces ]
340         if faces and isinstance( faces[0], geomBuilder.GEOM._objref_GEOM_Object ):
341             faceIDs = []
342             for shape in faces:
343                 ff = self.mesh.geompyD.SubShapeAll( shape, self.mesh.geompyD.ShapeType["FACE"] )
344                 for f in ff:
345                     faceIDs.append( self.mesh.geompyD.GetSubShapeID(self.mesh.geom, f))
346             faces = faceIDs
347         hyp = self.Hypothesis("ViscousLayers",
348                               [thickness, numberOfLayers, stretchFactor, faces, isFacesToIgnore],
349                               toAdd=False)
350         hyp.SetTotalThickness( thickness )
351         hyp.SetNumberLayers( numberOfLayers )
352         hyp.SetStretchFactor( stretchFactor )
353         hyp.SetFaces( faces, isFacesToIgnore )
354         hyp.SetMethod( extrMethod )
355         hyp.SetGroupName( groupName )
356         self.mesh.AddHypothesis( hyp, self.geom )
357         return hyp
358
359     def ViscousLayers2D(self, thickness, numberOfLayers, stretchFactor,
360                         edges=[], isEdgesToIgnore=True,  groupName="" ):
361         """
362         Defines "ViscousLayers2D" hypothesis to give parameters of layers of quadrilateral
363         elements to build near mesh boundary. This hypothesis can be used by several 2D algorithms:
364         NETGEN 2D, NETGEN 1D-2D, Quadrangle (mapping), MEFISTO, MG-CADSurf
365
366         Parameters:
367                 thickness: total thickness of layers of quadrilaterals
368                 numberOfLayers: number of layers
369                 stretchFactor: factor (>1.0) of growth of layer thickness towards inside of mesh
370                 edges: list of geometrical edges (or their ids).
371                         Viscous layers are either generated on these edges or not, depending on
372                         the value of **isEdgesToIgnore** parameter.
373                 isEdgesToIgnore: if *True*, the Viscous layers are not generated on the
374                         edges specified by the previous parameter (**edges**).
375                 groupName: name of a group to contain elements of layers. If not provided,
376                         no group is created. The group is created upon mesh generation.
377                         It can be retrieved by calling
378                         ::
379
380                           group = mesh.GetGroupByName( groupName, SMESH.FACE )[0]
381
382         Returns:
383                 StdMeshers.StdMeshers_ViscousLayers2D hypothesis
384         """
385         
386         if not isinstance(self.algo, SMESH._objref_SMESH_2D_Algo):
387             raise TypeError("ViscousLayers2D are supported by 2D algorithms only")
388         if not "ViscousLayers2D" in self.GetCompatibleHypothesis():
389             raise TypeError("ViscousLayers2D are not supported by %s"%self.algo.GetName())
390         if edges and not isinstance( edges, list ) and not isinstance( edges, tuple ):
391             edges = [edges]
392         if edges and isinstance( edges[0], geomBuilder.GEOM._objref_GEOM_Object ):
393             edgeIDs = []
394             for shape in edges:
395                 ee = self.mesh.geompyD.SubShapeAll( shape, self.mesh.geompyD.ShapeType["EDGE"])
396                 for e in ee:
397                     edgeIDs.append( self.mesh.geompyD.GetSubShapeID( self.mesh.geom, e ))
398             edges = edgeIDs
399         hyp = self.Hypothesis("ViscousLayers2D",
400                               [thickness, numberOfLayers, stretchFactor, edges, isEdgesToIgnore],
401                               toAdd=False)
402         hyp.SetTotalThickness(thickness)
403         hyp.SetNumberLayers(numberOfLayers)
404         hyp.SetStretchFactor(stretchFactor)
405         hyp.SetEdges(edges, isEdgesToIgnore)
406         hyp.SetGroupName( groupName )
407         self.mesh.AddHypothesis( hyp, self.geom )
408         return hyp
409
410     def ReversedEdgeIndices(self, reverseList):
411         """
412         Transform a list of either edges or tuples (edge, 1st_vertex_of_edge)
413         into a list acceptable to SetReversedEdges() of some 1D hypotheses
414         """
415         
416         resList = []
417         geompy = self.mesh.geompyD
418         for i in reverseList:
419             if isinstance( i, int ):
420                 s = geompy.GetSubShape(self.mesh.geom, [i])
421                 if s.GetShapeType() != geomBuilder.GEOM.EDGE:
422                     raise TypeError("Not EDGE index given")
423                 resList.append( i )
424             elif isinstance( i, geomBuilder.GEOM._objref_GEOM_Object ):
425                 if i.GetShapeType() != geomBuilder.GEOM.EDGE:
426                     raise TypeError("Not an EDGE given")
427                 resList.append( geompy.GetSubShapeID(self.mesh.geom, i ))
428             elif len( i ) > 1:
429                 e = i[0]
430                 v = i[1]
431                 if not isinstance( e, geomBuilder.GEOM._objref_GEOM_Object ) or \
432                    not isinstance( v, geomBuilder.GEOM._objref_GEOM_Object ):
433                     raise TypeError("A list item must be a tuple (edge, 1st_vertex_of_edge)")
434                 if v.GetShapeType() == geomBuilder.GEOM.EDGE and \
435                    e.GetShapeType() == geomBuilder.GEOM.VERTEX:
436                     v,e = e,v
437                 if e.GetShapeType() != geomBuilder.GEOM.EDGE or \
438                    v.GetShapeType() != geomBuilder.GEOM.VERTEX:
439                     raise TypeError("A list item must be a tuple (edge, 1st_vertex_of_edge)")
440                 vFirst = geompy.GetVertexByIndex( e, 0, False )
441                 tol    = geompy.Tolerance( vFirst )[-1]
442                 if geompy.MinDistance( v, vFirst ) > 1.5*tol:
443                     resList.append( geompy.GetSubShapeID(self.mesh.geom, e ))
444             else:
445                 raise TypeError("Item must be either an edge or tuple (edge, 1st_vertex_of_edge)")
446         return resList
447