1+ # ice mesher 2D
2+ import Converter .PyTree as C
3+ import Transform .PyTree as T
4+ import Generator .PyTree as G
5+ import Post .PyTree as P
6+ import Geom .PyTree as D
7+ import KCore .Vector as Vector
8+ import Converter .Internal as Internal
9+ import numpy
10+
11+ #===============================================================================================
12+ # verifie l'order de profile (1D STRUCT)
13+ # retourne 1 si normales exterieures et 0 sinon
14+ #===============================================================================================
15+ def checkOrder (profile ):
16+ distrib = D .line ((0 ,0 ,0 ), (0.01 ,0 ,0 ), N = 2 )
17+ lay = G .addNormalLayers (profile , distrib , niter = 0 , check = False )
18+ lay1 = T .subzone (lay , (1 ,- 1 ,1 ), (- 1 ,- 1 ,1 ))
19+ l0 = D .getLength (profile )
20+ l1 = D .getLength (lay1 )
21+ if l1 > l0 : return 1
22+ else : return 0
23+
24+ #===============================================================================================
25+ # IN: BAR1: profile with ice, correctly meshed, in XY plane, simple loop, no branching
26+ # IN: BAR3: outer domain boundary, correctly meshed, in XY plane, simple curve with no branching
27+ # IN: ht: total height of boundary layer mesh
28+ # IN: hf: height of first wall cell
29+ # OUT: mesh: ready for coda
30+ #===============================================================================================
31+ def mesher (BAR1 , BAR3 , ht , hf , extruder = 0 ):
32+
33+ #===============
34+ # BAR1 (profile)
35+ #===============
36+ BAR1 = C .convertArray2Tetra (BAR1 )
37+ profile = C .initVars (BAR1 , 'CoordinateZ' , 0. ) # force xy
38+ profile = C .convertBAR2Struct (profile ) # profile is STRUCT
39+ # check order
40+ order = checkOrder (profile )
41+ print ('order of profile (BAR1)' , order )
42+ # reverse order
43+ if order == 0 : T ._reorder (profile , (- 1 ,2 ,3 ))
44+ C .convertPyTree2File (profile , 'profileOrdered.cgns' )
45+
46+ #================
47+ # BAR3 (exterior)
48+ #================
49+ BAR3 = C .convertBAR2Struct (BAR3 )
50+ BAR3 = C .initVars (BAR3 , 'CoordinateZ' , 0. ) # force xy
51+ order = checkOrder (BAR3 )
52+ print ('order if exterior (BAR3)' , order )
53+ if order == 0 : T ._reorder (BAR3 , (- 1 ,2 ,3 ))
54+ BAR3 = C .convertArray2Tetra (BAR3 )
55+ C .convertPyTree2File (BAR3 , 'exterior.cgns' )
56+
57+ #====================
58+ # Boundary layer mesh - avoid addNL fail a automatiser
59+ #====================
60+ G ._getVolumeMap (profile )
61+ hmean = C .getMeanValue (profile , 'centers:vol' )
62+ nLayers = int (ht / hmean )
63+ nLayers = max (nLayers , 10 )
64+ nLayers = min (nLayers , 100 )
65+
66+ # choice extruder
67+ if extruder == 0 :
68+ # version addNormalLayer
69+ print ("extruder addNormalLayers" )
70+ # number of iteration of normal smoothing
71+ niter = 100
72+ distrib = D .line ((0 ,0 ,0 ), (ht ,0 ,0 ), N = nLayers )
73+ goOn = True
74+ while goOn :
75+ bl = G .addNormalLayers (profile , distrib , niter = niter , check = True )
76+ zoneDim = Internal .getZoneDim (bl )
77+ nk = zoneDim [2 ]
78+ if nk < nLayers :
79+ print ('addNL was stopped %d out of %d' % (nk , nLayers ))
80+ niter += 200 ; #nLayers += 10
81+ if niter > 1000 : goOn = False
82+ else : goOn = False
83+ T ._reorder (bl , (- 1 ,2 ,3 ))
84+ else :
85+ # version hyper2D
86+ print ("extruder hyper2D" )
87+ zoneDim = Internal .getZoneDim (profile )
88+ distrib = G .cart ((0 ,0 ,0 ), (1 ,ht / (nLayers - 1 ),1 ), (zoneDim [1 ],nLayers ,1 ))
89+ profile2 = T .reorder (profile , (- 1 ,2 ,3 ))
90+ bl = G .hyper2D (profile2 , distrib , "O" , etaStart = 5 , etaEnd = 100 , beta = 0. , forced = True )
91+ C .convertPyTree2File (bl , 'bl.cgns' )
92+
93+ # k-remeshing - weakness: no control of outer cell height
94+ #d = D.line((0,0,0), (1,0,0), N=40)
95+ #d = G.enforcePlusX(d, hf/ht, 40, 50)
96+ d = G .cartr2 ((0 ,0 ,0 ), (hf ,1 ,1 ), (1.1 ,1.1 ,1.1 ), (ht ,1 ,1 ))
97+ d = T .subzone (d , (1 ,1 ,1 ), (- 1 ,1 ,1 ))
98+ C ._initVars (d , '{CoordinateX}={CoordinateX}/%g' % ht )
99+ bl = G .map (bl , d , dir = 2 )
100+ C .convertPyTree2File (bl , 'bl2.cgns' )
101+
102+ # conversion en quad
103+ bl = C .convertArray2Hexa (bl )
104+ bl = G .close (bl )
105+
106+ #=========================
107+ # exterior of bl mesh - OK
108+ #=========================
109+ ext = P .exteriorFaces (bl )
110+ ext = T .splitConnexity (ext )
111+ ext = ext [1 ] # always true
112+ T ._reorder (ext , (- 1 ,))
113+
114+ #=============
115+ # T3 mesh - OK
116+ #=============
117+ borders = T .join (ext , BAR3 )
118+ C .convertPyTree2File (borders , 'borders.cgns' )
119+ m2 = G .T3mesher2D (borders , triangulateOnly = 0 , grading = 1.1 , metricInterpType = 0 )
120+ T ._reorder (m2 , (1 ,))
121+
122+ # All mesh
123+ m = [bl , m2 ]
124+
125+ # addkplane
126+ dz = 1.e-6
127+ T ._addkplane (m )
128+ T ._contract (m , (0 ,0 ,0 ), (1 ,0 ,0 ), (0 ,1 ,0 ), dz )
129+
130+ # merge in one zone
131+ m2 = C .mergeConnectivity (m [0 ], m [1 ])
132+
133+ #==========
134+ # BCs - OK
135+ #==========
136+ e0 = P .exteriorFaces (m )
137+ e0 = T .breakElements (e0 )
138+
139+ # recover BAR1
140+ e1 = T .addkplane (BAR1 )
141+ T ._contract (e1 , (0 ,0 ,0 ), (1 ,0 ,0 ), (0 ,1 ,0 ), dz )
142+ e1 [0 ] = 'wall'
143+ C ._addBC2Zone (m2 , 'wall' , 'BCWall' , subzone = e1 )
144+
145+ # recover BAR3
146+ e3 = T .addkplane (BAR3 )
147+ T ._contract (e3 , (0 ,0 ,0 ), (1 ,0 ,0 ), (0 ,1 ,0 ), dz )
148+ e3 [0 ] = 'far'
149+ C ._addBC2Zone (m2 , 'far' , 'BCFarfield' , subzone = e3 )
150+
151+ # recover symmetry planes
152+ es1 = P .selectCells (e0 , '{CoordinateZ}<%g' % (0.5 * dz ), strict = True )
153+ es2 = P .selectCells (e0 , '{CoordinateZ}>%g' % (dz - 0.5 * dz ), strict = True )
154+
155+ c = 0
156+ for e in es1 + es2 :
157+ if C .getNCells (e ) > 0 :
158+ e [0 ] = 'sym%d' % c
159+ C ._addBC2Zone (m2 , 'sym%d' % c , 'BCSymmetryPlane' , subzone = e ); c += 1
160+
161+ return m2
162+
163+ #============================================================================
164+ # IN: a: 1D struct or BAR (input geometry)
165+ # IN: hx: mean h step to apply on profile
166+ # IN: hmin/hmax: minimum and maximum h
167+ # IN: fp: factor for peaks (pics)
168+ # IN: ft: factor for throughs (creux)
169+ # OUT: 1D struct remeshed, ready for extrusion and consSmooth
170+ #============================================================================
171+ def mapper (BAR1 , hmin , hmax , hx , fp = 0.5 , ft = 2. ):
172+
173+ BAR1 = C .convertArray2Tetra (BAR1 )
174+ profile = C .initVars (BAR1 , 'CoordinateZ' , 0. ) # force xy
175+ profile = C .convertBAR2Struct (profile ) # profile is STRUCT
176+ # check order
177+ order = checkOrder (profile )
178+ if order == 0 : T ._reorder (profile , (- 1 ,2 ,3 ))
179+
180+ # closed?
181+ x0 , y0 , z0 = C .getValue (profile , 'GridCoordinates' , 0 )
182+ x1 , y1 , z1 = C .getValue (profile , 'GridCoordinates' , - 1 )
183+ nn = Vector .norm ( (x1 - x0 ,y1 - y0 ,z1 - z0 ))
184+ closed = False
185+ if nn < 1.e-10 : closed = True
186+ print ("closed" , closed )
187+
188+ # split
189+ bs = T .splitSharpEdges (profile , alphaRef = 30. )
190+ for c , b in enumerate (bs ):
191+ bs [c ] = C .convertBAR2Struct (b )
192+
193+ # compute left and right vectors
194+ dxLeft = []; dxRight = []
195+ for b in bs :
196+ # left
197+ x0 , y0 , z0 = C .getValue (b , 'GridCoordinates' , 0 )
198+ x1 , y1 , z1 = C .getValue (b , 'GridCoordinates' , 1 )
199+ dxLeft .append ((x1 - x0 ,y1 - y0 ,z1 - z0 ))
200+
201+ # right
202+ x0 , y0 , z0 = C .getValue (b , 'GridCoordinates' , - 1 )
203+ x1 , y1 , z1 = C .getValue (b , 'GridCoordinates' , - 2 )
204+ dxRight .append ((x1 - x0 ,y1 - y0 ,z1 - z0 ))
205+
206+ # compute angle left
207+ angLeft = []
208+ l = len (bs )
209+ for c , b in enumerate (bs ):
210+ if c == 0 :
211+ dx1 = dxLeft [c ]
212+ if closed : dx2 = dxRight [l - 1 ]
213+ else : dx2 = (- dx1 [0 ],- dx1 [1 ],- dx1 [2 ])
214+ else :
215+ dx1 = dxLeft [c ]
216+ dx2 = dxRight [c - 1 ]
217+
218+ h1 = Vector .norm (dx1 )
219+ h2 = Vector .norm (dx2 )
220+ co = Vector .dot (dx1 ,dx2 )
221+ co = co / (h1 * h2 )
222+ si = Vector .cross (dx1 ,dx2 )
223+ si = si [2 ]/ (h1 * h2 )
224+ asi = numpy .asin (si )
225+ aco = numpy .acos (co )
226+ if asi > 0 : angle = aco # pic
227+ else : angle = - aco # creux
228+ angLeft .append (angle )
229+
230+ # angle right
231+ angRight = []
232+ for c , b in enumerate (bs ):
233+ if c < l - 1 : angle = angLeft [c + 1 ]
234+ else :
235+ if closed : angle = angLeft [0 ]
236+ else : angle = numpy .pi
237+ angRight .append (angle )
238+
239+ # creux: angle > 0, pointe: angle < 0
240+ for c , b in enumerate (bs ):
241+ print (c , 'angleLeft=' , angLeft [c ]* 180. / numpy .pi , 'angleRight=' , angRight [c ]* 180. / numpy .pi )
242+
243+ # set h
244+ out = []
245+ critc = 100. * numpy .pi / 180.
246+ critp = - 100. * numpy .pi / 180.
247+ for c , b in enumerate (bs ):
248+ h1 = hx
249+ h2 = hx
250+
251+ if angLeft [c ] > 0 and angLeft [c ] < critc : # creux critic
252+ h1 = h1 * ft
253+ elif angLeft [c ] < 0 and angLeft [c ] > critp : # pic critic
254+ h1 = h1 * fp
255+ if angRight [c ] > 0 and angRight [c ] < critc : # creux critic
256+ h2 = h2 * ft
257+ elif angRight [c ] < 0 and angRight [c ] > critp : # pic critic
258+ h2 = h2 * fp
259+
260+ h1 = min (h1 , hmax )
261+ h1 = max (h1 , hmin )
262+ h2 = min (h2 , hmax )
263+ h2 = max (h2 , hmin )
264+
265+ print (c , 'h1=' , h1 , 'h2=' , h2 )
266+ d = D .distrib2 (b , h1 , h2 , normalized = True , algo = 1 )
267+ out .append (G .map (b , d ))
268+ all = T .join (out )
269+ C .convertPyTree2File (all , 'profileMapped.cgns' )
270+ return all
271+
272+ #==============================
273+ # check quality of output mesh
274+ #==============================
275+ def checkQuality (m ):
276+ # check positive volumes
277+ G ._getVolumeMap (m )
278+ volmin = C .getMinValue (m , 'centers:vol' )
279+ volmax = C .getMaxValue (m , 'centers:vol' )
280+ volmean = C .getMeanValue (m , 'centers:vol' )
281+ if volmin > 0 : print ("INFO: all positive volumes." )
282+ else : print ("Warning: negative volume detected in final mesh." )
283+
284+ return 0
285+
286+ #===============================================
287+ # check deviation of profile from input profile
288+ #===============================================
289+ def checkDeviation (m ):
290+ return 0.
291+
292+ def checkArea (m ):
293+ return 0
0 commit comments