Skip to content

Commit 5b4587f

Browse files
committed
Generator: update hyper2D
1 parent 1067a7a commit 5b4587f

File tree

15 files changed

+602
-227
lines changed

15 files changed

+602
-227
lines changed
Lines changed: 293 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
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

Comments
 (0)