Salome HOME
Merge branch 'V9_9_BR'
[modules/smesh.git] / src / SMESH_SWIG / smesh_algorithm.py
1 # Copyright (C) 2007-2022  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                 try:
344                   ff = self.mesh.geompyD.SubShapeAll( shape, self.mesh.geompyD.ShapeType["FACE"] )
345                   for f in ff:
346                     faceIDs.append( self.mesh.geompyD.GetSubShapeID(self.mesh.geom, f))
347                 except:
348                   # try to get the SHAPERSTUDY engine directly, because GetGen does not work because of
349                   # simplification of access in geomBuilder: omniORB.registerObjref
350                   from SHAPERSTUDY_utils import getEngine
351                   gen = getEngine()
352                   if gen:
353                     aShapeOp = gen.GetIShapesOperations()
354                     ff = aShapeOp.ExtractSubShapes( shape, self.mesh.geompyD.ShapeType["FACE"], False)
355                     for f in ff:
356                       faceIDs.append( aShapeOp.GetSubShapeIndex( self.mesh.geom, f ))
357             faces = faceIDs
358         hyp = self.Hypothesis("ViscousLayers",
359                               [thickness, numberOfLayers, stretchFactor, faces, isFacesToIgnore],
360                               toAdd=False)
361         hyp.SetTotalThickness( thickness )
362         hyp.SetNumberLayers( numberOfLayers )
363         hyp.SetStretchFactor( stretchFactor )
364         hyp.SetFaces( faces, isFacesToIgnore )
365         hyp.SetMethod( extrMethod )
366         hyp.SetGroupName( groupName )
367         self.mesh.AddHypothesis( hyp, self.geom )
368         return hyp
369
370     def ViscousLayers2D(self, thickness, numberOfLayers, stretchFactor,
371                         edges=[], isEdgesToIgnore=True,  groupName="" ):
372         """
373         Defines "ViscousLayers2D" hypothesis to give parameters of layers of quadrilateral
374         elements to build near mesh boundary. This hypothesis can be used by several 2D algorithms:
375         NETGEN 2D, NETGEN 1D-2D, Quadrangle (mapping), MEFISTO, MG-CADSurf
376
377         Parameters:
378                 thickness: total thickness of layers of quadrilaterals
379                 numberOfLayers: number of layers
380                 stretchFactor: factor (>1.0) of growth of layer thickness towards inside of mesh
381                 edges: list of geometrical edges (or their ids).
382                         Viscous layers are either generated on these edges or not, depending on
383                         the value of **isEdgesToIgnore** parameter.
384                 isEdgesToIgnore: if *True*, the Viscous layers are not generated on the
385                         edges specified by the previous parameter (**edges**).
386                 groupName: name of a group to contain elements of layers. If not provided,
387                         no group is created. The group is created upon mesh generation.
388                         It can be retrieved by calling
389                         ::
390
391                           group = mesh.GetGroupByName( groupName, SMESH.FACE )[0]
392
393         Returns:
394                 StdMeshers.StdMeshers_ViscousLayers2D hypothesis
395         """
396         
397         if not isinstance(self.algo, SMESH._objref_SMESH_2D_Algo):
398             raise TypeError("ViscousLayers2D are supported by 2D algorithms only")
399         if not "ViscousLayers2D" in self.GetCompatibleHypothesis():
400             raise TypeError("ViscousLayers2D are not supported by %s"%self.algo.GetName())
401         if edges and not isinstance( edges, list ) and not isinstance( edges, tuple ):
402             edges = [edges]
403         if edges and isinstance( edges[0], geomBuilder.GEOM._objref_GEOM_Object ):
404             edgeIDs = []
405             for shape in edges:
406               try:
407                 ee = self.mesh.geompyD.SubShapeAll( shape, self.mesh.geompyD.ShapeType["EDGE"])
408                 for e in ee:
409                   edgeIDs.append( self.mesh.geompyD.GetSubShapeID( self.mesh.geom, e ))
410               except:
411                 # try to get the SHAPERSTUDY engine directly, because GetGen does not work because of
412                 # simplification of access in geomBuilder: omniORB.registerObjref
413                 from SHAPERSTUDY_utils import getEngine
414                 gen = getEngine()
415                 if gen:
416                   aShapeOp = gen.GetIShapesOperations()
417                   ee = aShapeOp.ExtractSubShapes( shape, self.mesh.geompyD.ShapeType["EDGE"], False)
418                   for e in ee:
419                     edgeIDs.append( aShapeOp.GetSubShapeIndex( self.mesh.geom, e ))
420             edges = edgeIDs
421         hyp = self.Hypothesis("ViscousLayers2D",
422                               [thickness, numberOfLayers, stretchFactor, edges, isEdgesToIgnore],
423                               toAdd=False)
424         hyp.SetTotalThickness(thickness)
425         hyp.SetNumberLayers(numberOfLayers)
426         hyp.SetStretchFactor(stretchFactor)
427         hyp.SetEdges(edges, isEdgesToIgnore)
428         hyp.SetGroupName( groupName )
429         self.mesh.AddHypothesis( hyp, self.geom )
430         return hyp
431
432     def ReversedEdgeIndices(self, reverseList):
433         """
434         Transform a list of either edges or tuples (edge, 1st_vertex_of_edge)
435         into a list acceptable to SetReversedEdges() of some 1D hypotheses
436         """
437         
438         resList = []
439         geompy = self.mesh.geompyD
440         for i in reverseList:
441             if isinstance( i, int ):
442                 s = geompy.GetSubShape(self.mesh.geom, [i])
443
444                 #bos #20082 begin:
445                 if s is None and type(self.geom) != geomBuilder.GEOM._objref_GEOM_Object:
446                     # try to get the SHAPERSTUDY engine directly, as GetGen does not work because of
447                     # simplification of access in geomBuilder: omniORB.registerObjref
448                     from SHAPERSTUDY_utils import getEngine
449                     gen = getEngine()
450                     if gen:
451                         aShapeOp = gen.GetIShapesOperations()
452                         s = aShapeOp.GetSubShape(self.mesh.geom, i)
453                 #bos #20082 end
454
455                 if s.GetShapeType() != geomBuilder.GEOM.EDGE:
456                     raise TypeError("Not EDGE index given")
457                 resList.append( i )
458             elif isinstance( i, geomBuilder.GEOM._objref_GEOM_Object ):
459                 if i.GetShapeType() != geomBuilder.GEOM.EDGE:
460                     raise TypeError("Not an EDGE given")
461                 resList.append( geompy.GetSubShapeID(self.mesh.geom, i ))
462             elif len( i ) > 1:
463                 e = i[0]
464                 v = i[1]
465                 if not isinstance( e, geomBuilder.GEOM._objref_GEOM_Object ) or \
466                    not isinstance( v, geomBuilder.GEOM._objref_GEOM_Object ):
467                     raise TypeError("A list item must be a tuple (edge, 1st_vertex_of_edge)")
468                 if v.GetShapeType() == geomBuilder.GEOM.EDGE and \
469                    e.GetShapeType() == geomBuilder.GEOM.VERTEX:
470                     v,e = e,v
471                 if e.GetShapeType() != geomBuilder.GEOM.EDGE or \
472                    v.GetShapeType() != geomBuilder.GEOM.VERTEX:
473                     raise TypeError("A list item must be a tuple (edge, 1st_vertex_of_edge)")
474                 vFirst = geompy.GetVertexByIndex( e, 0, False )
475                 tol    = geompy.Tolerance( vFirst )[-1]
476                 if geompy.MinDistance( v, vFirst ) > 1.5*tol:
477                     resList.append( geompy.GetSubShapeID(self.mesh.geom, e ))
478             else:
479                 raise TypeError("Item must be either an edge or tuple (edge, 1st_vertex_of_edge)")
480         return resList
481