ringspy 0.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- RingsPy/MeshGenTools.py +2021 -0
- RingsPy/__init__.py +8 -0
- RingsPy/test_cube.py +231 -0
- RingsPy-0.1.0.dist-info/LICENSE +674 -0
- RingsPy-0.1.0.dist-info/METADATA +199 -0
- RingsPy-0.1.0.dist-info/RECORD +8 -0
- RingsPy-0.1.0.dist-info/WHEEL +5 -0
- RingsPy-0.1.0.dist-info/top_level.txt +1 -0
RingsPy/MeshGenTools.py
ADDED
|
@@ -0,0 +1,2021 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
"""
|
|
3
|
+
RingsPy utilities and functions
|
|
4
|
+
|
|
5
|
+
author: king_yin3613
|
|
6
|
+
email: haoyin2022@u.northwestern.edu
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
import math
|
|
10
|
+
import bisect
|
|
11
|
+
import numpy as np
|
|
12
|
+
import scipy.stats
|
|
13
|
+
from scipy.spatial.distance import cdist
|
|
14
|
+
import matplotlib.pyplot as plt
|
|
15
|
+
from scipy.spatial import cKDTree
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
def intersect2D(a, b):
|
|
19
|
+
"""
|
|
20
|
+
Find row intersection between 2D numpy arrays, a and b.
|
|
21
|
+
Returns another numpy array with shared rows
|
|
22
|
+
credit to: https://gist.github.com/Robaina/b742f44f489a07cd26b49222f6063ef7
|
|
23
|
+
"""
|
|
24
|
+
return np.array([x for x in set(tuple(x) for x in a) & set(tuple(x) for x in b)])
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def TriangleArea2D(x1, y1, x2, y2, x3, y3):
|
|
28
|
+
"""
|
|
29
|
+
A utility function to calculate area
|
|
30
|
+
of triangle formed by (x1, y1),
|
|
31
|
+
(x2, y2) and (x3, y3)
|
|
32
|
+
"""
|
|
33
|
+
return abs((x1 * (y2 - y3) + x2 * (y3 - y1) + x3 * (y1 - y2)) / 2.0)
|
|
34
|
+
|
|
35
|
+
def check_point_boundbox(point,log_center,r):
|
|
36
|
+
''' Make sure this point does not protrude outside the bounding circle '''
|
|
37
|
+
within = ( math.sqrt((point[0]-log_center[0])**2 + (point[1]-log_center[1])**2) <= r )
|
|
38
|
+
|
|
39
|
+
return within
|
|
40
|
+
|
|
41
|
+
def check_isinside_boundcircle(circC,log_center,rmin,rmax):
|
|
42
|
+
''' Make sure this circle does not protrude outside the ring's bounding circle '''
|
|
43
|
+
x = circC[0]
|
|
44
|
+
y = circC[1]
|
|
45
|
+
w = circC[2]
|
|
46
|
+
within = ( math.sqrt((x-log_center[0])**2 + (y-log_center[1])**2) > rmin+w ) and ( math.sqrt((x-log_center[0])**2 + (y-log_center[1])**2)<rmax-w )
|
|
47
|
+
|
|
48
|
+
return within
|
|
49
|
+
|
|
50
|
+
def check_isinside_boundTriangle2D(circC,boundaries):
|
|
51
|
+
"""
|
|
52
|
+
A function to check whether point P(x, y)
|
|
53
|
+
lies inside the triangle formed by
|
|
54
|
+
A(x1, y1), B(x2, y2) and C(x3, y3)
|
|
55
|
+
credit to: https://www.geeksforgeeks.org/check-whether-a-given-point-lies-inside-a-triangle-or-not/
|
|
56
|
+
"""
|
|
57
|
+
x = circC[0]
|
|
58
|
+
y = circC[1]
|
|
59
|
+
w = circC[2]
|
|
60
|
+
|
|
61
|
+
x1 = boundaries[0][1][0][0]
|
|
62
|
+
y1 = boundaries[0][1][0][1]
|
|
63
|
+
x2 = boundaries[1][1][0][0]
|
|
64
|
+
y2 = boundaries[1][1][0][1]
|
|
65
|
+
x3 = boundaries[2][1][0][0]
|
|
66
|
+
y3 = boundaries[2][1][0][1]
|
|
67
|
+
|
|
68
|
+
# Calculate area of triangle ABC
|
|
69
|
+
A = TriangleArea2D(x1, y1, x2, y2, x3, y3)
|
|
70
|
+
|
|
71
|
+
# Calculate area of triangle PBC
|
|
72
|
+
A1 = TriangleArea2D(x, y, x2, y2, x3, y3)
|
|
73
|
+
|
|
74
|
+
# Calculate area of triangle PAC
|
|
75
|
+
A2 = TriangleArea2D(x1, y1, x, y, x3, y3)
|
|
76
|
+
|
|
77
|
+
# Calculate area of triangle PAB
|
|
78
|
+
A3 = TriangleArea2D(x1, y1, x2, y2, x, y)
|
|
79
|
+
|
|
80
|
+
# Check if sum of A1, A2 and A3 is same as A
|
|
81
|
+
# within = (A == A1 + A2 + A3)
|
|
82
|
+
# with tolerance
|
|
83
|
+
within = (np.abs(A - (A1 + A2 + A3)) <= A*1e-3)
|
|
84
|
+
|
|
85
|
+
return within
|
|
86
|
+
|
|
87
|
+
def check_isinside_boundbox2D(circC,x_min,x_max,y_min,y_max):
|
|
88
|
+
''' Make sure this circle does not protrude outside the bounding box '''
|
|
89
|
+
x = circC[0]
|
|
90
|
+
y = circC[1]
|
|
91
|
+
w = circC[2]
|
|
92
|
+
within = ( x > x_min+w ) and ( x < x_max-w ) and ( y > y_min+w ) and ( y < y_max-w )
|
|
93
|
+
|
|
94
|
+
return within
|
|
95
|
+
|
|
96
|
+
def check_isinside_boundbox2Dindent(circC,x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max):
|
|
97
|
+
''' Make sure this circle does not protrude outside the indent bounding box '''
|
|
98
|
+
x = circC[0]
|
|
99
|
+
y = circC[1]
|
|
100
|
+
w = circC[2]
|
|
101
|
+
within = ( x > x_min+w ) and ( x < x_max-w ) and ( y > y_min+w ) and ( y < y_max-w )
|
|
102
|
+
within_indent = ( x > x_min+w ) and ( x < x_indent-w ) and ( y > y_indent_min+w ) and ( y < y_indent_max-w )
|
|
103
|
+
within = np.subtract(within,within_indent,dtype=np.float32)
|
|
104
|
+
return within
|
|
105
|
+
|
|
106
|
+
def check_isinside_boundbox2Dprecrack(circC,x_min,x_max,y_min,y_max,x_indent,y_indent,x_precrack,y_precrack):
|
|
107
|
+
''' Make sure this circle does not protrude outside the ring's bounding box '''
|
|
108
|
+
# Neither/nor set operation, ref:https://math.stackexchange.com/questions/1257097/how-to-represent-a-neither-nor-set-operation
|
|
109
|
+
x = circC[0]
|
|
110
|
+
y = circC[1]
|
|
111
|
+
w = circC[2]
|
|
112
|
+
within = ( x > x_min+w ) and ( x < x_max-w ) and ( y > y_min+w ) and ( y < y_max-w )
|
|
113
|
+
within_indent = ( x > x_min+w ) and ( x < x_indent-w ) and ( y > -y_indent+w ) and ( y < y_indent-w )
|
|
114
|
+
within_precrack = ( x > x_indent+w ) and ( x < x_precrack-w ) and ( y > -y_precrack+w ) and ( y < y_precrack-w )
|
|
115
|
+
within_both = within_indent or within_precrack
|
|
116
|
+
within = np.subtract(within,within_both,dtype=np.float32)
|
|
117
|
+
return within
|
|
118
|
+
|
|
119
|
+
def check_isinside_boundHexagon2D(circC,L,box_center):
|
|
120
|
+
''' Make sure this circle does not protrude outside the bounding hexagon
|
|
121
|
+
credit to: http://www.playchilla.com/how-to-check-if-a-point-is-inside-a-hexagon
|
|
122
|
+
'''
|
|
123
|
+
x = np.abs(circC[0] - box_center[0])
|
|
124
|
+
y = np.abs(circC[1] - box_center[1])
|
|
125
|
+
v = L/2
|
|
126
|
+
h = L/np.sqrt(3)/2
|
|
127
|
+
within = ( x < 2*h ) and ( y < v ) and ((2*v*h - x*v - h*y) > 0)
|
|
128
|
+
return within
|
|
129
|
+
|
|
130
|
+
def check_overlap(circles,circC):
|
|
131
|
+
''' Make sure the distance between the current circle's center and all
|
|
132
|
+
other circle centers is greater than or equal to the circle's perimeter (2r)
|
|
133
|
+
'''
|
|
134
|
+
x = circC[0]
|
|
135
|
+
y = circC[1]
|
|
136
|
+
w = circC[2]
|
|
137
|
+
nooverlap = all( (x - c[0])**2 + (y - c[1])**2 >= (w + c[2])**2 for c in circles )
|
|
138
|
+
return nooverlap
|
|
139
|
+
|
|
140
|
+
def check_iscollinear(p1,p2,boundaries):
|
|
141
|
+
''' Make sure the line segment x1-x2 is collinear with
|
|
142
|
+
any line segment of the boundary polygon
|
|
143
|
+
'''
|
|
144
|
+
collinear = 0
|
|
145
|
+
if (p2[0]-p1[0]) == 0: # inf slope
|
|
146
|
+
k1 = 99999999
|
|
147
|
+
else:
|
|
148
|
+
k1 = (p2[1]-p1[1])/(p2[0]-p1[0]) # slope of the new line segment
|
|
149
|
+
|
|
150
|
+
for boundary in boundaries: # loop over boundary lines
|
|
151
|
+
bp1 = boundary[1][0]
|
|
152
|
+
bp2 = boundary[1][1]
|
|
153
|
+
|
|
154
|
+
if (bp2[0]-bp1[0]) == 0: # inf slope
|
|
155
|
+
k2 = 99999999
|
|
156
|
+
else:
|
|
157
|
+
k2 = (bp2[1]-bp1[1])/(bp2[0]-bp1[0]) # slope of the boundary line segment
|
|
158
|
+
|
|
159
|
+
if math.isclose(k1,k2): # check if slopes are equal (with a tolerance)
|
|
160
|
+
p3 = (p1+p2)/2 # mid point of the new line
|
|
161
|
+
if k2 == 99999999: # inf slope
|
|
162
|
+
p3_on = (p3[0] == bp1[0])
|
|
163
|
+
else:
|
|
164
|
+
p3_on = math.isclose((p3[1] - bp1[1]),k2*(p3[0] - bp1[0]))
|
|
165
|
+
p3_between = (min(bp1[0], bp2[0]) <= p3[0] <= max(bp1[0], bp2[0])) and (min(bp1[1], bp2[1]) <= p3[1] <= max(bp1[1], bp2[1]))
|
|
166
|
+
if (p3_on and p3_between): # check if mid point of the new line is on the boundary line segment
|
|
167
|
+
collinear += 1
|
|
168
|
+
|
|
169
|
+
return collinear
|
|
170
|
+
|
|
171
|
+
def rotate_around_point_highperf(xy, radians, origin=(0, 0)):
|
|
172
|
+
"""Rotate a point around a given point.
|
|
173
|
+
|
|
174
|
+
I call this the "high performance" version since we're caching some
|
|
175
|
+
values that are needed >1 time. It's less readable than the previous
|
|
176
|
+
function but it's faster.
|
|
177
|
+
"""
|
|
178
|
+
x, y = xy
|
|
179
|
+
offset_x, offset_y = origin
|
|
180
|
+
adjusted_x = (x - offset_x)
|
|
181
|
+
adjusted_y = (y - offset_y)
|
|
182
|
+
cos_rad = math.cos(radians)
|
|
183
|
+
sin_rad = math.sin(radians)
|
|
184
|
+
qx = offset_x + cos_rad * adjusted_x + sin_rad * adjusted_y
|
|
185
|
+
qy = offset_y + -sin_rad * adjusted_x + cos_rad * adjusted_y
|
|
186
|
+
|
|
187
|
+
return qx, qy
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def CellPlacementWood(log_center,r_max,r_min,nrings,width_heart,width_early,width_late,\
|
|
191
|
+
cellsize_early,cellsize_late,iter_max,print_interval):
|
|
192
|
+
'''packing cells in annual rings'''
|
|
193
|
+
|
|
194
|
+
# Annual ring width distribution
|
|
195
|
+
width = np.concatenate(([width_heart],np.tile([width_early,width_late],nrings)))
|
|
196
|
+
noise = np.random.normal(1,0.25,len(width))
|
|
197
|
+
width = np.multiply(width,noise)
|
|
198
|
+
radius = np.concatenate(([0],np.cumsum(width)))
|
|
199
|
+
|
|
200
|
+
# Generate circle seeds in each annual ring (can be parallelized in the future)
|
|
201
|
+
circles = []
|
|
202
|
+
for ibin in range(0,nrings*2+1):
|
|
203
|
+
iter = 0
|
|
204
|
+
while iter < iter_max:
|
|
205
|
+
r = (radius[ibin+1]-radius[ibin])*math.sqrt(np.random.random()) + radius[ibin] # randomly generate a point in the domain
|
|
206
|
+
t = np.random.random()*2*math.pi
|
|
207
|
+
|
|
208
|
+
x = r*math.cos(t) + log_center[0]
|
|
209
|
+
y = r*math.sin(t) + log_center[1]
|
|
210
|
+
|
|
211
|
+
if (ibin % 2) == 0: # if even, latewood
|
|
212
|
+
w = cellsize_late #scipy.stats.truncnorm.rvs((we_lower - we_mu) / we_sigma, (we_upper - we_mu) / we_sigma, loc=we_mu, scale=we_sigma)
|
|
213
|
+
else:
|
|
214
|
+
w = cellsize_early #scipy.stats.truncnorm.rvs((wl_lower - wl_mu) / wl_sigma, (wl_upper - wl_mu) / wl_sigma, loc=wl_mu, scale=wl_sigma)
|
|
215
|
+
|
|
216
|
+
circC = [x, y, w]
|
|
217
|
+
|
|
218
|
+
if check_overlap(circles, circC) and check_isinside_boundcircle(circC,log_center,radius[ibin],radius[ibin+1]):
|
|
219
|
+
circles.append(circC)
|
|
220
|
+
|
|
221
|
+
# regularly print
|
|
222
|
+
if (len(circles) % print_interval == 0):
|
|
223
|
+
if (nrings*2+1-ibin == 1):
|
|
224
|
+
print('{:d} ring remaining; {:d} cells/particles placed.'.format(nrings*2+1-ibin,len(circles)))
|
|
225
|
+
else:
|
|
226
|
+
print('{:d} rings remaining; {:d} cells/particles placed.'.format(nrings*2+1-ibin,len(circles)))
|
|
227
|
+
iter += 1
|
|
228
|
+
|
|
229
|
+
circles = np.matrix(circles)
|
|
230
|
+
|
|
231
|
+
# take off out of bound Voronoi vertices
|
|
232
|
+
outofbound = []
|
|
233
|
+
for i in range(0,circles.shape[0]):
|
|
234
|
+
if( ((circles[i,0]-log_center[0])**2+(circles[i,1]-log_center[1])**2) > (1.2*r_max)**2 ):
|
|
235
|
+
outofbound.append(i)
|
|
236
|
+
circles = np.delete(circles,outofbound,0)
|
|
237
|
+
|
|
238
|
+
return circles, radius
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
def RebuildVoronoi(vor,circles,boundaries,log_center,x_min,x_max,y_min,y_max,box_center,boundaryFlag):
|
|
244
|
+
'''Clip Voronoi mesh by the boundaries, rebuild the new Voronoi mesh'''
|
|
245
|
+
# Store indices of Voronoi vertices for each finite ridge
|
|
246
|
+
finite_ridges = []
|
|
247
|
+
finite_ridges_pointid = []
|
|
248
|
+
infinite_ridges = []
|
|
249
|
+
infinite_ridges_pointid = []
|
|
250
|
+
boundary_points = []
|
|
251
|
+
|
|
252
|
+
# Form two new Voronoi vertices lists with and without out-of-bound vertices
|
|
253
|
+
voronoi_vertices_in = []
|
|
254
|
+
voronoi_vertices_out = []
|
|
255
|
+
|
|
256
|
+
if len(boundaries) == 3:
|
|
257
|
+
for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
|
|
258
|
+
simplex = np.asarray(simplex)
|
|
259
|
+
simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
|
|
260
|
+
if np.all(simplex >= 0) and check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[0]],0),boundaries) and check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[1]],0),boundaries):
|
|
261
|
+
voronoi_vertices_in.append(vor.vertices[simplex[0]])
|
|
262
|
+
voronoi_vertices_in.append(vor.vertices[simplex[1]])
|
|
263
|
+
finite_ridges.append(simplex)
|
|
264
|
+
finite_ridges_pointid.append(pointidx)
|
|
265
|
+
plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-',linewidth=1)
|
|
266
|
+
else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
|
|
267
|
+
if np.all(simplex >= 0) and (check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[0]],0),boundaries) or check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[1]],0),boundaries)): # finite Voronoi ridge but one vertex is far
|
|
268
|
+
|
|
269
|
+
if (check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[0]],0),boundaries) == 0):# if vertex is out-of-bound, inverse its index
|
|
270
|
+
voronoi_vertices_out.append(vor.vertices[simplex[0]])
|
|
271
|
+
if simplex[0] == 0:
|
|
272
|
+
simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
273
|
+
else:
|
|
274
|
+
simplex[0] = -simplex[0]
|
|
275
|
+
elif (check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[1]],0),boundaries) == 0):
|
|
276
|
+
voronoi_vertices_out.append(vor.vertices[simplex[1]])
|
|
277
|
+
if simplex[1] == 0:
|
|
278
|
+
simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
279
|
+
else:
|
|
280
|
+
simplex[1] = -simplex[1]
|
|
281
|
+
infinite_ridges.append(simplex)
|
|
282
|
+
infinite_ridges_pointid.append(pointidx)
|
|
283
|
+
elif check_isinside_boundTriangle2D(np.append(vor.vertices[simplex[simplex >= 0][0]],0),boundaries): # index of finite end Voronoi vertex of the infinite ridge
|
|
284
|
+
infinite_ridges.append(simplex)
|
|
285
|
+
infinite_ridges_pointid.append(pointidx)
|
|
286
|
+
elif len(boundaries) == 4:
|
|
287
|
+
for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
|
|
288
|
+
simplex = np.asarray(simplex)
|
|
289
|
+
simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
|
|
290
|
+
if np.all(simplex >= 0) and check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) and check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max):
|
|
291
|
+
voronoi_vertices_in.append(vor.vertices[simplex[0]])
|
|
292
|
+
voronoi_vertices_in.append(vor.vertices[simplex[1]])
|
|
293
|
+
finite_ridges.append(simplex)
|
|
294
|
+
finite_ridges_pointid.append(pointidx)
|
|
295
|
+
plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-',linewidth=1)
|
|
296
|
+
else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
|
|
297
|
+
if np.all(simplex >= 0) and (check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) or check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max)): # finite Voronoi ridge but one vertex is far
|
|
298
|
+
|
|
299
|
+
if (check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) == 0):# if vertex is out-of-bound, inverse its index
|
|
300
|
+
voronoi_vertices_out.append(vor.vertices[simplex[0]])
|
|
301
|
+
if simplex[0] == 0:
|
|
302
|
+
simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
303
|
+
else:
|
|
304
|
+
simplex[0] = -simplex[0]
|
|
305
|
+
elif (check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max) == 0):
|
|
306
|
+
voronoi_vertices_out.append(vor.vertices[simplex[1]])
|
|
307
|
+
if simplex[1] == 0:
|
|
308
|
+
simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
309
|
+
else:
|
|
310
|
+
simplex[1] = -simplex[1]
|
|
311
|
+
infinite_ridges.append(simplex)
|
|
312
|
+
infinite_ridges_pointid.append(pointidx)
|
|
313
|
+
elif check_isinside_boundbox2D(np.append(vor.vertices[simplex[simplex >= 0][0]],0),x_min,x_max,y_min,y_max): # index of finite end Voronoi vertex of the infinite ridge
|
|
314
|
+
infinite_ridges.append(simplex)
|
|
315
|
+
infinite_ridges_pointid.append(pointidx)
|
|
316
|
+
elif len(boundaries) == 6:
|
|
317
|
+
L = y_max - y_min
|
|
318
|
+
box_center = log_center
|
|
319
|
+
for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
|
|
320
|
+
simplex = np.asarray(simplex)
|
|
321
|
+
simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
|
|
322
|
+
if np.all(simplex >= 0) and check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[0]],0),L,box_center) and check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[1]],0),L,box_center):
|
|
323
|
+
voronoi_vertices_in.append(vor.vertices[simplex[0]])
|
|
324
|
+
voronoi_vertices_in.append(vor.vertices[simplex[1]])
|
|
325
|
+
|
|
326
|
+
finite_ridges.append(simplex)
|
|
327
|
+
finite_ridges_pointid.append(pointidx)
|
|
328
|
+
plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-',linewidth=1)
|
|
329
|
+
else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
|
|
330
|
+
|
|
331
|
+
if np.all(simplex >= 0) and (check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[0]],0),L,box_center) or check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[1]],0),L,box_center)): # finite Voronoi ridge but one vertex is far
|
|
332
|
+
|
|
333
|
+
if (check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[0]],0),L,box_center) == 0):# if vertex is out-of-bound, inverse its index
|
|
334
|
+
voronoi_vertices_out.append(vor.vertices[simplex[0]])
|
|
335
|
+
if simplex[0] == 0:
|
|
336
|
+
simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
337
|
+
else:
|
|
338
|
+
simplex[0] = -simplex[0]
|
|
339
|
+
elif (check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[1]],0),L,box_center) == 0):
|
|
340
|
+
voronoi_vertices_out.append(vor.vertices[simplex[1]])
|
|
341
|
+
if simplex[1] == 0:
|
|
342
|
+
simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
343
|
+
else:
|
|
344
|
+
simplex[1] = -simplex[1]
|
|
345
|
+
infinite_ridges.append(simplex)
|
|
346
|
+
infinite_ridges_pointid.append(pointidx)
|
|
347
|
+
elif check_isinside_boundHexagon2D(np.append(vor.vertices[simplex[simplex >= 0][0]],0),L,box_center): # index of finite end Voronoi vertex of the infinite ridge
|
|
348
|
+
infinite_ridges.append(simplex)
|
|
349
|
+
infinite_ridges_pointid.append(pointidx)
|
|
350
|
+
elif len(boundaries) == 8: # grooved
|
|
351
|
+
x_min = boundaries[0][1][0][0]
|
|
352
|
+
x_max = boundaries[1][1][0][0]
|
|
353
|
+
y_min = boundaries[0][1][0][1]
|
|
354
|
+
y_max = boundaries[2][1][0][1]
|
|
355
|
+
x_indent = boundaries[5][1][0][0]
|
|
356
|
+
y_indent_min = boundaries[6][1][0][1]
|
|
357
|
+
y_indent_max = boundaries[4][1][0][1]
|
|
358
|
+
|
|
359
|
+
|
|
360
|
+
for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
|
|
361
|
+
simplex = np.asarray(simplex)
|
|
362
|
+
simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
|
|
363
|
+
if np.all(simplex >= 0) and check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) and check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max):
|
|
364
|
+
voronoi_vertices_in.append(vor.vertices[simplex[0]])
|
|
365
|
+
voronoi_vertices_in.append(vor.vertices[simplex[1]])
|
|
366
|
+
finite_ridges.append(simplex)
|
|
367
|
+
finite_ridges_pointid.append(pointidx)
|
|
368
|
+
plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-')
|
|
369
|
+
else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
|
|
370
|
+
if np.all(simplex >= 0) and (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) or check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max)): # finite Voronoi ridge but one vertex is far
|
|
371
|
+
|
|
372
|
+
if (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) == 0):# if vertex is out-of-bound, inverse its index
|
|
373
|
+
voronoi_vertices_out.append(vor.vertices[simplex[0]])
|
|
374
|
+
if simplex[0] == 0:
|
|
375
|
+
simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
376
|
+
else:
|
|
377
|
+
simplex[0] = -simplex[0]
|
|
378
|
+
elif (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) == 0):
|
|
379
|
+
voronoi_vertices_out.append(vor.vertices[simplex[1]])
|
|
380
|
+
if simplex[1] == 0:
|
|
381
|
+
simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
382
|
+
else:
|
|
383
|
+
simplex[1] = -simplex[1]
|
|
384
|
+
infinite_ridges.append(simplex)
|
|
385
|
+
infinite_ridges_pointid.append(pointidx)
|
|
386
|
+
elif check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[simplex >= 0][0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max): # index of finite end Voronoi vertex of the infinite ridge
|
|
387
|
+
infinite_ridges.append(simplex)
|
|
388
|
+
infinite_ridges_pointid.append(pointidx)
|
|
389
|
+
else:
|
|
390
|
+
return
|
|
391
|
+
|
|
392
|
+
voronoi_vertices_in = np.unique(voronoi_vertices_in,axis=0)
|
|
393
|
+
voronoi_vertices_out = np.unique(voronoi_vertices_out,axis=0)
|
|
394
|
+
# plt.plot(voronoi_vertices_in[:, 0], voronoi_vertices_in[:, 1], 'ro')
|
|
395
|
+
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
# Find the intersect point of infinite ridges (rays) with the boundary lines
|
|
399
|
+
# Ref: https://stackoverflow.com/questions/14307158/how-do-you-check-for-intersection-between-a-line-segment-and-a-line-ray-emanatin#:~:text=Let%20r%20%3D%20(cos%20%CE%B8%2C,0%20%E2%89%A4%20u%20%E2%89%A4%201).&text=Then%20your%20line%20segment%20intersects,0%20%E2%89%A4%20u%20%E2%89%A4%201.
|
|
400
|
+
for pointidx, simplex in zip(infinite_ridges_pointid, infinite_ridges):
|
|
401
|
+
simplex = np.asarray(simplex)
|
|
402
|
+
|
|
403
|
+
i = simplex[simplex >= 0][0] # index of finite end Voronoi vertex of the infinite ridge
|
|
404
|
+
p = vor.vertices[i]
|
|
405
|
+
s = circles[pointidx[1],0:2] - circles[pointidx[0],0:2]
|
|
406
|
+
if simplex[simplex < 0][0] == -9999999999:
|
|
407
|
+
tangent = s / np.linalg.norm(s)
|
|
408
|
+
normal = np.array([-tangent[1], tangent[0]]) # normal
|
|
409
|
+
midpoint = circles[:,0:2][pointidx].mean(axis=0) # midpoint between site points
|
|
410
|
+
for boundary in boundaries: # loop over boundary lines
|
|
411
|
+
q = np.asarray(boundary[1][0])
|
|
412
|
+
s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
|
|
413
|
+
t = np.cross((q-p),s)/np.cross(normal,s)
|
|
414
|
+
u = np.cross((q-p),normal)/np.cross(normal,s)
|
|
415
|
+
|
|
416
|
+
if (u >= 0) and (u <= 1):
|
|
417
|
+
if np.sign(np.dot(midpoint - log_center, normal)) == 1: # facing outwards
|
|
418
|
+
if (t >= 0) and math.isfinite(t):
|
|
419
|
+
t_final = t
|
|
420
|
+
elif np.sign(np.dot(midpoint - log_center, normal)) == -1: # facing inwards
|
|
421
|
+
if (t < 0) and math.isfinite(t):
|
|
422
|
+
t_final = t
|
|
423
|
+
|
|
424
|
+
elif simplex[simplex < 0][0] == -9999999998:
|
|
425
|
+
t_final = np.inf
|
|
426
|
+
normal = (vor.vertices[0] - p)/np.linalg.norm(vor.vertices[0] - p)
|
|
427
|
+
for boundary in boundaries: # loop over boundary lines
|
|
428
|
+
q = np.asarray(boundary[1][0])
|
|
429
|
+
s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
|
|
430
|
+
t = np.cross((q-p),s)/np.cross(normal,s)
|
|
431
|
+
u = np.cross((q-p),normal)/np.cross(normal,s)
|
|
432
|
+
|
|
433
|
+
if (u >= 0) and (u <= 1):
|
|
434
|
+
if (t >= 0) and (t < t_final):
|
|
435
|
+
t_final = t
|
|
436
|
+
else:
|
|
437
|
+
t_final = np.inf
|
|
438
|
+
normal = (vor.vertices[-simplex[simplex < 0][0]] - p)/np.linalg.norm(vor.vertices[-simplex[simplex < 0][0]] - p)
|
|
439
|
+
for boundary in boundaries: # loop over boundary lines
|
|
440
|
+
q = np.asarray(boundary[1][0])
|
|
441
|
+
s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
|
|
442
|
+
t = np.cross((q-p),s)/np.cross(normal,s)
|
|
443
|
+
u = np.cross((q-p),normal)/np.cross(normal,s)
|
|
444
|
+
|
|
445
|
+
if (u >= 0) and (u <= 1):
|
|
446
|
+
if (t >= 0) and (t < t_final):
|
|
447
|
+
t_final = t
|
|
448
|
+
|
|
449
|
+
intersect_point = p + normal * t_final
|
|
450
|
+
boundary_points.append(intersect_point)
|
|
451
|
+
|
|
452
|
+
plt.plot([vor.vertices[i,0], intersect_point[0]], [vor.vertices[i,1], intersect_point[1]], 'k-',linewidth=1)
|
|
453
|
+
|
|
454
|
+
if boundaryFlag in ['on','On','Y','y','Yes','yes']:
|
|
455
|
+
boundary_points = np.asarray(boundary_points)
|
|
456
|
+
boundary_points_featured = [x[1][0] for x in boundaries]
|
|
457
|
+
boundary_points_featured = np.reshape(np.asarray(boundary_points_featured),(-1,2))
|
|
458
|
+
nboundary_pts_featured = boundary_points_featured.shape[0]
|
|
459
|
+
boundary_points = np.vstack((boundary_points,boundary_points_featured))
|
|
460
|
+
nvertices_in = voronoi_vertices_in.shape[0]
|
|
461
|
+
nboundary_pts = boundary_points.shape[0]
|
|
462
|
+
finite_ridges = np.asarray(finite_ridges)
|
|
463
|
+
nfinite_ridge = finite_ridges.shape[0]
|
|
464
|
+
infinite_ridges = np.asarray(infinite_ridges)
|
|
465
|
+
ninfinite_ridge = infinite_ridges.shape[0]
|
|
466
|
+
|
|
467
|
+
else:
|
|
468
|
+
boundary_points = np.asarray(boundary_points)
|
|
469
|
+
nboundary_pts_featured = 0
|
|
470
|
+
nvertices_in = voronoi_vertices_in.shape[0]
|
|
471
|
+
nboundary_pts = boundary_points.shape[0]
|
|
472
|
+
finite_ridges = np.asarray(finite_ridges)
|
|
473
|
+
nfinite_ridge = finite_ridges.shape[0]
|
|
474
|
+
infinite_ridges = np.asarray(infinite_ridges)
|
|
475
|
+
ninfinite_ridge = infinite_ridges.shape[0]
|
|
476
|
+
|
|
477
|
+
# reconstruct the connectivity for ridges since the unique operation rearrange the order of vertices (need to find a more efficient way like vectorize)
|
|
478
|
+
finite_ridges_new = np.copy(finite_ridges)
|
|
479
|
+
for i in range(0,nfinite_ridge): # loop over voronoi ridges in original finite_ridge list
|
|
480
|
+
ii = np.where(np.all(voronoi_vertices_in==vor.vertices[finite_ridges[i,0]],axis=1))[0][0] # index of first vertex in voronoi_vertices_in array
|
|
481
|
+
jj = np.where(np.all(voronoi_vertices_in==vor.vertices[finite_ridges[i,1]],axis=1))[0][0] # index of second vertex in voronoi_vertices_in array
|
|
482
|
+
finite_ridges_new[i,:] = [ii,jj]
|
|
483
|
+
|
|
484
|
+
infinite_ridges_new = np.copy(infinite_ridges)
|
|
485
|
+
for i in range(0,ninfinite_ridge): # loop over voronoi ridges in original infinite_ridge list
|
|
486
|
+
if (infinite_ridges[i,:] < 0).argmax(axis=0) == 0: # first vertex is of negative index
|
|
487
|
+
ii = nvertices_in + i
|
|
488
|
+
jj = np.where(np.all(voronoi_vertices_in==vor.vertices[infinite_ridges[i,1]],axis=1))[0][0] # index of finite vertex in voronoi_vertices_in array
|
|
489
|
+
else:
|
|
490
|
+
ii = np.where(np.all(voronoi_vertices_in==vor.vertices[infinite_ridges[i,0]],axis=1))[0][0] # index of finite vertex in voronoi_vertices_in array
|
|
491
|
+
jj = nvertices_in + i
|
|
492
|
+
infinite_ridges_new[i,:] = [ii,jj]
|
|
493
|
+
|
|
494
|
+
if boundaryFlag in ['on','On','Y','y','Yes','yes']:
|
|
495
|
+
# construct the connectivity for a line path consisting of the boundary points
|
|
496
|
+
boundary_ridges_new = np.zeros(boundary_points.shape)
|
|
497
|
+
boundary_points_new = np.copy(boundary_points)
|
|
498
|
+
next_point = np.copy(boundary_points_new[0]) # get first point
|
|
499
|
+
boundary_points_new[0] = [np.inf,np.inf]
|
|
500
|
+
|
|
501
|
+
for i in range(0,boundary_points_new.shape[0]-1):
|
|
502
|
+
next_point_id = cdist([next_point], boundary_points_new).argmin()
|
|
503
|
+
if check_iscollinear(next_point,boundary_points_new[next_point_id],boundaries) > 0: # check if the new line segment is collinear with any boundary line segment
|
|
504
|
+
boundary_ridges_new[i,1] = next_point_id
|
|
505
|
+
boundary_ridges_new[i+1,0] = next_point_id
|
|
506
|
+
next_point = np.copy(boundary_points_new[next_point_id])
|
|
507
|
+
boundary_points_new[next_point_id] = [np.inf,np.inf]
|
|
508
|
+
else:
|
|
509
|
+
boundary_points_new_check = np.copy(boundary_points_new)
|
|
510
|
+
while check_iscollinear(next_point,boundary_points_new_check[next_point_id],boundaries) == 0:
|
|
511
|
+
boundary_points_new_check[next_point_id] = [np.inf,np.inf]
|
|
512
|
+
next_point_id = cdist([next_point], boundary_points_new_check).argmin()
|
|
513
|
+
|
|
514
|
+
boundary_ridges_new[i,1] = next_point_id
|
|
515
|
+
boundary_ridges_new[i+1,0] = next_point_id
|
|
516
|
+
next_point = np.copy(boundary_points_new[next_point_id])
|
|
517
|
+
boundary_points_new[next_point_id] = [np.inf,np.inf]
|
|
518
|
+
|
|
519
|
+
boundary_ridges_new = (boundary_ridges_new+nvertices_in).astype(int) # shift the indices with "nvertices_in"
|
|
520
|
+
|
|
521
|
+
voronoi_vertices = np.vstack((voronoi_vertices_in,boundary_points)) # vertical stack "in" vetices and "cross" boundary points
|
|
522
|
+
nvertex = voronoi_vertices.shape[0]
|
|
523
|
+
boundary_ridges_new = np.vstack((infinite_ridges_new,boundary_ridges_new))
|
|
524
|
+
nboundary_ridge = boundary_ridges_new.shape[0]
|
|
525
|
+
voronoi_ridges = np.vstack((finite_ridges_new,boundary_ridges_new))
|
|
526
|
+
nridge = voronoi_ridges.shape[0]
|
|
527
|
+
else:
|
|
528
|
+
voronoi_vertices = np.vstack((voronoi_vertices_in,boundary_points)) # vertical stack "in" vetices and "cross" boundary points
|
|
529
|
+
nvertex = voronoi_vertices.shape[0]
|
|
530
|
+
boundary_ridges_new = np.copy(infinite_ridges_new)
|
|
531
|
+
nboundary_ridge = boundary_ridges_new.shape[0]
|
|
532
|
+
voronoi_ridges = np.vstack((finite_ridges_new,boundary_ridges_new))
|
|
533
|
+
nridge = voronoi_ridges.shape[0]
|
|
534
|
+
|
|
535
|
+
return voronoi_vertices,finite_ridges,boundary_points,finite_ridges_new,\
|
|
536
|
+
boundary_ridges_new,nvertex,nvertices_in,nfinite_ridge,nboundary_ridge,\
|
|
537
|
+
nboundary_pts,nboundary_pts_featured,voronoi_ridges,nridge
|
|
538
|
+
|
|
539
|
+
def RebuildVoronoi_new(vor,circles,boundaries,log_center,x_min,x_max,y_min,y_max,box_center,merge_tol,boundaryFlag):
|
|
540
|
+
'''Clip Voronoi mesh by the boundaries, rebuild the new Voronoi mesh'''
|
|
541
|
+
# Store indices of Voronoi vertices for each finite ridge
|
|
542
|
+
finite_ridges = []
|
|
543
|
+
finite_ridges_pointid = []
|
|
544
|
+
infinite_ridges = []
|
|
545
|
+
infinite_ridges_pointid = []
|
|
546
|
+
boundary_points = []
|
|
547
|
+
|
|
548
|
+
# Form two new Voronoi vertices lists with and without out-of-bound vertices
|
|
549
|
+
voronoi_vertices_in = []
|
|
550
|
+
voronoi_vertices_out = []
|
|
551
|
+
|
|
552
|
+
if len(boundaries) == 8: # grooved
|
|
553
|
+
|
|
554
|
+
x_min = boundaries[0][1][0][0]
|
|
555
|
+
x_max = boundaries[1][1][0][0]
|
|
556
|
+
y_min = boundaries[0][1][0][1]
|
|
557
|
+
y_max = boundaries[2][1][0][1]
|
|
558
|
+
x_indent = boundaries[5][1][0][0]
|
|
559
|
+
y_indent_min = boundaries[6][1][0][1]
|
|
560
|
+
y_indent_max = boundaries[4][1][0][1]
|
|
561
|
+
|
|
562
|
+
for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
|
|
563
|
+
simplex = np.asarray(simplex)
|
|
564
|
+
simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
|
|
565
|
+
if np.all(simplex >= 0) and check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) and check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max):
|
|
566
|
+
voronoi_vertices_in.append(vor.vertices[simplex[0]])
|
|
567
|
+
voronoi_vertices_in.append(vor.vertices[simplex[1]])
|
|
568
|
+
finite_ridges.append(simplex)
|
|
569
|
+
finite_ridges_pointid.append(pointidx)
|
|
570
|
+
plt.plot(vor.vertices[simplex, 0], vor.vertices[simplex, 1], 'k-')
|
|
571
|
+
else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
|
|
572
|
+
if np.all(simplex >= 0) and (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) or check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max)): # finite Voronoi ridge but one vertex is far
|
|
573
|
+
if (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) == 0):# if vertex is out-of-bound, inverse its index
|
|
574
|
+
voronoi_vertices_out.append(vor.vertices[simplex[0]])
|
|
575
|
+
if simplex[0] == 0:
|
|
576
|
+
simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
577
|
+
else:
|
|
578
|
+
simplex[0] = -simplex[0]
|
|
579
|
+
elif (check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max) == 0):
|
|
580
|
+
voronoi_vertices_out.append(vor.vertices[simplex[1]])
|
|
581
|
+
if simplex[1] == 0:
|
|
582
|
+
simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
583
|
+
else:
|
|
584
|
+
simplex[1] = -simplex[1]
|
|
585
|
+
infinite_ridges.append(simplex)
|
|
586
|
+
infinite_ridges_pointid.append(pointidx)
|
|
587
|
+
elif check_isinside_boundbox2Dindent(np.append(vor.vertices[simplex[simplex >= 0][0]],0),x_min,x_max,y_min,y_max,x_indent,y_indent_min,y_indent_max): # index of finite end Voronoi vertex of the infinite ridge
|
|
588
|
+
infinite_ridges.append(simplex)
|
|
589
|
+
infinite_ridges_pointid.append(pointidx)
|
|
590
|
+
else:
|
|
591
|
+
for pointidx, simplex in zip(vor.ridge_points, vor.ridge_vertices):
|
|
592
|
+
simplex = np.asarray(simplex)
|
|
593
|
+
simplex = np.where(simplex == -1, -9999999999, simplex) # use a large negative number instead of -1 to represent the infinite ridge vertices
|
|
594
|
+
if np.all(simplex >= 0) and check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) and check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max):
|
|
595
|
+
voronoi_vertices_in.append(vor.vertices[simplex[0]])
|
|
596
|
+
voronoi_vertices_in.append(vor.vertices[simplex[1]])
|
|
597
|
+
finite_ridges.append(simplex)
|
|
598
|
+
finite_ridges_pointid.append(pointidx)
|
|
599
|
+
else: # infinite Voronoi ridges and finite ridges with out-of-bound vetices
|
|
600
|
+
if np.all(simplex >= 0) and (check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) or check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max)): # finite Voronoi ridge but one vertex is far
|
|
601
|
+
|
|
602
|
+
if (check_isinside_boundbox2D(np.append(vor.vertices[simplex[0]],0),x_min,x_max,y_min,y_max) == 0):# if vertex is out-of-bound, inverse its index
|
|
603
|
+
voronoi_vertices_out.append(vor.vertices[simplex[0]])
|
|
604
|
+
voronoi_vertices_in.append(vor.vertices[simplex[1]])
|
|
605
|
+
if simplex[0] == 0:
|
|
606
|
+
simplex[0] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
607
|
+
else:
|
|
608
|
+
simplex[0] = -simplex[0]
|
|
609
|
+
elif (check_isinside_boundbox2D(np.append(vor.vertices[simplex[1]],0),x_min,x_max,y_min,y_max) == 0):
|
|
610
|
+
voronoi_vertices_out.append(vor.vertices[simplex[1]])
|
|
611
|
+
voronoi_vertices_in.append(vor.vertices[simplex[0]])
|
|
612
|
+
if simplex[1] == 0:
|
|
613
|
+
simplex[1] = -9999999998 # since inverse of 0 is still zero, we use another large number to represent -0
|
|
614
|
+
else:
|
|
615
|
+
simplex[1] = -simplex[1]
|
|
616
|
+
infinite_ridges.append(simplex)
|
|
617
|
+
infinite_ridges_pointid.append(pointidx)
|
|
618
|
+
elif check_isinside_boundbox2D(np.append(vor.vertices[simplex[simplex >= 0][0]],0),x_min,x_max,y_min,y_max): # index of finite end Voronoi vertex of the infinite ridge
|
|
619
|
+
infinite_ridges.append(simplex)
|
|
620
|
+
infinite_ridges_pointid.append(pointidx)
|
|
621
|
+
|
|
622
|
+
voronoi_vertices_in = np.unique(voronoi_vertices_in,axis=0)
|
|
623
|
+
voronoi_vertices_out = np.unique(voronoi_vertices_out,axis=0)
|
|
624
|
+
|
|
625
|
+
# Find the intersect point of infinite ridges (rays) with the boundary lines
|
|
626
|
+
# Ref: https://stackoverflow.com/questions/14307158/how-do-you-check-for-intersection-between-a-line-segment-and-a-line-ray-emanatin#:~:text=Let%20r%20%3D%20(cos%20%CE%B8%2C,0%20%E2%89%A4%20u%20%E2%89%A4%201).&text=Then%20your%20line%20segment%20intersects,0%20%E2%89%A4%20u%20%E2%89%A4%201.
|
|
627
|
+
for pointidx, simplex in zip(infinite_ridges_pointid, infinite_ridges):
|
|
628
|
+
simplex = np.asarray(simplex)
|
|
629
|
+
i = simplex[simplex >= 0][0] # index of finite end Voronoi vertex of the infinite ridge
|
|
630
|
+
p = vor.vertices[i]
|
|
631
|
+
s = circles[pointidx[1],0:2] - circles[pointidx[0],0:2]
|
|
632
|
+
if simplex[simplex < 0][0] == -9999999999:
|
|
633
|
+
tangent = s / np.linalg.norm(s)
|
|
634
|
+
normal = np.array([-tangent[0,1], tangent[0,0]]) # normal
|
|
635
|
+
midpoint = circles[:,0:2][pointidx].mean(axis=0) # midpoint between site points
|
|
636
|
+
for boundary in boundaries: # loop over boundary lines
|
|
637
|
+
q = np.asarray(boundary[1][0])
|
|
638
|
+
s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
|
|
639
|
+
t = np.cross((q-p),s)/np.cross(normal,s)
|
|
640
|
+
u = np.cross((q-p),normal)/np.cross(normal,s)
|
|
641
|
+
|
|
642
|
+
if (u>= 0) and (u<=1):
|
|
643
|
+
if np.sign(np.dot(midpoint - log_center, normal)) == 1: # facing outwards
|
|
644
|
+
if (t >= 0) and math.isfinite(t):
|
|
645
|
+
t_final = t
|
|
646
|
+
elif np.sign(np.dot(midpoint - log_center, normal)) == -1: # facing inwards
|
|
647
|
+
if (t < 0) and math.isfinite(t):
|
|
648
|
+
t_final = t
|
|
649
|
+
|
|
650
|
+
elif simplex[simplex < 0][0] == -9999999998:
|
|
651
|
+
t_final = np.inf
|
|
652
|
+
normal = (vor.vertices[0] - p)/np.linalg.norm(vor.vertices[0] - p)
|
|
653
|
+
for boundary in boundaries: # loop over boundary lines
|
|
654
|
+
q = np.asarray(boundary[1][0])
|
|
655
|
+
s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
|
|
656
|
+
t = np.cross((q-p),s)/np.cross(normal,s)
|
|
657
|
+
u = np.cross((q-p),normal)/np.cross(normal,s)
|
|
658
|
+
|
|
659
|
+
if (u >= 0) and (u <= 1):
|
|
660
|
+
if (t > 0) and (t < t_final):
|
|
661
|
+
t_final = t
|
|
662
|
+
else:
|
|
663
|
+
t_final = np.inf
|
|
664
|
+
normal = (vor.vertices[-simplex[simplex < 0][0]] - p)/np.linalg.norm(vor.vertices[-simplex[simplex < 0][0]] - p)
|
|
665
|
+
for boundary in boundaries: # loop over boundary lines
|
|
666
|
+
q = np.asarray(boundary[1][0])
|
|
667
|
+
s = np.asarray(boundary[1][1]) - np.asarray(boundary[1][0])
|
|
668
|
+
t = np.cross((q-p),s)/np.cross(normal,s)
|
|
669
|
+
u = np.cross((q-p),normal)/np.cross(normal,s)
|
|
670
|
+
|
|
671
|
+
if (u >= 0) and (u <= 1):
|
|
672
|
+
if (t > 0) and (t < t_final):
|
|
673
|
+
t_final = t
|
|
674
|
+
|
|
675
|
+
intersect_point = p + normal * t_final
|
|
676
|
+
boundary_points.append(intersect_point)
|
|
677
|
+
|
|
678
|
+
if boundaryFlag in ['on','On','Y','y','Yes','yes']:
|
|
679
|
+
boundary_points = np.asarray(boundary_points)
|
|
680
|
+
boundary_points_featured = [x[1][0] for x in boundaries]
|
|
681
|
+
boundary_points_featured = np.reshape(np.asarray(boundary_points_featured),(-1,2))
|
|
682
|
+
nboundary_pts_ini = boundary_points.shape[0]
|
|
683
|
+
nboundary_pts_featured = boundary_points_featured.shape[0]
|
|
684
|
+
boundary_points = np.vstack((boundary_points,boundary_points_featured))
|
|
685
|
+
|
|
686
|
+
nvertices_in = voronoi_vertices_in.shape[0]
|
|
687
|
+
nboundary_pts = boundary_points.shape[0]
|
|
688
|
+
finite_ridges = np.asarray(finite_ridges)
|
|
689
|
+
nfinite_ridge = finite_ridges.shape[0]
|
|
690
|
+
infinite_ridges = np.asarray(infinite_ridges)
|
|
691
|
+
ninfinite_ridge = infinite_ridges.shape[0]
|
|
692
|
+
else:
|
|
693
|
+
boundary_points = np.asarray(boundary_points)
|
|
694
|
+
nboundary_pts_featured = 0
|
|
695
|
+
nvertices_in = voronoi_vertices_in.shape[0]
|
|
696
|
+
nboundary_pts = boundary_points.shape[0]
|
|
697
|
+
finite_ridges = np.asarray(finite_ridges)
|
|
698
|
+
nfinite_ridge = finite_ridges.shape[0]
|
|
699
|
+
infinite_ridges = np.asarray(infinite_ridges)
|
|
700
|
+
ninfinite_ridge = infinite_ridges.shape[0]
|
|
701
|
+
|
|
702
|
+
# reconstruct the connectivity for ridges since the unique operation rearrange the order of vertices (need to find a more efficient way like vectorize)
|
|
703
|
+
finite_ridges_new = np.copy(finite_ridges)
|
|
704
|
+
for i in range(0,nfinite_ridge): # loop over voronoi ridges in original finite_ridge list
|
|
705
|
+
ii = np.where(np.all(voronoi_vertices_in==vor.vertices[finite_ridges[i,0]],axis=1))[0][0] # index of first vertex in voronoi_vertices_in array
|
|
706
|
+
jj = np.where(np.all(voronoi_vertices_in==vor.vertices[finite_ridges[i,1]],axis=1))[0][0] # index of second vertex in voronoi_vertices_in array
|
|
707
|
+
finite_ridges_new[i,:] = [ii,jj]
|
|
708
|
+
|
|
709
|
+
infinite_ridges_new = np.copy(infinite_ridges)
|
|
710
|
+
for i in range(0,ninfinite_ridge): # loop over voronoi ridges in original infinite_ridge list
|
|
711
|
+
if (infinite_ridges[i,:] < 0).argmax(axis=0) == 0: # first vertex is of negative index
|
|
712
|
+
ii = nvertices_in + i
|
|
713
|
+
jj = np.where(np.all(voronoi_vertices_in==vor.vertices[infinite_ridges[i,1]],axis=1))[0][0] # index of finite vertex in voronoi_vertices_in array
|
|
714
|
+
else:
|
|
715
|
+
ii = np.where(np.all(voronoi_vertices_in==vor.vertices[infinite_ridges[i,0]],axis=1))[0][0] # index of finite vertex in voronoi_vertices_in array
|
|
716
|
+
jj = nvertices_in + i
|
|
717
|
+
infinite_ridges_new[i,:] = [ii,jj]
|
|
718
|
+
|
|
719
|
+
|
|
720
|
+
# Merge operation
|
|
721
|
+
# Case 1 merge
|
|
722
|
+
voronoi_vertices_in_whole = np.copy(voronoi_vertices_in)
|
|
723
|
+
tree_voronoi_vertices_in = cKDTree(voronoi_vertices_in)
|
|
724
|
+
rows_to_fuse_voronoi_vertices_in = tree_voronoi_vertices_in.query_pairs(r=merge_tol,output_type='ndarray')
|
|
725
|
+
rows_to_fuse_voronoi_vertices_in_whole = np.copy(rows_to_fuse_voronoi_vertices_in)
|
|
726
|
+
|
|
727
|
+
finite_ridges_merged = np.copy(finite_ridges_new)
|
|
728
|
+
infinite_ridges_merged = np.copy(infinite_ridges_new)
|
|
729
|
+
while len(rows_to_fuse_voronoi_vertices_in) != 0:
|
|
730
|
+
points_new = np.reshape(voronoi_vertices_in_whole[rows_to_fuse_voronoi_vertices_in_whole],(-1,2))
|
|
731
|
+
voronoi_vertices_in_fused = 0.5*(points_new[0::2] + points_new[1::2])
|
|
732
|
+
|
|
733
|
+
# voronoi_vertices_in_fused = voronoi_vertices_in_fused.round(decimals=0)
|
|
734
|
+
voronoi_vertices_in_fused = np.round(voronoi_vertices_in_fused/merge_tol).astype(int)*merge_tol # here we have to round to the nearest merge_tol number to avoid infinite loop
|
|
735
|
+
|
|
736
|
+
# check if there are identical merged points from different pairs of closeby points (usually happens when three points are clustered)
|
|
737
|
+
if len(np.unique(voronoi_vertices_in_fused,axis=0)) != len(voronoi_vertices_in_fused):
|
|
738
|
+
[unique_pts,unique_inverse] = np.unique(voronoi_vertices_in_fused,return_inverse=True,axis=0)
|
|
739
|
+
|
|
740
|
+
# Offset the indices of boundary points as we inserted points in vertice_in list
|
|
741
|
+
infinite_ridges_merged[infinite_ridges_merged >= nvertices_in] += len(unique_pts)
|
|
742
|
+
# Replace the indices of two close points with the index of new merged point in both finite_ridges_new and infinite_ridges_new lists
|
|
743
|
+
for i in range(0,len(rows_to_fuse_voronoi_vertices_in_whole)):
|
|
744
|
+
finite_ridges_merged[finite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,0]] = nvertices_in + unique_inverse[i]
|
|
745
|
+
finite_ridges_merged[finite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,1]] = nvertices_in + unique_inverse[i]
|
|
746
|
+
infinite_ridges_merged[infinite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,0]] = nvertices_in + unique_inverse[i]
|
|
747
|
+
infinite_ridges_merged[infinite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,1]] = nvertices_in + unique_inverse[i]
|
|
748
|
+
voronoi_vertices_in_fused = unique_pts.astype(float)
|
|
749
|
+
else:
|
|
750
|
+
# Offset the indices of boundary points as we inserted points in vertice_in list
|
|
751
|
+
infinite_ridges_merged[infinite_ridges_merged >= nvertices_in] += len(rows_to_fuse_voronoi_vertices_in_whole)
|
|
752
|
+
# Replace the indices of two close points with the index of new merged point in both finite_ridges_new and infinite_ridges_new lists
|
|
753
|
+
for i in range(0,len(rows_to_fuse_voronoi_vertices_in_whole)):
|
|
754
|
+
finite_ridges_merged[finite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,0]] = nvertices_in + i
|
|
755
|
+
finite_ridges_merged[finite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,1]] = nvertices_in + i
|
|
756
|
+
infinite_ridges_merged[infinite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,0]] = nvertices_in + i
|
|
757
|
+
infinite_ridges_merged[infinite_ridges_merged == rows_to_fuse_voronoi_vertices_in_whole[i,1]] = nvertices_in + i
|
|
758
|
+
|
|
759
|
+
voronoi_vertices_in_whole = np.vstack((voronoi_vertices_in_whole,voronoi_vertices_in_fused)) # create a whole vertex list
|
|
760
|
+
voronoi_vertices_in_new = np.vstack((np.delete(voronoi_vertices_in,rows_to_fuse_voronoi_vertices_in,0),voronoi_vertices_in_fused)) # create a new vertex list
|
|
761
|
+
# update the voronoi_vertices_in
|
|
762
|
+
voronoi_vertices_in = np.copy(voronoi_vertices_in_new)
|
|
763
|
+
nvertices_in = voronoi_vertices_in_whole.shape[0]
|
|
764
|
+
# update the kd-tree
|
|
765
|
+
tree_voronoi_vertices_in = cKDTree(voronoi_vertices_in)
|
|
766
|
+
rows_to_fuse_voronoi_vertices_in = tree_voronoi_vertices_in.query_pairs(r=merge_tol,output_type='ndarray')
|
|
767
|
+
# update the rows_to_fuse_voronoi_vertices_in with the rows-to-fuse in list voronoi_vertices_in_whole
|
|
768
|
+
rows_to_fuse_voronoi_vertices_in_whole = np.copy(rows_to_fuse_voronoi_vertices_in)
|
|
769
|
+
for i in range(0,len(rows_to_fuse_voronoi_vertices_in)):
|
|
770
|
+
rows_to_fuse_voronoi_vertices_in_whole[i,0] = np.where(np.all(voronoi_vertices_in_whole==voronoi_vertices_in_new[rows_to_fuse_voronoi_vertices_in[i,0],:],axis=1))[0][0]
|
|
771
|
+
rows_to_fuse_voronoi_vertices_in_whole[i,1] = np.where(np.all(voronoi_vertices_in_whole==voronoi_vertices_in_new[rows_to_fuse_voronoi_vertices_in[i,1],:],axis=1))[0][0]
|
|
772
|
+
|
|
773
|
+
# delete rows with identical indices and reverse indices in finite_ridges_merged
|
|
774
|
+
zero_rows = np.where((finite_ridges_merged[:,0] - finite_ridges_merged[:,1]) == 0)[0]
|
|
775
|
+
finite_ridges_merged = np.delete(finite_ridges_merged,zero_rows,0)
|
|
776
|
+
|
|
777
|
+
# delete rows with identical indices and reverse indices in infinite_ridges_merged
|
|
778
|
+
zero_rows = np.where((infinite_ridges_merged[:,0] - infinite_ridges_merged[:,1]) == 0)[0]
|
|
779
|
+
infinite_ridges_merged = np.delete(infinite_ridges_merged,zero_rows,0)
|
|
780
|
+
|
|
781
|
+
if boundaryFlag in ['on','On','Y','y','Yes','yes']:
|
|
782
|
+
# construct the connectivity for a line path consisting of the boundary points
|
|
783
|
+
####################################################################################################################################################
|
|
784
|
+
# The algorithm here needs improve: bug contains when searching for the closest point, the closest point may be on the other sides
|
|
785
|
+
####################################################################################################################################################
|
|
786
|
+
boundary_points_ini = np.copy(boundary_points)
|
|
787
|
+
boundary_ridges_new = np.zeros(boundary_points.shape)
|
|
788
|
+
boundary_points_new = np.copy(boundary_points)
|
|
789
|
+
next_point = np.copy(boundary_points_new[0]) # get first point
|
|
790
|
+
boundary_points_new[0] = [np.inf,np.inf]
|
|
791
|
+
|
|
792
|
+
for i in range(0,boundary_points_new.shape[0]-1):
|
|
793
|
+
next_point_id = cdist([next_point], boundary_points_new).argmin()
|
|
794
|
+
boundary_ridges_new[i,1] = next_point_id
|
|
795
|
+
boundary_ridges_new[i+1,0] = next_point_id
|
|
796
|
+
next_point = np.copy(boundary_points_new[next_point_id])
|
|
797
|
+
boundary_points_new[next_point_id] = [np.inf,np.inf]
|
|
798
|
+
boundary_ridges_new = (boundary_ridges_new+nvertices_in).astype(int) # shift the indices with "nvertices_in"
|
|
799
|
+
|
|
800
|
+
# Case 2 merge
|
|
801
|
+
tree_boundary_points = cKDTree(boundary_points)
|
|
802
|
+
rows_to_fuse_boundary_points = tree_boundary_points.query_pairs(r=merge_tol,output_type='ndarray')
|
|
803
|
+
rows_to_fuse_boundary_points_whole = np.copy(rows_to_fuse_boundary_points)
|
|
804
|
+
boundary_ridges_merged = np.copy(boundary_ridges_new)
|
|
805
|
+
nboundary_pts = boundary_points.shape[0]
|
|
806
|
+
boundary_points_whole = np.copy(boundary_points)
|
|
807
|
+
|
|
808
|
+
while len(rows_to_fuse_boundary_points) != 0:
|
|
809
|
+
points_new = np.reshape(boundary_points_whole[rows_to_fuse_boundary_points_whole],(-1,2))
|
|
810
|
+
boundary_points_fused = 0.5*(points_new[0::2] + points_new[1::2])
|
|
811
|
+
|
|
812
|
+
# boundary_points_fused = boundary_points_fused.round(decimals=0)
|
|
813
|
+
boundary_points_fused = np.round(boundary_points_fused/merge_tol).astype(int)*merge_tol # here we have to round to the nearest merge_tol number to avoid infinite loop
|
|
814
|
+
|
|
815
|
+
# check if there are identical merged points from different pairs of closeby points (usually happens when three points are clustered)
|
|
816
|
+
if len(np.unique(boundary_points_fused,axis=0)) != len(boundary_points_fused):
|
|
817
|
+
[unique_pts,unique_inverse] = np.unique(boundary_points_fused,return_inverse=True,axis=0)
|
|
818
|
+
# Replace the indices of closeby points with the index of new merged point in both finite_ridges_new and infinite_ridges_new lists
|
|
819
|
+
for i in range(0,len(rows_to_fuse_boundary_points)):
|
|
820
|
+
boundary_ridges_merged[boundary_ridges_merged == (rows_to_fuse_boundary_points_whole[i,0]+nvertices_in)] = nvertices_in + nboundary_pts + unique_inverse[i]
|
|
821
|
+
boundary_ridges_merged[boundary_ridges_merged == (rows_to_fuse_boundary_points_whole[i,1]+nvertices_in)] = nvertices_in + nboundary_pts + unique_inverse[i]
|
|
822
|
+
infinite_ridges_merged[infinite_ridges_merged == (rows_to_fuse_boundary_points_whole[i,0]+nvertices_in)] = nvertices_in + nboundary_pts + unique_inverse[i]
|
|
823
|
+
infinite_ridges_merged[infinite_ridges_merged == (rows_to_fuse_boundary_points_whole[i,1]+nvertices_in)] = nvertices_in + nboundary_pts + unique_inverse[i]
|
|
824
|
+
boundary_points_fused = unique_pts.astype(float)
|
|
825
|
+
else:
|
|
826
|
+
# Replace the indices of two close points with the index of new merged point in both boundary_ridges_new and infinite_ridges_new lists
|
|
827
|
+
for i in range(0,len(rows_to_fuse_boundary_points)):
|
|
828
|
+
boundary_ridges_merged[boundary_ridges_merged == (rows_to_fuse_boundary_points_whole[i,0]+nvertices_in)] = nvertices_in + nboundary_pts + i
|
|
829
|
+
boundary_ridges_merged[boundary_ridges_merged == (rows_to_fuse_boundary_points_whole[i,1]+nvertices_in)] = nvertices_in + nboundary_pts + i
|
|
830
|
+
infinite_ridges_merged[infinite_ridges_merged == (rows_to_fuse_boundary_points_whole[i,0]+nvertices_in)] = nvertices_in + nboundary_pts + i
|
|
831
|
+
infinite_ridges_merged[infinite_ridges_merged == (rows_to_fuse_boundary_points_whole[i,1]+nvertices_in)] = nvertices_in + nboundary_pts + i
|
|
832
|
+
|
|
833
|
+
boundary_points_whole = np.vstack((boundary_points_whole,boundary_points_fused)) # create a whole vertex list
|
|
834
|
+
boundary_points_new = np.vstack((np.delete(boundary_points,rows_to_fuse_boundary_points,0),boundary_points_fused)) # create a new vertex list
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
# update the boundary_points
|
|
838
|
+
boundary_points = np.copy(boundary_points_new)
|
|
839
|
+
nboundary_pts = boundary_points_whole.shape[0]
|
|
840
|
+
# update the kd-tree
|
|
841
|
+
tree_boundary_points = cKDTree(boundary_points)
|
|
842
|
+
rows_to_fuse_boundary_points = tree_boundary_points.query_pairs(r=merge_tol,output_type='ndarray')
|
|
843
|
+
# update the rows_to_fuse in list voronoi_vertices_in with the rows-to-fuse in list voronoi_vertices_in_whole
|
|
844
|
+
rows_to_fuse_boundary_points_whole = np.copy(rows_to_fuse_boundary_points)
|
|
845
|
+
for i in range(0,len(rows_to_fuse_boundary_points)):
|
|
846
|
+
rows_to_fuse_boundary_points_whole[i,0] = np.where(np.all(boundary_points_whole==boundary_points_new[rows_to_fuse_boundary_points[i,0],:],axis=1))[0][0]
|
|
847
|
+
rows_to_fuse_boundary_points_whole[i,1] = np.where(np.all(boundary_points_whole==boundary_points_new[rows_to_fuse_boundary_points[i,1],:],axis=1))[0][0]
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
# delete rows with identical indices and reverse indices in finite_ridges_merged
|
|
851
|
+
zero_rows = np.where((boundary_ridges_merged[:,0] - boundary_ridges_merged[:,1]) == 0)[0]
|
|
852
|
+
boundary_ridges_merged = np.delete(boundary_ridges_merged,zero_rows,0)
|
|
853
|
+
|
|
854
|
+
# delete rows with identical indices and reverse indices in infinite_ridges_merged
|
|
855
|
+
zero_rows = np.where((infinite_ridges_merged[:,0] - infinite_ridges_merged[:,1]) == 0)[0]
|
|
856
|
+
infinite_ridges_merged = np.delete(infinite_ridges_merged,zero_rows,0)
|
|
857
|
+
|
|
858
|
+
# replace too close boundary points with the featured
|
|
859
|
+
tree_boundary_points_whole = cKDTree(boundary_points_whole)
|
|
860
|
+
for i in range(0,boundary_points_featured.shape[0]):
|
|
861
|
+
rows_to_replace_boundary_points_whole = tree_boundary_points_whole.query_ball_point(boundary_points_featured[i,:],merge_tol)
|
|
862
|
+
if len(rows_to_replace_boundary_points_whole) != 0:
|
|
863
|
+
# Visualize nearby points
|
|
864
|
+
nearby_points = boundary_points_whole[rows_to_replace_boundary_points_whole]
|
|
865
|
+
# replace indices
|
|
866
|
+
for j in range(0,len(rows_to_replace_boundary_points_whole)):
|
|
867
|
+
boundary_ridges_merged[boundary_ridges_merged == (rows_to_replace_boundary_points_whole[j]+nvertices_in)] = nvertices_in + nboundary_pts_ini + i
|
|
868
|
+
infinite_ridges_merged[infinite_ridges_merged == (rows_to_replace_boundary_points_whole[j]+nvertices_in)] = nvertices_in + nboundary_pts_ini + i
|
|
869
|
+
|
|
870
|
+
# delete rows with identical indices and reverse indices in boundary_ridges_merged
|
|
871
|
+
zero_rows = np.where((boundary_ridges_merged[:,0] - boundary_ridges_merged[:,1]) == 0)[0]
|
|
872
|
+
boundary_ridges_merged = np.delete(boundary_ridges_merged,zero_rows,0)
|
|
873
|
+
|
|
874
|
+
# delete rows with identical indices and reverse indices in infinite_ridges_merged
|
|
875
|
+
zero_rows = np.where((infinite_ridges_merged[:,0] - infinite_ridges_merged[:,1]) == 0)[0]
|
|
876
|
+
infinite_ridges_merged = np.delete(infinite_ridges_merged,zero_rows,0)
|
|
877
|
+
|
|
878
|
+
# Case 3 merge (after loop of case 1 and 2)
|
|
879
|
+
for i in range(0,len(boundary_points)):
|
|
880
|
+
rows_to_replace_voronoi_vertices_in = tree_voronoi_vertices_in.query_ball_point(boundary_points[i,:],merge_tol)
|
|
881
|
+
if len(rows_to_replace_voronoi_vertices_in) != 0:
|
|
882
|
+
# update the rows_to_place_voronoi_vertices_in with the rows-to-replace in list voronoi_vertices_in_whole
|
|
883
|
+
rows_to_replace_voronoi_vertices_in_whole = np.copy(rows_to_replace_voronoi_vertices_in)
|
|
884
|
+
for j in range(0,len(rows_to_replace_voronoi_vertices_in_whole)):
|
|
885
|
+
rows_to_replace_voronoi_vertices_in_whole[j] = np.where(np.all(voronoi_vertices_in_whole==voronoi_vertices_in[rows_to_replace_voronoi_vertices_in[j],:],axis=1))[0][0]
|
|
886
|
+
|
|
887
|
+
# Visualize nearby points
|
|
888
|
+
nearby_points = voronoi_vertices_in_whole[rows_to_replace_voronoi_vertices_in_whole]
|
|
889
|
+
# replace indices
|
|
890
|
+
for rows in rows_to_replace_voronoi_vertices_in_whole:
|
|
891
|
+
finite_ridges_merged[finite_ridges_merged == rows] = np.where(np.all(boundary_points_whole == boundary_points[i,:],axis=1))[0][0] + nvertices_in
|
|
892
|
+
infinite_ridges_merged[infinite_ridges_merged == rows] = np.where(np.all(boundary_points_whole == boundary_points[i,:],axis=1))[0][0] + nvertices_in
|
|
893
|
+
|
|
894
|
+
zero_rows = np.where((finite_ridges_merged[:,0] - finite_ridges_merged[:,1]) == 0)[0]
|
|
895
|
+
finite_ridges_merged = np.delete(finite_ridges_merged,zero_rows,0)
|
|
896
|
+
|
|
897
|
+
# delete rows with identical indices and reverse indices in infinite_ridges_merged
|
|
898
|
+
zero_rows = np.where((infinite_ridges_merged[:,0] - infinite_ridges_merged[:,1]) == 0)[0]
|
|
899
|
+
infinite_ridges_merged = np.delete(infinite_ridges_merged,zero_rows,0)
|
|
900
|
+
|
|
901
|
+
voronoi_vertices = np.vstack((voronoi_vertices_in,boundary_points)) # vertical stack "in" vetices and "cross" boundary points
|
|
902
|
+
voronoi_vertices_whole = np.vstack((voronoi_vertices_in_whole,boundary_points_whole)) # vertical stack "in" vetices and "cross" boundary points
|
|
903
|
+
|
|
904
|
+
all_ridges_merged = np.vstack((finite_ridges_merged,infinite_ridges_merged,boundary_ridges_merged))
|
|
905
|
+
|
|
906
|
+
else:
|
|
907
|
+
boundary_points_whole = np.copy(boundary_points)
|
|
908
|
+
voronoi_vertices_whole = np.vstack((voronoi_vertices_in_whole,boundary_points_whole)) # vertical stack "in" vetices and "cross" boundary points
|
|
909
|
+
all_ridges_merged = np.vstack((finite_ridges_merged,infinite_ridges_merged))
|
|
910
|
+
|
|
911
|
+
# Visualize merged mesh
|
|
912
|
+
for vpair in all_ridges_merged:
|
|
913
|
+
plt.plot([voronoi_vertices_whole[vpair[0],0], voronoi_vertices_whole[vpair[1],0]],[voronoi_vertices_whole[vpair[0],1], voronoi_vertices_whole[vpair[1],1]], 'g--')
|
|
914
|
+
|
|
915
|
+
|
|
916
|
+
voronoi_vertices = np.unique(np.reshape(voronoi_vertices_whole[all_ridges_merged],(-1,2)),axis=0)
|
|
917
|
+
# update the indices from voronoi_vertices_whole to voronoi_vertices(which is the list with no useless points)
|
|
918
|
+
for i in range(0,len(finite_ridges_merged)):
|
|
919
|
+
finite_ridges_merged[i,0] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[finite_ridges_merged[i,0],:],axis=1))[0][0]
|
|
920
|
+
finite_ridges_merged[i,1] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[finite_ridges_merged[i,1],:],axis=1))[0][0]
|
|
921
|
+
for i in range(0,len(infinite_ridges_merged)):
|
|
922
|
+
infinite_ridges_merged[i,0] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[infinite_ridges_merged[i,0],:],axis=1))[0][0]
|
|
923
|
+
infinite_ridges_merged[i,1] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[infinite_ridges_merged[i,1],:],axis=1))[0][0]
|
|
924
|
+
if boundaryFlag in ['on','On','Y','y','Yes','yes']:
|
|
925
|
+
for i in range(0,len(boundary_ridges_merged)):
|
|
926
|
+
boundary_ridges_merged[i,0] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[boundary_ridges_merged[i,0],:],axis=1))[0][0]
|
|
927
|
+
boundary_ridges_merged[i,1] = np.where(np.all(voronoi_vertices==voronoi_vertices_whole[boundary_ridges_merged[i,1],:],axis=1))[0][0]
|
|
928
|
+
# delete rows with identical indices and reverse indices in finite_ridges_merged
|
|
929
|
+
zero_rows = np.where((finite_ridges_merged[:,0] - finite_ridges_merged[:,1]) == 0)[0]
|
|
930
|
+
finite_ridges_merged = np.delete(finite_ridges_merged,zero_rows,0)
|
|
931
|
+
# delete rows with identical indices and reverse indices in infinite_ridges_merged
|
|
932
|
+
zero_rows = np.where((infinite_ridges_merged[:,0] - infinite_ridges_merged[:,1]) == 0)[0]
|
|
933
|
+
infinite_ridges_merged = np.delete(infinite_ridges_merged,zero_rows,0)
|
|
934
|
+
|
|
935
|
+
if boundaryFlag in ['on','On','Y','y','Yes','yes']:
|
|
936
|
+
# delete rows with identical indices and reverse indices in boundary_ridges_merged
|
|
937
|
+
zero_rows = np.where((boundary_ridges_merged[:,0] - boundary_ridges_merged[:,1]) == 0)[0]
|
|
938
|
+
boundary_ridges_merged = np.delete(boundary_ridges_merged,zero_rows,0)
|
|
939
|
+
|
|
940
|
+
# voronoi_vertices = voronoi_vertices_whole
|
|
941
|
+
nvertex = voronoi_vertices.shape[0]
|
|
942
|
+
finite_ridges_new = finite_ridges_merged
|
|
943
|
+
nfinite_ridge = finite_ridges_new.shape[0]
|
|
944
|
+
if boundaryFlag in ['on','On','Y','y','Yes','yes']:
|
|
945
|
+
boundary_ridges_new = np.vstack((infinite_ridges_merged,boundary_ridges_merged))
|
|
946
|
+
nboundary_ridge = boundary_ridges_new.shape[0]
|
|
947
|
+
voronoi_ridges = np.vstack((finite_ridges_new,boundary_ridges_new))
|
|
948
|
+
nridge = voronoi_ridges.shape[0]
|
|
949
|
+
|
|
950
|
+
intersect_temp = intersect2D(np.unique(np.reshape(voronoi_vertices_whole[finite_ridges_merged],(-1,2)),axis=0),np.unique(np.reshape(voronoi_vertices_whole[boundary_ridges_merged],(-1,2)),axis=0))
|
|
951
|
+
nvertices_in = np.unique(np.reshape(voronoi_vertices_whole[finite_ridges_merged],(-1,2)),axis=0).shape[0] - len(intersect_temp)
|
|
952
|
+
nboundary_pts = np.unique(np.reshape(voronoi_vertices_whole[boundary_ridges_merged],(-1,2)),axis=0).shape[0]
|
|
953
|
+
else:
|
|
954
|
+
boundary_ridges_new = np.copy(infinite_ridges_merged)
|
|
955
|
+
nboundary_ridge = boundary_ridges_new.shape[0]
|
|
956
|
+
voronoi_ridges = np.vstack((finite_ridges_new,boundary_ridges_new))
|
|
957
|
+
nridge = voronoi_ridges.shape[0]
|
|
958
|
+
|
|
959
|
+
nvertices_in = np.unique(np.reshape(voronoi_vertices_whole[finite_ridges_merged],(-1,2)),axis=0).shape[0]
|
|
960
|
+
|
|
961
|
+
return voronoi_vertices,finite_ridges,boundary_points,finite_ridges_new,\
|
|
962
|
+
boundary_ridges_new,nvertex,nvertices_in,nfinite_ridge,nboundary_ridge,\
|
|
963
|
+
nboundary_pts,nboundary_pts_featured,voronoi_ridges,nridge
|
|
964
|
+
|
|
965
|
+
|
|
966
|
+
def RidgeMidQuarterPts(voronoi_vertices,nvertex,nvertices_in,voronoi_ridges,\
|
|
967
|
+
finite_ridges_new,boundary_ridges_new,nfinite_ridge,\
|
|
968
|
+
nboundary_ridge,nboundary_pts,nboundary_pts_featured):
|
|
969
|
+
######################### For finite Voronoi ridges ###########################
|
|
970
|
+
# Form a list of middle points of finite Voronoi edges
|
|
971
|
+
finite_ridge_mid = []
|
|
972
|
+
finite_midpt_indices = []
|
|
973
|
+
count = nvertex
|
|
974
|
+
|
|
975
|
+
for vpair in finite_ridges_new:
|
|
976
|
+
midpoint = (voronoi_vertices[vpair[0]] + voronoi_vertices[vpair[1]])/2
|
|
977
|
+
finite_ridge_mid.append(midpoint)
|
|
978
|
+
finite_midpt_indices.append(count)
|
|
979
|
+
count += 1
|
|
980
|
+
|
|
981
|
+
finite_ridge_mid = np.tile(finite_ridge_mid, (2,1)) # duplicate the mid point list
|
|
982
|
+
finite_second_midpt_indices = [x+nfinite_ridge for x in finite_midpt_indices]
|
|
983
|
+
finite_midpt_indices = np.concatenate((finite_midpt_indices,finite_second_midpt_indices))
|
|
984
|
+
count += nfinite_ridge
|
|
985
|
+
nfinite_midpt = nfinite_ridge*2
|
|
986
|
+
|
|
987
|
+
# Form a list of quarter points of Voronoi edges
|
|
988
|
+
finite_ridge_quarter = []
|
|
989
|
+
finite_quarterpt_indices = []
|
|
990
|
+
for vpair in finite_ridges_new:
|
|
991
|
+
quarterpoint = 3./4*voronoi_vertices[vpair[0]] + 1./4*voronoi_vertices[vpair[1]]
|
|
992
|
+
finite_ridge_quarter.append(quarterpoint)
|
|
993
|
+
finite_quarterpt_indices.append(count)
|
|
994
|
+
count += 1
|
|
995
|
+
|
|
996
|
+
for vpair in finite_ridges_new:
|
|
997
|
+
quarterpoint = 1./4*voronoi_vertices[vpair[0]] + 3./4*voronoi_vertices[vpair[1]]
|
|
998
|
+
finite_ridge_quarter.append(quarterpoint)
|
|
999
|
+
finite_quarterpt_indices.append(count)
|
|
1000
|
+
count += 1
|
|
1001
|
+
finite_quarterpt_indices = np.asarray(finite_quarterpt_indices)
|
|
1002
|
+
|
|
1003
|
+
nfinite_quarterpt = nfinite_ridge*2
|
|
1004
|
+
|
|
1005
|
+
nfinitept_per_layer = nvertices_in + nfinite_midpt + nfinite_quarterpt
|
|
1006
|
+
|
|
1007
|
+
######################### For boundary Voronoi ridges #########################
|
|
1008
|
+
# Form a list of mid-points of boundary Voronoi edges
|
|
1009
|
+
boundary_ridge_mid = []
|
|
1010
|
+
boundary_midpt_indices = []
|
|
1011
|
+
for vpair in boundary_ridges_new:
|
|
1012
|
+
boundary_midpoint = (voronoi_vertices[vpair[0]] + voronoi_vertices[vpair[1]])/2
|
|
1013
|
+
boundary_ridge_mid.append(boundary_midpoint)
|
|
1014
|
+
boundary_midpt_indices.append(count)
|
|
1015
|
+
count += 1
|
|
1016
|
+
|
|
1017
|
+
boundary_ridge_mid = np.tile(boundary_ridge_mid, (2,1))
|
|
1018
|
+
boundary_second_midpt_indices = [x+nboundary_ridge for x in boundary_midpt_indices]
|
|
1019
|
+
boundary_midpt_indices = np.concatenate((boundary_midpt_indices,boundary_second_midpt_indices))
|
|
1020
|
+
count += nboundary_ridge
|
|
1021
|
+
nboundary_midpt = nboundary_ridge*2
|
|
1022
|
+
|
|
1023
|
+
# Form a list of quarter-points of boundary Voronoi edges
|
|
1024
|
+
boundary_ridge_quarter = []
|
|
1025
|
+
boundary_quarterpt_indices = []
|
|
1026
|
+
for vpair in boundary_ridges_new:
|
|
1027
|
+
boundary_quarterpoint = 3./4*voronoi_vertices[vpair[0]] + 1./4*voronoi_vertices[vpair[1]]
|
|
1028
|
+
boundary_ridge_quarter.append(boundary_quarterpoint)
|
|
1029
|
+
boundary_quarterpt_indices.append(count)
|
|
1030
|
+
count += 1
|
|
1031
|
+
|
|
1032
|
+
for vpair in boundary_ridges_new:
|
|
1033
|
+
boundary_quarterpoint = 1./4*voronoi_vertices[vpair[0]] + 3./4*voronoi_vertices[vpair[1]]
|
|
1034
|
+
boundary_ridge_quarter.append(boundary_quarterpoint)
|
|
1035
|
+
boundary_quarterpt_indices.append(count)
|
|
1036
|
+
count += 1
|
|
1037
|
+
boundary_quarterpt_indices = np.asarray(boundary_quarterpt_indices)
|
|
1038
|
+
|
|
1039
|
+
|
|
1040
|
+
nboundary_quarterpt = nboundary_ridge*2
|
|
1041
|
+
|
|
1042
|
+
nboundary_pt_per_layer = nboundary_pts + nboundary_midpt + nboundary_quarterpt
|
|
1043
|
+
|
|
1044
|
+
|
|
1045
|
+
npt_per_layer = nvertices_in + nboundary_pts
|
|
1046
|
+
npt_per_layer_normal = npt_per_layer - nboundary_pts_featured
|
|
1047
|
+
npt_per_layer_vtk = nfinitept_per_layer + nboundary_pt_per_layer
|
|
1048
|
+
|
|
1049
|
+
all_midpt_indices = np.vstack((np.reshape(finite_midpt_indices,(2,-1)).T,np.reshape(boundary_midpt_indices,(2,-1)).T))
|
|
1050
|
+
all_quarterpt_indices = np.vstack((np.reshape(finite_quarterpt_indices,(2,-1)).T,np.reshape(boundary_quarterpt_indices,(2,-1)).T))
|
|
1051
|
+
|
|
1052
|
+
all_pts_2D = np.vstack((voronoi_vertices,finite_ridge_mid,finite_ridge_quarter,boundary_ridge_mid,boundary_ridge_quarter))
|
|
1053
|
+
all_ridges = np.hstack((voronoi_ridges,all_midpt_indices,all_quarterpt_indices))
|
|
1054
|
+
|
|
1055
|
+
return all_pts_2D,all_ridges,npt_per_layer,npt_per_layer_normal,npt_per_layer_vtk
|
|
1056
|
+
|
|
1057
|
+
|
|
1058
|
+
|
|
1059
|
+
def VertexandRidgeinfo(all_pts_2D,all_ridges,npt_per_layer,npt_per_layer_normal,\
|
|
1060
|
+
npt_per_layer_vtk,nridge,geoName,radius,log_center,\
|
|
1061
|
+
cellwallthickness_early,cellwallthickness_late):
|
|
1062
|
+
|
|
1063
|
+
'''Generate the vertex and ridge info
|
|
1064
|
+
Vertex info includes: coordinates, ridge indices, indices of another vertex for each ridge, ridge lengths, ridge angles, ridge width
|
|
1065
|
+
Ridge info includes: 2 vertex indices, 2 mid point indices, 2 quarter point indices '''
|
|
1066
|
+
|
|
1067
|
+
|
|
1068
|
+
# Calculate lengths of all Voronoi ridges
|
|
1069
|
+
vector = all_pts_2D[all_ridges[:,1],:] - all_pts_2D[all_ridges[:,0],:]
|
|
1070
|
+
all_ridge_lengths = np.linalg.norm(vector, axis=1)
|
|
1071
|
+
|
|
1072
|
+
# Calculate angles of all Voronoi ridges (angles measured counter-clock wise, x-axis --> (1,0), y-axis --> (0,1))
|
|
1073
|
+
all_ridge_angles = np.arctan2(vector[:,1],vector[:,0]) # np.arctan2(y, x) * 180 / np.pi = the angle
|
|
1074
|
+
|
|
1075
|
+
#======== This part can be expanded to allow the smooth transition of cell wall thickness =========
|
|
1076
|
+
# Case 1: Abrupt transition of cell wall thickness between earlywood/latewood
|
|
1077
|
+
# thicknesses of all Voronoi vertices (here identical thickness for all wings belonging to the same vertex is assumed)
|
|
1078
|
+
vertex_cellwallthickness_2D = np.zeros(npt_per_layer)
|
|
1079
|
+
|
|
1080
|
+
vertex_distance2logcenter = np.linalg.norm(all_pts_2D - log_center, axis=1) # calc distance between vertex and logcenter
|
|
1081
|
+
for i in range(0,npt_per_layer):
|
|
1082
|
+
if (bisect.bisect(radius,vertex_distance2logcenter[i]) % 2) != 0: # if odd, earlywood
|
|
1083
|
+
vertex_cellwallthickness_2D[i] = cellwallthickness_early
|
|
1084
|
+
else: # if even, latewood
|
|
1085
|
+
vertex_cellwallthickness_2D[i] = cellwallthickness_late
|
|
1086
|
+
#==================================================================================================
|
|
1087
|
+
|
|
1088
|
+
# Generate a list containing info for each vertex
|
|
1089
|
+
all_vertices_info_2D = []
|
|
1090
|
+
all_direction_info_2D = []
|
|
1091
|
+
for i in range(0,npt_per_layer): # loop over all vertices
|
|
1092
|
+
|
|
1093
|
+
allrows = np.where(all_ridges == i)[0] # find all Voronoi ridges containing the ith vertex
|
|
1094
|
+
vertex_info = []
|
|
1095
|
+
vertex_info.append(len(allrows)) # append total number of wings for the ith vertex
|
|
1096
|
+
vertex_info.append(allrows) # append ridge indices
|
|
1097
|
+
vertex_info.append(all_ridges[allrows,0:2][np.nonzero(all_ridges[allrows,0:2]-i)]) # append the indices of vetices at the other end of each ridge
|
|
1098
|
+
direction_info = []
|
|
1099
|
+
direction = np.where(all_ridges[allrows,0:2] == i)[1] # the position of current vertex in each ridge, p1 --> 0, p2 --> 1
|
|
1100
|
+
direction = -np.power(-1,direction+1) # convert the way to express the direction of each ridge to: 1/-1 --> pointing from/to the current vertex
|
|
1101
|
+
direction_info.append(direction) # append ridge direction
|
|
1102
|
+
vertex_info.append(all_ridge_lengths[allrows]/2) # append wing lengths
|
|
1103
|
+
vertex_info.append(np.ones(len(allrows))*vertex_cellwallthickness_2D[i]) # append wing widths
|
|
1104
|
+
vertex_info.append(all_ridge_angles[allrows]+math.pi*(-direction.clip(max=0))) # append wing angles
|
|
1105
|
+
|
|
1106
|
+
all_vertices_info_2D.append(vertex_info)
|
|
1107
|
+
all_direction_info_2D.append(direction_info)
|
|
1108
|
+
|
|
1109
|
+
# flattened 1D numpy array of lengths, widths and angles of wings
|
|
1110
|
+
# ref:https://stackoverflow.com/questions/952914/how-do-i-make-a-flat-list-out-of-a-list-of-lists
|
|
1111
|
+
ridge_inices = [vertex_info[1] for vertex_info in all_vertices_info_2D]
|
|
1112
|
+
ridge_inices = np.array([item for sublist in ridge_inices for item in sublist])
|
|
1113
|
+
another_vertex_inices = [vertex_info[2] for vertex_info in all_vertices_info_2D]
|
|
1114
|
+
another_vertex_inices = np.array([item for sublist in another_vertex_inices for item in sublist])
|
|
1115
|
+
ridge_lengths = [vertex_info[3] for vertex_info in all_vertices_info_2D]
|
|
1116
|
+
ridge_lengths = np.array([item for sublist in ridge_lengths for item in sublist])
|
|
1117
|
+
ridge_widths = [vertex_info[4] for vertex_info in all_vertices_info_2D]
|
|
1118
|
+
ridge_widths = np.array([item for sublist in ridge_widths for item in sublist])
|
|
1119
|
+
ridge_angles = [vertex_info[5] for vertex_info in all_vertices_info_2D]
|
|
1120
|
+
ridge_angles = np.array([item for sublist in ridge_angles for item in sublist])
|
|
1121
|
+
ridge_directions = [direction_info[0] for direction_info in all_direction_info_2D]
|
|
1122
|
+
ridge_directions = np.array([item for sublist in ridge_directions for item in sublist])
|
|
1123
|
+
|
|
1124
|
+
flattened_all_vertices_2D = np.vstack((ridge_inices,another_vertex_inices,ridge_lengths,ridge_widths,ridge_angles,ridge_directions))
|
|
1125
|
+
|
|
1126
|
+
# Convert ragged info list to a rectangular shape numpy array
|
|
1127
|
+
max_wings = max([vertex_info[0] for vertex_info in all_vertices_info_2D])
|
|
1128
|
+
|
|
1129
|
+
all_vertices_info_2D_nparray = np.zeros((npt_per_layer,1+max_wings*5))
|
|
1130
|
+
all_vertices_info_2D_nparray[:,0] = [vertex_info[0] for vertex_info in all_vertices_info_2D]
|
|
1131
|
+
|
|
1132
|
+
for i in range(0,npt_per_layer):
|
|
1133
|
+
nwings = all_vertices_info_2D[i][0]
|
|
1134
|
+
all_vertices_info_2D_nparray[i,1:nwings+1] = all_vertices_info_2D[i][1]
|
|
1135
|
+
all_vertices_info_2D_nparray[i,max_wings+1:max_wings+nwings+1] = all_vertices_info_2D[i][2]
|
|
1136
|
+
all_vertices_info_2D_nparray[i,2*max_wings+1:2*max_wings+nwings+1] = all_vertices_info_2D[i][3]
|
|
1137
|
+
all_vertices_info_2D_nparray[i,3*max_wings+1:3*max_wings+nwings+1] = all_vertices_info_2D[i][4]
|
|
1138
|
+
all_vertices_info_2D_nparray[i,4*max_wings+1:4*max_wings+nwings+1] = all_vertices_info_2D[i][5]
|
|
1139
|
+
|
|
1140
|
+
# Save info to txt files
|
|
1141
|
+
all_vertices_2D = np.hstack((all_pts_2D[0:npt_per_layer,:],all_vertices_info_2D_nparray))
|
|
1142
|
+
np.savetxt(Path('meshes/' + geoName + '/' + geoName +'-vertex.mesh'), all_vertices_2D, fmt='%.16g', delimiter=' '\
|
|
1143
|
+
,header='Vertex Data Generated with Wood Mesh Generation Tool\n\
|
|
1144
|
+
Number of vertices\n'+ str(npt_per_layer) +
|
|
1145
|
+
'\n\
|
|
1146
|
+
Max number of wings for one vertex\n'+ str(max_wings) +
|
|
1147
|
+
'\n\
|
|
1148
|
+
[xcoord ycoord nwings ridge1 ... ridgen farvertex1 ... farvertexn length1 ... lengthn width1 ... widthn angle1 ... anglen]', comments='')
|
|
1149
|
+
|
|
1150
|
+
np.savetxt(Path('meshes/' + geoName + '/' + geoName +'-ridge.mesh'), all_ridges, fmt='%d', delimiter=' '\
|
|
1151
|
+
,header='Ridge Data Generated with Wood Mesh Generation Tool\n\
|
|
1152
|
+
Number of ridges\n'+ str(nridge) +
|
|
1153
|
+
'\n\
|
|
1154
|
+
[vertex1 vertex2 midpt1 midpt2 qrtrpt1 qrtrpt2]', comments='')
|
|
1155
|
+
|
|
1156
|
+
return all_vertices_2D, max_wings, flattened_all_vertices_2D, all_ridges
|
|
1157
|
+
|
|
1158
|
+
|
|
1159
|
+
def GenerateBeamElement(NURBS_degree,nctrlpt_per_beam,fiberlength,theta_min,theta_max,\
|
|
1160
|
+
z_min,z_max,long_connector_ratio,npt_per_layer,voronoi_vertices,\
|
|
1161
|
+
nvertex,voronoi_ridges,nridge,log_center,all_vertices_2D,max_wings,\
|
|
1162
|
+
flattened_all_vertices_2D,all_ridges):
|
|
1163
|
+
|
|
1164
|
+
|
|
1165
|
+
nctrlpt_per_elem = NURBS_degree + 1
|
|
1166
|
+
nbeam_per_grain = int(round((z_max-z_min)/fiberlength))
|
|
1167
|
+
|
|
1168
|
+
nlayer = nctrlpt_per_beam*nbeam_per_grain
|
|
1169
|
+
|
|
1170
|
+
nconnector_t_per_beam = int((nctrlpt_per_beam-1)/NURBS_degree+1)
|
|
1171
|
+
nconnector_t_per_grain = int(nconnector_t_per_beam*nbeam_per_grain)
|
|
1172
|
+
|
|
1173
|
+
theta = np.linspace(theta_min,theta_max,nlayer-(nbeam_per_grain-1))
|
|
1174
|
+
z_coord = np.linspace(z_min,z_max,nlayer-(nbeam_per_grain-1))
|
|
1175
|
+
connector_l_length = fiberlength*long_connector_ratio
|
|
1176
|
+
connector_l_angle = (theta_max-theta_min)/nbeam_per_grain*long_connector_ratio
|
|
1177
|
+
|
|
1178
|
+
# Insert repeated layers for the longitudinal connectors
|
|
1179
|
+
for i in range(nlayer-(nbeam_per_grain-1)-(nctrlpt_per_beam-1),1,-(nctrlpt_per_beam-1)):
|
|
1180
|
+
theta = np.insert(theta,i,theta[i-1])
|
|
1181
|
+
theta[i:] += connector_l_angle
|
|
1182
|
+
for i in range(nlayer-(nbeam_per_grain-1)-(nctrlpt_per_beam-1),1,-(nctrlpt_per_beam-1)):
|
|
1183
|
+
z_coord = np.insert(z_coord,i,z_coord[i-1])
|
|
1184
|
+
z_coord[i:] += connector_l_length
|
|
1185
|
+
|
|
1186
|
+
# Data for IGA use
|
|
1187
|
+
vertices_new = np.zeros((nlayer,npt_per_layer,3))
|
|
1188
|
+
for i in range(0,nlayer):
|
|
1189
|
+
for j in range(0,npt_per_layer):
|
|
1190
|
+
vertices_new[i,j,:2] = rotate_around_point_highperf(voronoi_vertices[j,:], theta[i], log_center)
|
|
1191
|
+
vertices_new[i,j,2] = z_coord[i]
|
|
1192
|
+
|
|
1193
|
+
# Vertex Data for IGA
|
|
1194
|
+
woodIGAvertices = np.reshape(vertices_new,(-1,3))
|
|
1195
|
+
|
|
1196
|
+
# Connectivity for IGA
|
|
1197
|
+
npt_total = nlayer*npt_per_layer
|
|
1198
|
+
vertex_connectivity = np.linspace(0,npt_total-1,npt_total)
|
|
1199
|
+
|
|
1200
|
+
# Beams
|
|
1201
|
+
ngrain = nvertex
|
|
1202
|
+
nbeam_total = ngrain*nbeam_per_grain
|
|
1203
|
+
beam_connectivity_original = np.zeros((nbeam_total,nctrlpt_per_beam))
|
|
1204
|
+
for i in range(0,nbeam_per_grain):
|
|
1205
|
+
for j in range(0, ngrain):
|
|
1206
|
+
irow = i*ngrain + j
|
|
1207
|
+
ivertex = i*npt_per_layer*nctrlpt_per_beam + j
|
|
1208
|
+
for icol in range(0,nctrlpt_per_beam):
|
|
1209
|
+
beam_connectivity_original[irow,icol] = ivertex+icol*npt_per_layer
|
|
1210
|
+
|
|
1211
|
+
# Rearange beam connectivity such that each row is corresponding to a Bezier beam element
|
|
1212
|
+
beam_connectivity = np.copy(beam_connectivity_original)
|
|
1213
|
+
for ictrlpt in range(nctrlpt_per_beam-NURBS_degree,1,-NURBS_degree):
|
|
1214
|
+
beam_connectivity = np.insert(beam_connectivity,ictrlpt,beam_connectivity[:,ictrlpt-1],axis=1)
|
|
1215
|
+
|
|
1216
|
+
beam_connectivity_original = (beam_connectivity_original+1).astype(int) # +1 because of in abaqus index starts from 1
|
|
1217
|
+
beam_connectivity = (np.reshape(beam_connectivity,(-1,nctrlpt_per_elem))+1).astype(int) # +1 because of in abaqus index starts from 1
|
|
1218
|
+
|
|
1219
|
+
|
|
1220
|
+
nbeamElem = beam_connectivity.shape[0]
|
|
1221
|
+
|
|
1222
|
+
|
|
1223
|
+
# Transverse connectors
|
|
1224
|
+
nconnector_t = nridge*nconnector_t_per_grain
|
|
1225
|
+
connector_t_connectivity = np.zeros((nconnector_t,2))
|
|
1226
|
+
for i in range(0,nbeam_per_grain):
|
|
1227
|
+
for j in range(0,nconnector_t_per_beam):
|
|
1228
|
+
for k in range(0,nridge):
|
|
1229
|
+
irow = i*nconnector_t_per_beam*nridge + j*nridge + k
|
|
1230
|
+
connector_t_connectivity[irow,:] = voronoi_ridges[k]+i*npt_per_layer*nctrlpt_per_beam+j*NURBS_degree*npt_per_layer
|
|
1231
|
+
|
|
1232
|
+
connector_t_connectivity = (connector_t_connectivity+1).astype(int)
|
|
1233
|
+
# Slicing block indices, https://stackoverflow.com/questions/39692769/efficient-numpy-indexing-take-first-n-rows-of-every-block-of-m-rows
|
|
1234
|
+
connector_t_index = np.linspace(0,nconnector_t-1,nconnector_t).astype(int)
|
|
1235
|
+
connector_t_bot_index = connector_t_index[np.mod(np.arange(connector_t_index.size),nridge*nconnector_t_per_beam)<nridge]
|
|
1236
|
+
connector_t_top_index = connector_t_index[np.mod(np.arange(connector_t_index.size),nridge*nconnector_t_per_beam)>=(nconnector_t_per_beam-1)*nridge]
|
|
1237
|
+
connector_t_reg_index = np.setdiff1d(np.setdiff1d(connector_t_index, connector_t_bot_index),connector_t_top_index)
|
|
1238
|
+
connector_t_bot_connectivity = np.copy(connector_t_connectivity)[connector_t_bot_index,:]
|
|
1239
|
+
connector_t_top_connectivity = np.copy(connector_t_connectivity)[connector_t_top_index,:]
|
|
1240
|
+
connector_t_reg_connectivity = np.copy(connector_t_connectivity)[connector_t_reg_index,:]
|
|
1241
|
+
|
|
1242
|
+
|
|
1243
|
+
# Longitudinal connectors
|
|
1244
|
+
nwings = all_vertices_2D[:,2].astype(int)
|
|
1245
|
+
nconnector_l_per_layer = sum(nwings)
|
|
1246
|
+
nconnector_l = nconnector_l_per_layer * (nbeam_per_grain-1)
|
|
1247
|
+
connector_l_connectivity = np.zeros((nconnector_l,2))
|
|
1248
|
+
connector_l_vertex_dict = np.zeros(nconnector_l)
|
|
1249
|
+
irow_conn_l = 0
|
|
1250
|
+
for i in range(0,nbeam_per_grain-1): # loop over layers of longitudinal connectors
|
|
1251
|
+
for j in range(0,ngrain): # loop over grains in each layer
|
|
1252
|
+
n = nwings[j]
|
|
1253
|
+
irow = i*ngrain + j
|
|
1254
|
+
for k in range(0,n): # loop over wings of each grain
|
|
1255
|
+
connector_l_connectivity[irow_conn_l,:] = (beam_connectivity_original[irow,-1],beam_connectivity_original[irow,-1]+npt_per_layer)
|
|
1256
|
+
connector_l_vertex_dict[irow_conn_l] = irow
|
|
1257
|
+
irow_conn_l += 1
|
|
1258
|
+
|
|
1259
|
+
connector_l_vertex_dict = connector_l_vertex_dict.astype(int)
|
|
1260
|
+
connector_l_connectivity = connector_l_connectivity.astype(int)
|
|
1261
|
+
|
|
1262
|
+
nconnector_total = nconnector_t + nconnector_l
|
|
1263
|
+
|
|
1264
|
+
return woodIGAvertices,vertex_connectivity,beam_connectivity_original,nbeam_total,\
|
|
1265
|
+
beam_connectivity,nbeamElem,nlayer,connector_t_connectivity,\
|
|
1266
|
+
connector_t_bot_connectivity,connector_t_top_connectivity,\
|
|
1267
|
+
connector_t_reg_connectivity,connector_l_connectivity,nconnector_t_per_beam,\
|
|
1268
|
+
nconnector_t_per_grain,nconnector_t,nconnector_l,nconnector_total,\
|
|
1269
|
+
theta,z_coord,nbeam_per_grain,connector_l_vertex_dict
|
|
1270
|
+
|
|
1271
|
+
|
|
1272
|
+
def ConnectorMeshFile(geoName,woodIGAvertices,connector_t_bot_connectivity,\
|
|
1273
|
+
connector_t_reg_connectivity,connector_t_top_connectivity,\
|
|
1274
|
+
height_connector_t,connector_l_connectivity,all_vertices_2D,\
|
|
1275
|
+
max_wings,flattened_all_vertices_2D,nbeam_per_grain,nridge,\
|
|
1276
|
+
connector_l_vertex_dict):
|
|
1277
|
+
######### txt File stores the connector data for Abaqus analyses ##############
|
|
1278
|
+
nelem_connector_t_bot = connector_t_bot_connectivity.shape[0]
|
|
1279
|
+
nelem_connector_t_reg = connector_t_reg_connectivity.shape[0]
|
|
1280
|
+
nelem_connector_t_top = connector_t_top_connectivity.shape[0]
|
|
1281
|
+
nelem_connector_l = connector_l_connectivity.shape[0]
|
|
1282
|
+
nelem_total = nelem_connector_t_bot + nelem_connector_t_reg + nelem_connector_t_top + nelem_connector_l
|
|
1283
|
+
|
|
1284
|
+
conn_l_ridge_index = flattened_all_vertices_2D[0,:].astype(int)
|
|
1285
|
+
conn_l_lengths = flattened_all_vertices_2D[2,:]
|
|
1286
|
+
# conn_l_widths = flattened_all_vertices_2D[3,:]
|
|
1287
|
+
conn_l_angles = flattened_all_vertices_2D[4,:]
|
|
1288
|
+
rot = np.array([[np.cos(conn_l_angles), -np.sin(conn_l_angles)], [np.sin(conn_l_angles), np.cos(conn_l_angles)]]).T
|
|
1289
|
+
rot = np.hstack((rot[:,0,:],np.zeros(len(conn_l_angles))[:, np.newaxis])) # convert to 3D rotation matrix, assumes rotation remains still in-plane
|
|
1290
|
+
conn_l_tangents = rot
|
|
1291
|
+
|
|
1292
|
+
# Meshdata = [nodex1 nodey1 nodez1 nodex2 nodey2 nodez2 centerx centery centerz dx1 dy1 dz1 dx2 dy2 dz2 width height]
|
|
1293
|
+
Meshdata = np.zeros((nelem_total,17))
|
|
1294
|
+
|
|
1295
|
+
for i in range(0,nelem_connector_t_bot):
|
|
1296
|
+
Meshdata[i,0:3] = np.copy(woodIGAvertices)[connector_t_bot_connectivity[i,0]-1,:]
|
|
1297
|
+
Meshdata[i,3:6] = np.copy(woodIGAvertices)[connector_t_bot_connectivity[i,1]-1,:]
|
|
1298
|
+
Meshdata[i,16] = height_connector_t
|
|
1299
|
+
for i in range(0,nelem_connector_t_reg):
|
|
1300
|
+
Meshdata[i+nelem_connector_t_bot,0:3] = np.copy(woodIGAvertices)[connector_t_reg_connectivity[i,0]-1,:]
|
|
1301
|
+
Meshdata[i+nelem_connector_t_bot,3:6] = np.copy(woodIGAvertices)[connector_t_reg_connectivity[i,1]-1,:]
|
|
1302
|
+
Meshdata[i+nelem_connector_t_bot,16] = height_connector_t*2
|
|
1303
|
+
for i in range(0,nelem_connector_t_top):
|
|
1304
|
+
Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg,0:3] = np.copy(woodIGAvertices)[connector_t_top_connectivity[i,0]-1,:]
|
|
1305
|
+
Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg,3:6] = np.copy(woodIGAvertices)[connector_t_top_connectivity[i,1]-1,:]
|
|
1306
|
+
Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg,16] = height_connector_t
|
|
1307
|
+
|
|
1308
|
+
for i in range(0,nelem_connector_l):
|
|
1309
|
+
Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg+nelem_connector_t_top,0:3] = np.copy(woodIGAvertices)[connector_l_connectivity[i,0]-1,:]
|
|
1310
|
+
Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg+nelem_connector_t_top,3:6] = np.copy(woodIGAvertices)[connector_l_connectivity[i,1]-1,:]
|
|
1311
|
+
Meshdata[i+nelem_connector_t_bot+nelem_connector_t_reg+nelem_connector_t_top,16] = conn_l_lengths[i]
|
|
1312
|
+
|
|
1313
|
+
Meshdata[:,6:9] = (Meshdata[:,0:3] + Meshdata[:,3:6])/2
|
|
1314
|
+
Meshdata[0:nelem_connector_t_bot,8] = Meshdata[0:nelem_connector_t_bot,8]
|
|
1315
|
+
Meshdata[nelem_total-nelem_connector_t_top:nelem_total,8] = Meshdata[nelem_total-nelem_connector_t_top:nelem_total,8]
|
|
1316
|
+
Meshdata[:,9:12] = Meshdata[:,6:9] - Meshdata[:,0:3]
|
|
1317
|
+
Meshdata[:,12:15] = Meshdata[:,6:9] - Meshdata[:,3:6]
|
|
1318
|
+
|
|
1319
|
+
# Add z-variation/random field for cellwall thicknesses/connector widths
|
|
1320
|
+
for i in range(0,nridge):
|
|
1321
|
+
Meshdata[i,15] = all_vertices_2D[connector_t_bot_connectivity[i,1]-1,3+max_wings*3] # assign widths to all bot transverse connectors
|
|
1322
|
+
Meshdata[:nelem_total-nelem_connector_l,15] = np.tile(Meshdata[0:nridge,15],3*nbeam_per_grain) # use the same widths for reg and top transverse connectors
|
|
1323
|
+
|
|
1324
|
+
conn_l_widths = Meshdata[conn_l_ridge_index,15]
|
|
1325
|
+
Meshdata[nelem_total-nelem_connector_l:nelem_total,15] = conn_l_widths
|
|
1326
|
+
|
|
1327
|
+
# add the eccentricity to the centers for longitudinal connectors (new center is correct)
|
|
1328
|
+
Meshdata[nelem_total-nelem_connector_l:nelem_total,6:9] += conn_l_tangents*Meshdata[nelem_total-nelem_connector_l:nelem_total,16][:, np.newaxis]/2
|
|
1329
|
+
|
|
1330
|
+
# Replace nodal coordinates with nodal indices
|
|
1331
|
+
Meshdata[:,0:2] = np.concatenate((connector_t_bot_connectivity,connector_t_reg_connectivity,connector_t_top_connectivity,connector_l_connectivity))
|
|
1332
|
+
Meshdata = np.delete(Meshdata,[2,3,4,5],1)
|
|
1333
|
+
|
|
1334
|
+
np.savetxt(Path('meshes/' + geoName + '/' + geoName+'-mesh.txt'), Meshdata, fmt='%.16g', delimiter=' '\
|
|
1335
|
+
,header='# Connector Data Generated with Wood Mesh Generation Tool\n\
|
|
1336
|
+
Number of bot connectors\n'+ str(nelem_connector_t_bot) +
|
|
1337
|
+
'\n\
|
|
1338
|
+
Number of reg connectors\n'+ str(nelem_connector_t_reg) +
|
|
1339
|
+
'\n\
|
|
1340
|
+
Number of top connectors\n'+ str(nelem_connector_t_top) +
|
|
1341
|
+
'\n\
|
|
1342
|
+
Number of long connectors\n'+ str(nelem_connector_l) +
|
|
1343
|
+
'\n\
|
|
1344
|
+
[inode jnode centerx centery centerz dx1 dy1 dz1 dx2 dy2 dz2 width height]', comments='')
|
|
1345
|
+
|
|
1346
|
+
return Meshdata
|
|
1347
|
+
|
|
1348
|
+
|
|
1349
|
+
def insert_precracks(all_pts_2D,all_ridges,nridge,npt_per_layer,\
|
|
1350
|
+
npt_per_layer_normal,npt_per_layer_vtk,\
|
|
1351
|
+
nlayer,precrack_nodes,precrack_widths,\
|
|
1352
|
+
cellsize_early,):
|
|
1353
|
+
|
|
1354
|
+
precrack_midpts = (precrack_nodes[:,0:2]+precrack_nodes[:,2:4])/2.0
|
|
1355
|
+
ridge_midpts = all_pts_2D[all_ridges[:,2]]
|
|
1356
|
+
ridge_midpts_tree = cKDTree(ridge_midpts)
|
|
1357
|
+
near_ridges = []
|
|
1358
|
+
for midpt in precrack_midpts:
|
|
1359
|
+
near_ridges.append(ridge_midpts_tree.query_ball_point(midpt,3*cellsize_early))
|
|
1360
|
+
|
|
1361
|
+
# Find the intersect point of neighboring ridges (lines) with the precrack line
|
|
1362
|
+
precracked_elem = []
|
|
1363
|
+
for i in range(0,len(precrack_nodes)):
|
|
1364
|
+
p = precrack_nodes[i,0:2]
|
|
1365
|
+
normal = (precrack_nodes[i,2:4] - p)/np.linalg.norm(precrack_nodes[i,2:4] - p)
|
|
1366
|
+
for ridge in near_ridges[i]:
|
|
1367
|
+
q = all_pts_2D[all_ridges[ridge,0],:]
|
|
1368
|
+
s = all_pts_2D[all_ridges[ridge,1],:] - q
|
|
1369
|
+
u = np.cross((q-p),normal)/np.cross(normal,s)
|
|
1370
|
+
if (u >= 0) and (u <= 1):
|
|
1371
|
+
precracked_elem.append(ridge)
|
|
1372
|
+
|
|
1373
|
+
# Visualize the precracks in the preview plot
|
|
1374
|
+
plt.plot(precrack_nodes[0,0::2],precrack_nodes[0,1::2],'r-',linewidth=2)
|
|
1375
|
+
return precracked_elem
|
|
1376
|
+
|
|
1377
|
+
|
|
1378
|
+
def VisualizationFiles(geoName,NURBS_degree,nlayer,npt_per_layer_vtk,all_pts_2D,\
|
|
1379
|
+
fiberlength,theta,z_coord,nbeam_per_grain,nridge,\
|
|
1380
|
+
voronoi_ridges,log_center,all_ridges,nvertex,\
|
|
1381
|
+
nconnector_t,nconnector_l,nctrlpt_per_beam,ConnMeshData,\
|
|
1382
|
+
all_vertices_2D,max_wings,flattened_all_vertices_2D):
|
|
1383
|
+
ngrain = nvertex
|
|
1384
|
+
ninterval_per_beam_vtk = int((nctrlpt_per_beam-1)/2) # 2 layers ----> 1 interval
|
|
1385
|
+
|
|
1386
|
+
nconnector_t_per_beam = int((nctrlpt_per_beam-1)/NURBS_degree+1)
|
|
1387
|
+
|
|
1388
|
+
# Data for VTK use
|
|
1389
|
+
vertices_new = np.zeros((nlayer,npt_per_layer_vtk,3))
|
|
1390
|
+
for i in range(0,nlayer):
|
|
1391
|
+
for j in range(0,npt_per_layer_vtk):
|
|
1392
|
+
vertices_new[i,j,:2] = rotate_around_point_highperf(all_pts_2D[j,:], theta[i], log_center)
|
|
1393
|
+
vertices_new[i,j,2] = z_coord[i]
|
|
1394
|
+
|
|
1395
|
+
# Vertex Data for VTK use
|
|
1396
|
+
woodVTKvertices = np.reshape(vertices_new,(-1,3))
|
|
1397
|
+
|
|
1398
|
+
# Connectivity for VTK use
|
|
1399
|
+
# Vertex
|
|
1400
|
+
npt_total_vtk = nlayer*npt_per_layer_vtk
|
|
1401
|
+
vertex_connectivity_vtk = np.linspace(0,npt_total_vtk-1,npt_total_vtk)
|
|
1402
|
+
|
|
1403
|
+
# Beam
|
|
1404
|
+
nbeam_total_vtk = ngrain*nbeam_per_grain*ninterval_per_beam_vtk
|
|
1405
|
+
beam_connectivity_vtk = np.zeros((nbeam_total_vtk,3)) # 3 is the number of points per beam in Paraview
|
|
1406
|
+
for i in range(0,nbeam_per_grain):
|
|
1407
|
+
for j in range(0,ninterval_per_beam_vtk):
|
|
1408
|
+
for k in range(0,ngrain):
|
|
1409
|
+
irow = i*ninterval_per_beam_vtk*ngrain + j*ngrain + k
|
|
1410
|
+
ivertex = (i + (i*ninterval_per_beam_vtk+j)*2)*npt_per_layer_vtk + k
|
|
1411
|
+
beam_connectivity_vtk[irow,:] = (ivertex,ivertex+2*npt_per_layer_vtk,ivertex+npt_per_layer_vtk)
|
|
1412
|
+
|
|
1413
|
+
# Transverse connectors
|
|
1414
|
+
connector_t_connectivity_vtk = np.zeros((nconnector_t,2))
|
|
1415
|
+
for i in range(0,nbeam_per_grain):
|
|
1416
|
+
for j in range(0,nconnector_t_per_beam):
|
|
1417
|
+
for k in range(0,nridge):
|
|
1418
|
+
irow = i*nconnector_t_per_beam*nridge + j*nridge + k
|
|
1419
|
+
connector_t_connectivity_vtk[irow,:] = (voronoi_ridges[k][0]+i*npt_per_layer_vtk*nctrlpt_per_beam+j*NURBS_degree*npt_per_layer_vtk,voronoi_ridges[k][1]+i*npt_per_layer_vtk*nctrlpt_per_beam+j*NURBS_degree*npt_per_layer_vtk)
|
|
1420
|
+
|
|
1421
|
+
# Longitudinal connectors
|
|
1422
|
+
connector_l_connectivity_vtk = np.zeros((nconnector_l,2))
|
|
1423
|
+
nwings = all_vertices_2D[:,2].astype(int)
|
|
1424
|
+
irow_conn_l = 0
|
|
1425
|
+
for i in range(0,nbeam_per_grain-1): # loop over layers of longitudinal connectors
|
|
1426
|
+
for j in range(0,ngrain): # loop over grains in each layer
|
|
1427
|
+
nw = nwings[j]
|
|
1428
|
+
irow = i*ngrain + j
|
|
1429
|
+
for k in range(0,nw): # loop over wings of each grain
|
|
1430
|
+
connector_l_connectivity_vtk[irow_conn_l,:] = (beam_connectivity_vtk[irow+(ninterval_per_beam_vtk-1)*ngrain,1],beam_connectivity_vtk[irow+(ninterval_per_beam_vtk-1)*ngrain,1]+npt_per_layer_vtk)
|
|
1431
|
+
irow_conn_l += 1
|
|
1432
|
+
|
|
1433
|
+
# Quad - Beam Wings
|
|
1434
|
+
nquad = nridge*2
|
|
1435
|
+
nquad_total = nquad*nbeam_per_grain*ninterval_per_beam_vtk
|
|
1436
|
+
quad_connectivity = np.zeros((nquad_total,8))
|
|
1437
|
+
for i in range(0,nbeam_per_grain):
|
|
1438
|
+
for j in range(0,ninterval_per_beam_vtk):
|
|
1439
|
+
for k in range(0,nridge):
|
|
1440
|
+
for l in range(0,2):
|
|
1441
|
+
irow = i*ninterval_per_beam_vtk*nquad + j*nquad + l*nridge + k
|
|
1442
|
+
ivertex = i + (i*ninterval_per_beam_vtk+j)*2
|
|
1443
|
+
quad_connectivity[irow,:] = (all_ridges[k][l]+ivertex*npt_per_layer_vtk,all_ridges[k][l]+(ivertex+2)*npt_per_layer_vtk, \
|
|
1444
|
+
all_ridges[k][l+2]+(ivertex+2)*npt_per_layer_vtk,all_ridges[k][l+2]+ivertex*npt_per_layer_vtk, \
|
|
1445
|
+
all_ridges[k][l]+(ivertex+1)*npt_per_layer_vtk,all_ridges[k][l+4]+(ivertex+2)*npt_per_layer_vtk, \
|
|
1446
|
+
all_ridges[k][l+2]+(ivertex+1)*npt_per_layer_vtk,all_ridges[k][l+4]+ivertex*npt_per_layer_vtk)
|
|
1447
|
+
|
|
1448
|
+
# Quad - Connector Cross-sections
|
|
1449
|
+
nquad_conn = nconnector_t + nconnector_l
|
|
1450
|
+
nquad_conn_total = nquad_conn
|
|
1451
|
+
|
|
1452
|
+
conn_l_angles = flattened_all_vertices_2D[4,:]
|
|
1453
|
+
|
|
1454
|
+
Quad_center = np.copy(ConnMeshData[:,2:5])
|
|
1455
|
+
Quad_normal1 = np.copy(ConnMeshData[:,5:8])
|
|
1456
|
+
Quad_normal2 = np.copy(ConnMeshData[:,8:11])
|
|
1457
|
+
Quad_length1 = np.linalg.norm(Quad_normal1, axis=1)
|
|
1458
|
+
Quad_length2 = np.linalg.norm(Quad_normal2, axis=1)
|
|
1459
|
+
Quad_width = np.copy(ConnMeshData[:,11])
|
|
1460
|
+
Quad_height = np.copy(ConnMeshData[:,12])
|
|
1461
|
+
Quad_normal = Quad_normal1/Quad_length1[:, np.newaxis]
|
|
1462
|
+
Quad_tangent = np.zeros((nquad_conn_total,3))
|
|
1463
|
+
Quad_tangent[0:nconnector_t] = np.tile(np.array([0.0,0.0,1.0]),(nconnector_t,1)) # assume a tangent of (0,0,1) for all conn_t
|
|
1464
|
+
|
|
1465
|
+
rot = np.array([[np.cos(conn_l_angles), -np.sin(conn_l_angles)], [np.sin(conn_l_angles), np.cos(conn_l_angles)]]).T
|
|
1466
|
+
rot = np.hstack((rot[:,0,:],np.zeros(len(conn_l_angles))[:, np.newaxis])) # convert to 3D rotation matrix, assumes rotation remains still in-plane
|
|
1467
|
+
Quad_tangent[nconnector_t:nquad_conn] = rot
|
|
1468
|
+
Quad_bitangent = np.cross(Quad_normal,Quad_tangent)
|
|
1469
|
+
|
|
1470
|
+
nconnector_t_bot = int(nconnector_t/3)
|
|
1471
|
+
nconnector_t_top = int(nconnector_t/3)
|
|
1472
|
+
# Add eccentricities to bot/top transverse connectors
|
|
1473
|
+
Quad_center[0:nconnector_t_bot,:] = Quad_center[0:nconnector_t_bot,:] + Quad_tangent[0:nconnector_t_bot,:]*Quad_height[0:nconnector_t_bot,np.newaxis]/2 # bot connectors
|
|
1474
|
+
Quad_center[(nconnector_t-nconnector_t_top):nconnector_t,:] = Quad_center[(nconnector_t-nconnector_t_top):nconnector_t,:] - Quad_tangent[(nconnector_t-nconnector_t_top):nconnector_t,:]*Quad_height[(nconnector_t-nconnector_t_top):nconnector_t,np.newaxis]/2
|
|
1475
|
+
|
|
1476
|
+
quad_conn_vertices = np.zeros((nquad_conn_total*4,3))
|
|
1477
|
+
quad_conn_vertices[0:nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2
|
|
1478
|
+
quad_conn_vertices[nquad_conn_total:2*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2
|
|
1479
|
+
quad_conn_vertices[2*nquad_conn_total:3*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2
|
|
1480
|
+
quad_conn_vertices[3*nquad_conn_total:4*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2
|
|
1481
|
+
|
|
1482
|
+
quad_conn_connectivity = np.linspace(0,nquad_conn_total*4-1,nquad_conn_total*4)
|
|
1483
|
+
quad_conn_connectivity = np.reshape(quad_conn_connectivity, [4,-1]).T
|
|
1484
|
+
quad_conn_connectivity = quad_conn_connectivity + npt_total_vtk
|
|
1485
|
+
|
|
1486
|
+
# Modify Vertex Data by adding quad_conn_vertices
|
|
1487
|
+
woodVTKvertices_quad_conn = np.vstack((woodVTKvertices,quad_conn_vertices))
|
|
1488
|
+
npt_total_vtk_quad_conn = woodVTKvertices_quad_conn.shape[0]
|
|
1489
|
+
vertex_connectivity_vtk_quad_conn = np.linspace(0,npt_total_vtk_quad_conn-1,npt_total_vtk_quad_conn)
|
|
1490
|
+
|
|
1491
|
+
# Hex - Connector Volumes
|
|
1492
|
+
nhex_conn_total = nquad_conn_total*2
|
|
1493
|
+
|
|
1494
|
+
hex_conn_vertices = np.zeros((nquad_conn_total*4*3,3))
|
|
1495
|
+
hex_conn_vertices[0:nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2
|
|
1496
|
+
hex_conn_vertices[nquad_conn_total:2*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2
|
|
1497
|
+
hex_conn_vertices[2*nquad_conn_total:3*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2
|
|
1498
|
+
hex_conn_vertices[3*nquad_conn_total:4*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2
|
|
1499
|
+
|
|
1500
|
+
hex_conn_vertices[4*nquad_conn_total:5*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2 - Quad_normal*Quad_length1[:, np.newaxis]
|
|
1501
|
+
hex_conn_vertices[5*nquad_conn_total:6*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2 - Quad_normal*Quad_length1[:, np.newaxis]
|
|
1502
|
+
hex_conn_vertices[6*nquad_conn_total:7*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2 - Quad_normal*Quad_length1[:, np.newaxis]
|
|
1503
|
+
hex_conn_vertices[7*nquad_conn_total:8*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2 - Quad_normal*Quad_length1[:, np.newaxis]
|
|
1504
|
+
|
|
1505
|
+
hex_conn_vertices[8*nquad_conn_total:9*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2 + Quad_normal*Quad_length2[:, np.newaxis]
|
|
1506
|
+
hex_conn_vertices[9*nquad_conn_total:10*nquad_conn_total,:] = Quad_center - Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2 + Quad_normal*Quad_length2[:, np.newaxis]
|
|
1507
|
+
hex_conn_vertices[10*nquad_conn_total:11*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 + Quad_bitangent*Quad_width[:, np.newaxis]/2 + Quad_normal*Quad_length2[:, np.newaxis]
|
|
1508
|
+
hex_conn_vertices[11*nquad_conn_total:12*nquad_conn_total,:] = Quad_center + Quad_tangent*Quad_height[:, np.newaxis]/2 - Quad_bitangent*Quad_width[:, np.newaxis]/2 + Quad_normal*Quad_length2[:, np.newaxis]
|
|
1509
|
+
|
|
1510
|
+
hex_conn1_connectivity = np.linspace(0,nquad_conn_total*4*2-1,nquad_conn_total*4*2)
|
|
1511
|
+
hex_conn1_connectivity = np.reshape(hex_conn1_connectivity, [8,-1]).T
|
|
1512
|
+
hex_conn2_connectivity = np.linspace(0,nquad_conn_total*4*2-1,nquad_conn_total*4*2)
|
|
1513
|
+
hex_conn2_connectivity = np.reshape(hex_conn2_connectivity, [8,-1]).T
|
|
1514
|
+
hex_conn2_connectivity[:,4:8] = hex_conn2_connectivity[:,4:8] + nquad_conn_total*4
|
|
1515
|
+
hex_conn_connectivity = np.vstack((hex_conn1_connectivity,hex_conn2_connectivity))
|
|
1516
|
+
hex_conn_connectivity = hex_conn_connectivity + npt_total_vtk
|
|
1517
|
+
|
|
1518
|
+
# Modify Vertex Data by adding hex_conn_vertices
|
|
1519
|
+
woodVTKvertices_hex_conn = np.vstack((woodVTKvertices,hex_conn_vertices))
|
|
1520
|
+
npt_total_vtk_hex_conn = woodVTKvertices_hex_conn.shape[0]
|
|
1521
|
+
vertex_connectivity_vtk_hex_conn = np.linspace(0,npt_total_vtk_hex_conn-1,npt_total_vtk_hex_conn)
|
|
1522
|
+
|
|
1523
|
+
# =============================================================================
|
|
1524
|
+
# Paraview Visualization File
|
|
1525
|
+
woodVTKcell_types = np.concatenate((np.ones(npt_total_vtk),3*np.ones(nconnector_t),3*np.ones(nconnector_l),21*np.ones(nbeam_total_vtk),23*np.ones(nquad_total))).astype(int)
|
|
1526
|
+
ncell = woodVTKcell_types.shape[0]
|
|
1527
|
+
|
|
1528
|
+
collocation_flag_vtk = np.concatenate((np.ones(ngrain),np.zeros(npt_per_layer_vtk*NURBS_degree-ngrain)))
|
|
1529
|
+
collocation_flag_vtk = np.concatenate((np.tile(collocation_flag_vtk, nconnector_t_per_beam-1),np.concatenate((np.ones(ngrain),np.zeros(npt_per_layer_vtk-ngrain)))))
|
|
1530
|
+
collocation_flag_vtk = np.tile(collocation_flag_vtk, nbeam_per_grain)
|
|
1531
|
+
|
|
1532
|
+
vtkfile = open (Path('meshes/' + geoName + '/' + geoName + '_00001'+'.vtu'),'w')
|
|
1533
|
+
|
|
1534
|
+
vtkfile.write('<VTKFile type="UnstructuredGrid" version="2.0" byte_order="LittleEndian">'+'\n')
|
|
1535
|
+
vtkfile.write('<UnstructuredGrid>'+'\n')
|
|
1536
|
+
vtkfile.write('<Piece NumberOfPoints="'+str(npt_total_vtk)+'"'+' '+'NumberOfCells="'+str(ncell)+'">'+'\n')
|
|
1537
|
+
|
|
1538
|
+
# <Points>
|
|
1539
|
+
vtkfile.write('<Points>'+'\n')
|
|
1540
|
+
vtkfile.write('<DataArray type="Float64" NumberOfComponents="3" format="ascii">'+'\n')
|
|
1541
|
+
for i in range(0,npt_total_vtk):
|
|
1542
|
+
X,Y,Z = woodVTKvertices[i]
|
|
1543
|
+
vtkfile.write(' '+'%11.8e'%X+' '+'%11.8e'%Y+' '+'%11.8e'%Z+'\n')
|
|
1544
|
+
vtkfile.write('</DataArray>'+'\n')
|
|
1545
|
+
vtkfile.write('</Points>'+'\n')
|
|
1546
|
+
# </Points>
|
|
1547
|
+
|
|
1548
|
+
|
|
1549
|
+
# <PointData>
|
|
1550
|
+
vtkfile.write("<"+"PointData"\
|
|
1551
|
+
+" "+"Tensors="+'"'+""+'"'\
|
|
1552
|
+
+" "+"Vevtors="+'"'+""+'"'\
|
|
1553
|
+
+" "+"Scalars="+'"'+"IGAcollocation_flag"+'"'+">"+'\n')
|
|
1554
|
+
|
|
1555
|
+
# IGA collocation flag
|
|
1556
|
+
vtkfile.write("<"+"DataArray"+" "+"type="+'"'+"Float32"+'"'+" "+"Name="+'"IGAcollocation_flag" format='+'"'+"ascii"+'"'+">"+'\n')
|
|
1557
|
+
for i in range(0,npt_total_vtk):
|
|
1558
|
+
X = collocation_flag_vtk[i]
|
|
1559
|
+
vtkfile.write('%11.8e'%X+'\n')
|
|
1560
|
+
vtkfile.write("</DataArray>"+'\n')
|
|
1561
|
+
vtkfile.write('</PointData>'+'\n')
|
|
1562
|
+
# </PointData>
|
|
1563
|
+
|
|
1564
|
+
|
|
1565
|
+
# <Cells>
|
|
1566
|
+
vtkfile.write('<Cells>'+'\n')
|
|
1567
|
+
|
|
1568
|
+
# Cell connectivity
|
|
1569
|
+
vtkfile.write('<DataArray type="Int32" Name="connectivity" format="ascii">'+'\n')
|
|
1570
|
+
for x in vertex_connectivity_vtk.astype(int):
|
|
1571
|
+
vtkfile.write("%s\n" % x)
|
|
1572
|
+
#vtkfile.write('\n')
|
|
1573
|
+
vtkfile.write("\n".join(" ".join(map(str, x)) for x in connector_t_connectivity_vtk.astype(int)))
|
|
1574
|
+
vtkfile.write('\n')
|
|
1575
|
+
vtkfile.write("\n".join(" ".join(map(str, x)) for x in connector_l_connectivity_vtk.astype(int)))
|
|
1576
|
+
vtkfile.write('\n')
|
|
1577
|
+
vtkfile.write("\n".join(" ".join(map(str, x)) for x in beam_connectivity_vtk.astype(int)))
|
|
1578
|
+
vtkfile.write('\n')
|
|
1579
|
+
vtkfile.write("\n".join(" ".join(map(str, x)) for x in quad_connectivity.astype(int)))
|
|
1580
|
+
vtkfile.write('\n')
|
|
1581
|
+
vtkfile.write('</DataArray>'+'\n')
|
|
1582
|
+
|
|
1583
|
+
# Cell offsets
|
|
1584
|
+
vtkfile.write('<DataArray type="Int32" Name="offsets" format="ascii">'+'\n')
|
|
1585
|
+
current_offset = 0
|
|
1586
|
+
for element in vertex_connectivity_vtk:
|
|
1587
|
+
element_offset = 1
|
|
1588
|
+
current_offset += element_offset
|
|
1589
|
+
vtkfile.write(str(current_offset)+'\n')
|
|
1590
|
+
for element in connector_t_connectivity_vtk:
|
|
1591
|
+
element_offset = len(element)
|
|
1592
|
+
current_offset += element_offset
|
|
1593
|
+
vtkfile.write(str(current_offset)+'\n')
|
|
1594
|
+
for element in connector_l_connectivity_vtk:
|
|
1595
|
+
element_offset = len(element)
|
|
1596
|
+
current_offset += element_offset
|
|
1597
|
+
vtkfile.write(str(current_offset)+'\n')
|
|
1598
|
+
for element in beam_connectivity_vtk:
|
|
1599
|
+
element_offset = len(element)
|
|
1600
|
+
current_offset += element_offset
|
|
1601
|
+
vtkfile.write(str(current_offset)+'\n')
|
|
1602
|
+
for element in quad_connectivity:
|
|
1603
|
+
element_offset = len(element)
|
|
1604
|
+
current_offset += element_offset
|
|
1605
|
+
vtkfile.write(str(current_offset)+'\n')
|
|
1606
|
+
vtkfile.write('</DataArray>'+'\n')
|
|
1607
|
+
|
|
1608
|
+
# Cell type
|
|
1609
|
+
vtkfile.write('<DataArray type="UInt8" Name="types" format="ascii">'+'\n')
|
|
1610
|
+
for i in range(0,ncell):
|
|
1611
|
+
element = woodVTKcell_types[i]
|
|
1612
|
+
vtkfile.write(str(element)+'\n')
|
|
1613
|
+
vtkfile.write('</DataArray>'+'\n')
|
|
1614
|
+
|
|
1615
|
+
vtkfile.write('</Cells>'+'\n')
|
|
1616
|
+
# </Cells>
|
|
1617
|
+
|
|
1618
|
+
vtkfile.write('</Piece>'+'\n')
|
|
1619
|
+
#</Piece>
|
|
1620
|
+
|
|
1621
|
+
vtkfile.write('</UnstructuredGrid>'+'\n')
|
|
1622
|
+
#</UnstructuredGrid>
|
|
1623
|
+
|
|
1624
|
+
vtkfile.write('</VTKFile>'+'\n')
|
|
1625
|
+
#</VTKFile>
|
|
1626
|
+
|
|
1627
|
+
vtkfile.close()
|
|
1628
|
+
|
|
1629
|
+
# =============================================================================
|
|
1630
|
+
# Paraview Vertices File
|
|
1631
|
+
woodVTKcell_types_vertices = (np.ones(npt_total_vtk)).astype(int)
|
|
1632
|
+
|
|
1633
|
+
ncell_vertices = woodVTKcell_types_vertices.shape[0]
|
|
1634
|
+
|
|
1635
|
+
vtkfile_vertices = open (Path('meshes/' + geoName + '/' + geoName + '_vertices'+'.vtu'),'w')
|
|
1636
|
+
|
|
1637
|
+
vtkfile_vertices.write('<VTKFile type="UnstructuredGrid" version="2.0" byte_order="LittleEndian">'+'\n')
|
|
1638
|
+
vtkfile_vertices.write('<UnstructuredGrid>'+'\n')
|
|
1639
|
+
vtkfile_vertices.write('<Piece NumberOfPoints="'+str(npt_total_vtk)+'"'+' '+'NumberOfCells="'+str(ncell_vertices)+'">'+'\n')
|
|
1640
|
+
|
|
1641
|
+
# <Points>
|
|
1642
|
+
vtkfile_vertices.write('<Points>'+'\n')
|
|
1643
|
+
vtkfile_vertices.write('<DataArray type="Float64" NumberOfComponents="3" format="ascii">'+'\n')
|
|
1644
|
+
for i in range(0,npt_total_vtk):
|
|
1645
|
+
X,Y,Z = woodVTKvertices[i]
|
|
1646
|
+
vtkfile_vertices.write(' '+'%11.8e'%X+' '+'%11.8e'%Y+' '+'%11.8e'%Z+'\n')
|
|
1647
|
+
vtkfile_vertices.write('</DataArray>'+'\n')
|
|
1648
|
+
vtkfile_vertices.write('</Points>'+'\n')
|
|
1649
|
+
# </Points>
|
|
1650
|
+
|
|
1651
|
+
# <PointData>
|
|
1652
|
+
vtkfile_vertices.write("<"+"PointData"\
|
|
1653
|
+
+" "+"Tensors="+'"'+""+'"'\
|
|
1654
|
+
+" "+"Vevtors="+'"'+""+'"'\
|
|
1655
|
+
+" "+"Scalars="+'"'+"IGAcollocation_flag"+'"'+">"+'\n')
|
|
1656
|
+
|
|
1657
|
+
# Point Data
|
|
1658
|
+
vtkfile_vertices.write("<"+"DataArray"+" "+"type="+'"'+"Float32"+'"'+" "+"Name="+'"IGAcollocation_flag" format='+'"'+"ascii"+'"'+">"+'\n')
|
|
1659
|
+
for i in range(0,npt_total_vtk):
|
|
1660
|
+
X = collocation_flag_vtk[i]
|
|
1661
|
+
vtkfile_vertices.write('%11.8e'%X+'\n')
|
|
1662
|
+
vtkfile_vertices.write("</DataArray>"+'\n')
|
|
1663
|
+
|
|
1664
|
+
vtkfile_vertices.write('</PointData>'+'\n')
|
|
1665
|
+
# </PointData>
|
|
1666
|
+
|
|
1667
|
+
|
|
1668
|
+
# <Cells>
|
|
1669
|
+
vtkfile_vertices.write('<Cells>'+'\n')
|
|
1670
|
+
|
|
1671
|
+
# Cell connectivity
|
|
1672
|
+
vtkfile_vertices.write('<DataArray type="Int32" Name="connectivity" format="ascii">'+'\n')
|
|
1673
|
+
for x in vertex_connectivity_vtk.astype(int):
|
|
1674
|
+
vtkfile_vertices.write("%s\n" % x)
|
|
1675
|
+
vtkfile_vertices.write('\n')
|
|
1676
|
+
vtkfile_vertices.write('</DataArray>'+'\n')
|
|
1677
|
+
|
|
1678
|
+
# Cell offsets
|
|
1679
|
+
vtkfile_vertices.write('<DataArray type="Int32" Name="offsets" format="ascii">'+'\n')
|
|
1680
|
+
current_offset = 0
|
|
1681
|
+
for element in vertex_connectivity_vtk:
|
|
1682
|
+
element_offset = 1
|
|
1683
|
+
current_offset += element_offset
|
|
1684
|
+
vtkfile_vertices.write(str(current_offset)+'\n')
|
|
1685
|
+
vtkfile_vertices.write('</DataArray>'+'\n')
|
|
1686
|
+
|
|
1687
|
+
# Cell type
|
|
1688
|
+
vtkfile_vertices.write('<DataArray type="UInt8" Name="types" format="ascii">'+'\n')
|
|
1689
|
+
for i in range(0,ncell_vertices):
|
|
1690
|
+
element = woodVTKcell_types_vertices[i]
|
|
1691
|
+
vtkfile_vertices.write(str(element)+'\n')
|
|
1692
|
+
|
|
1693
|
+
vtkfile_vertices.write('</DataArray>'+'\n')
|
|
1694
|
+
|
|
1695
|
+
vtkfile_vertices.write('</Cells>'+'\n')
|
|
1696
|
+
# </Cells>
|
|
1697
|
+
|
|
1698
|
+
vtkfile_vertices.write('</Piece>'+'\n')
|
|
1699
|
+
#</Piece>
|
|
1700
|
+
|
|
1701
|
+
vtkfile_vertices.write('</UnstructuredGrid>'+'\n')
|
|
1702
|
+
#</UnstructuredGrid>
|
|
1703
|
+
|
|
1704
|
+
vtkfile_vertices.write('</VTKFile>'+'\n')
|
|
1705
|
+
#</VTKFile>
|
|
1706
|
+
|
|
1707
|
+
vtkfile_vertices.close()
|
|
1708
|
+
|
|
1709
|
+
# =============================================================================
|
|
1710
|
+
# Paraview Beam File
|
|
1711
|
+
woodVTKcell_types_beams = np.concatenate((21*np.ones(nbeam_total_vtk),23*np.ones(nquad_total))).astype(int)
|
|
1712
|
+
|
|
1713
|
+
ncell_beams = woodVTKcell_types_beams.shape[0]
|
|
1714
|
+
|
|
1715
|
+
vtkfile_beams = open (Path('meshes/' + geoName + '/' + geoName + '_beams'+'.vtu'),'w')
|
|
1716
|
+
|
|
1717
|
+
vtkfile_beams.write('<VTKFile type="UnstructuredGrid" version="2.0" byte_order="LittleEndian">'+'\n')
|
|
1718
|
+
vtkfile_beams.write('<UnstructuredGrid>'+'\n')
|
|
1719
|
+
vtkfile_beams.write('<Piece NumberOfPoints="'+str(npt_total_vtk)+'"'+' '+'NumberOfCells="'+str(ncell_beams)+'">'+'\n')
|
|
1720
|
+
|
|
1721
|
+
# <Points>
|
|
1722
|
+
vtkfile_beams.write('<Points>'+'\n')
|
|
1723
|
+
vtkfile_beams.write('<DataArray type="Float64" NumberOfComponents="3" format="ascii">'+'\n')
|
|
1724
|
+
for i in range(0,npt_total_vtk):
|
|
1725
|
+
X,Y,Z = woodVTKvertices[i]
|
|
1726
|
+
vtkfile_beams.write(' '+'%11.8e'%X+' '+'%11.8e'%Y+' '+'%11.8e'%Z+'\n')
|
|
1727
|
+
vtkfile_beams.write('</DataArray>'+'\n')
|
|
1728
|
+
vtkfile_beams.write('</Points>'+'\n')
|
|
1729
|
+
# </Points>
|
|
1730
|
+
|
|
1731
|
+
# <PointData>
|
|
1732
|
+
# </PointData>
|
|
1733
|
+
|
|
1734
|
+
# <Cells>
|
|
1735
|
+
vtkfile_beams.write('<Cells>'+'\n')
|
|
1736
|
+
|
|
1737
|
+
# Cell connectivity
|
|
1738
|
+
vtkfile_beams.write('<DataArray type="Int32" Name="connectivity" format="ascii">'+'\n')
|
|
1739
|
+
vtkfile_beams.write("\n".join(" ".join(map(str, x)) for x in beam_connectivity_vtk.astype(int)))
|
|
1740
|
+
vtkfile_beams.write('\n')
|
|
1741
|
+
vtkfile_beams.write("\n".join(" ".join(map(str, x)) for x in quad_connectivity.astype(int)))
|
|
1742
|
+
vtkfile_beams.write('\n')
|
|
1743
|
+
vtkfile_beams.write('</DataArray>'+'\n')
|
|
1744
|
+
|
|
1745
|
+
# Cell offsets
|
|
1746
|
+
vtkfile_beams.write('<DataArray type="Int32" Name="offsets" format="ascii">'+'\n')
|
|
1747
|
+
current_offset = 0
|
|
1748
|
+
for element in beam_connectivity_vtk:
|
|
1749
|
+
element_offset = len(element)
|
|
1750
|
+
current_offset += element_offset
|
|
1751
|
+
vtkfile_beams.write(str(current_offset)+'\n')
|
|
1752
|
+
for element in quad_connectivity:
|
|
1753
|
+
element_offset = len(element)
|
|
1754
|
+
current_offset += element_offset
|
|
1755
|
+
vtkfile_beams.write(str(current_offset)+'\n')
|
|
1756
|
+
vtkfile_beams.write('</DataArray>'+'\n')
|
|
1757
|
+
|
|
1758
|
+
# Cell type
|
|
1759
|
+
vtkfile_beams.write('<DataArray type="UInt8" Name="types" format="ascii">'+'\n')
|
|
1760
|
+
for i in range(0,ncell_beams):
|
|
1761
|
+
element = woodVTKcell_types_beams[i]
|
|
1762
|
+
vtkfile_beams.write(str(element)+'\n')
|
|
1763
|
+
|
|
1764
|
+
vtkfile_beams.write('</DataArray>'+'\n')
|
|
1765
|
+
|
|
1766
|
+
vtkfile_beams.write('</Cells>'+'\n')
|
|
1767
|
+
# </Cells>
|
|
1768
|
+
|
|
1769
|
+
vtkfile_beams.write('</Piece>'+'\n')
|
|
1770
|
+
#</Piece>
|
|
1771
|
+
|
|
1772
|
+
vtkfile_beams.write('</UnstructuredGrid>'+'\n')
|
|
1773
|
+
#</UnstructuredGrid>
|
|
1774
|
+
|
|
1775
|
+
vtkfile_beams.write('</VTKFile>'+'\n')
|
|
1776
|
+
#</VTKFile>
|
|
1777
|
+
|
|
1778
|
+
vtkfile_beams.close()
|
|
1779
|
+
|
|
1780
|
+
# =============================================================================
|
|
1781
|
+
# Paraview Connector (Axis + Center section) File
|
|
1782
|
+
woodVTKcell_types_conns = np.concatenate((3*np.ones(nconnector_t),3*np.ones(nconnector_l),9*np.ones(nconnector_t),9*np.ones(nconnector_l))).astype(int)
|
|
1783
|
+
ncell_conns = woodVTKcell_types_conns.shape[0]
|
|
1784
|
+
|
|
1785
|
+
Quad_width_vtk = np.tile(Quad_width,(2))
|
|
1786
|
+
|
|
1787
|
+
vtkfile_conns = open (Path('meshes/' + geoName + '/' + geoName + '_conns'+'.vtu'),'w')
|
|
1788
|
+
|
|
1789
|
+
vtkfile_conns.write('<VTKFile type="UnstructuredGrid" version="2.0" byte_order="LittleEndian">'+'\n')
|
|
1790
|
+
vtkfile_conns.write('<UnstructuredGrid>'+'\n')
|
|
1791
|
+
vtkfile_conns.write('<Piece NumberOfPoints="'+str(npt_total_vtk_quad_conn)+'"'+' '+'NumberOfCells="'+str(ncell_conns)+'">'+'\n')
|
|
1792
|
+
|
|
1793
|
+
# <Points>
|
|
1794
|
+
vtkfile_conns.write('<Points>'+'\n')
|
|
1795
|
+
vtkfile_conns.write('<DataArray type="Float64" NumberOfComponents="3" format="ascii">'+'\n')
|
|
1796
|
+
for i in range(0,npt_total_vtk_quad_conn):
|
|
1797
|
+
X,Y,Z = woodVTKvertices_quad_conn[i]
|
|
1798
|
+
vtkfile_conns.write(' '+'%11.8e'%X+' '+'%11.8e'%Y+' '+'%11.8e'%Z+'\n')
|
|
1799
|
+
vtkfile_conns.write('</DataArray>'+'\n')
|
|
1800
|
+
vtkfile_conns.write('</Points>'+'\n')
|
|
1801
|
+
# </Points>
|
|
1802
|
+
|
|
1803
|
+
# <PointData>
|
|
1804
|
+
# </PointData>
|
|
1805
|
+
|
|
1806
|
+
# <Cells>
|
|
1807
|
+
vtkfile_conns.write('<Cells>'+'\n')
|
|
1808
|
+
|
|
1809
|
+
# Cell connectivity
|
|
1810
|
+
vtkfile_conns.write('<DataArray type="Int32" Name="connectivity" format="ascii">'+'\n')
|
|
1811
|
+
vtkfile_conns.write("\n".join(" ".join(map(str, x)) for x in connector_t_connectivity_vtk.astype(int)))
|
|
1812
|
+
vtkfile_conns.write('\n')
|
|
1813
|
+
vtkfile_conns.write("\n".join(" ".join(map(str, x)) for x in connector_l_connectivity_vtk.astype(int)))
|
|
1814
|
+
vtkfile_conns.write('\n')
|
|
1815
|
+
vtkfile_conns.write("\n".join(" ".join(map(str, x)) for x in quad_conn_connectivity.astype(int)))
|
|
1816
|
+
vtkfile_conns.write('\n')
|
|
1817
|
+
vtkfile_conns.write('</DataArray>'+'\n')
|
|
1818
|
+
|
|
1819
|
+
# Cell offsets
|
|
1820
|
+
vtkfile_conns.write('<DataArray type="Int32" Name="offsets" format="ascii">'+'\n')
|
|
1821
|
+
current_offset = 0
|
|
1822
|
+
for element in connector_t_connectivity_vtk:
|
|
1823
|
+
element_offset = len(element)
|
|
1824
|
+
current_offset += element_offset
|
|
1825
|
+
vtkfile_conns.write(str(current_offset)+'\n')
|
|
1826
|
+
for element in connector_l_connectivity_vtk:
|
|
1827
|
+
element_offset = len(element)
|
|
1828
|
+
current_offset += element_offset
|
|
1829
|
+
vtkfile_conns.write(str(current_offset)+'\n')
|
|
1830
|
+
for element in quad_conn_connectivity:
|
|
1831
|
+
element_offset = len(element)
|
|
1832
|
+
current_offset += element_offset
|
|
1833
|
+
vtkfile_conns.write(str(current_offset)+'\n')
|
|
1834
|
+
vtkfile_conns.write('</DataArray>'+'\n')
|
|
1835
|
+
|
|
1836
|
+
# Cell type
|
|
1837
|
+
vtkfile_conns.write('<DataArray type="UInt8" Name="types" format="ascii">'+'\n')
|
|
1838
|
+
for i in range(0,ncell_conns):
|
|
1839
|
+
element = woodVTKcell_types_conns[i]
|
|
1840
|
+
vtkfile_conns.write(str(element)+'\n')
|
|
1841
|
+
|
|
1842
|
+
vtkfile_conns.write('</DataArray>'+'\n')
|
|
1843
|
+
|
|
1844
|
+
vtkfile_conns.write('</Cells>'+'\n')
|
|
1845
|
+
# </Cells>
|
|
1846
|
+
|
|
1847
|
+
# <CellData>
|
|
1848
|
+
vtkfile_conns.write("<"+"CellData"\
|
|
1849
|
+
+" "+"Tensors="+'"'+""+'"'\
|
|
1850
|
+
+" "+"Vevtors="+'"'+""+'"'\
|
|
1851
|
+
+" "+"Scalars="+'"'+"Connector_width"+'"'+">"+'\n')
|
|
1852
|
+
|
|
1853
|
+
vtkfile_conns.write("<"+"DataArray"+" "+"type="+'"'+"Float32"+'"'+" "+"Name="+'"Connector_width"'+" "+"format="+'"'+"ascii"+'"'+">"+'\n')
|
|
1854
|
+
for i in range(0,ncell_conns):
|
|
1855
|
+
X = Quad_width_vtk[i]
|
|
1856
|
+
vtkfile_conns.write('%11.8e'%X+'\n')
|
|
1857
|
+
vtkfile_conns.write('</DataArray>'+'\n')
|
|
1858
|
+
|
|
1859
|
+
vtkfile_conns.write('</CellData>'+'\n')
|
|
1860
|
+
|
|
1861
|
+
vtkfile_conns.write('</Piece>'+'\n')
|
|
1862
|
+
#</Piece>
|
|
1863
|
+
|
|
1864
|
+
vtkfile_conns.write('</UnstructuredGrid>'+'\n')
|
|
1865
|
+
#</UnstructuredGrid>
|
|
1866
|
+
|
|
1867
|
+
vtkfile_conns.write('</VTKFile>'+'\n')
|
|
1868
|
+
#</VTKFile>
|
|
1869
|
+
|
|
1870
|
+
vtkfile_conns.close()
|
|
1871
|
+
|
|
1872
|
+
# =============================================================================
|
|
1873
|
+
# Paraview Connector (Volume) File
|
|
1874
|
+
woodVTKcell_types_conns_vol = np.concatenate((12*np.ones(nconnector_t),12*np.ones(nconnector_l),12*np.ones(nconnector_t),12*np.ones(nconnector_l))).astype(int)
|
|
1875
|
+
ncell_conns_vol = woodVTKcell_types_conns_vol.shape[0]
|
|
1876
|
+
|
|
1877
|
+
Quad_width_vtk = np.tile(Quad_width,(2))
|
|
1878
|
+
|
|
1879
|
+
vtkfile_conns_vol = open (Path('meshes/' + geoName + '/' + geoName + '_conns_vol'+'.vtu'),'w')
|
|
1880
|
+
|
|
1881
|
+
vtkfile_conns_vol.write('<VTKFile type="UnstructuredGrid" version="2.0" byte_order="LittleEndian">'+'\n')
|
|
1882
|
+
vtkfile_conns_vol.write('<UnstructuredGrid>'+'\n')
|
|
1883
|
+
vtkfile_conns_vol.write('<Piece NumberOfPoints="'+str(npt_total_vtk_hex_conn)+'"'+' '+'NumberOfCells="'+str(ncell_conns_vol)+'">'+'\n')
|
|
1884
|
+
|
|
1885
|
+
# <Points>
|
|
1886
|
+
vtkfile_conns_vol.write('<Points>'+'\n')
|
|
1887
|
+
vtkfile_conns_vol.write('<DataArray type="Float64" NumberOfComponents="3" format="ascii">'+'\n')
|
|
1888
|
+
for i in range(0,npt_total_vtk_hex_conn):
|
|
1889
|
+
X,Y,Z = woodVTKvertices_hex_conn[i]
|
|
1890
|
+
vtkfile_conns_vol.write(' '+'%11.8e'%X+' '+'%11.8e'%Y+' '+'%11.8e'%Z+'\n')
|
|
1891
|
+
vtkfile_conns_vol.write('</DataArray>'+'\n')
|
|
1892
|
+
vtkfile_conns_vol.write('</Points>'+'\n')
|
|
1893
|
+
# </Points>
|
|
1894
|
+
|
|
1895
|
+
# <PointData>
|
|
1896
|
+
# </PointData>
|
|
1897
|
+
|
|
1898
|
+
# <Cells>
|
|
1899
|
+
vtkfile_conns_vol.write('<Cells>'+'\n')
|
|
1900
|
+
|
|
1901
|
+
# Cell connectivity
|
|
1902
|
+
vtkfile_conns_vol.write('<DataArray type="Int32" Name="connectivity" format="ascii">'+'\n')
|
|
1903
|
+
vtkfile_conns_vol.write("\n".join(" ".join(map(str, x)) for x in hex_conn_connectivity.astype(int)))
|
|
1904
|
+
vtkfile_conns_vol.write('\n')
|
|
1905
|
+
vtkfile_conns_vol.write('</DataArray>'+'\n')
|
|
1906
|
+
|
|
1907
|
+
# Cell offsets
|
|
1908
|
+
vtkfile_conns_vol.write('<DataArray type="Int32" Name="offsets" format="ascii">'+'\n')
|
|
1909
|
+
current_offset = 0
|
|
1910
|
+
for element in hex_conn_connectivity:
|
|
1911
|
+
element_offset = len(element)
|
|
1912
|
+
current_offset += element_offset
|
|
1913
|
+
vtkfile_conns_vol.write(str(current_offset)+'\n')
|
|
1914
|
+
vtkfile_conns_vol.write('</DataArray>'+'\n')
|
|
1915
|
+
|
|
1916
|
+
# Cell type
|
|
1917
|
+
vtkfile_conns_vol.write('<DataArray type="UInt8" Name="types" format="ascii">'+'\n')
|
|
1918
|
+
for i in range(0,ncell_conns_vol):
|
|
1919
|
+
element = woodVTKcell_types_conns_vol[i]
|
|
1920
|
+
vtkfile_conns_vol.write(str(element)+'\n')
|
|
1921
|
+
|
|
1922
|
+
vtkfile_conns_vol.write('</DataArray>'+'\n')
|
|
1923
|
+
|
|
1924
|
+
vtkfile_conns_vol.write('</Cells>'+'\n')
|
|
1925
|
+
# </Cells>
|
|
1926
|
+
|
|
1927
|
+
# <CellData>
|
|
1928
|
+
vtkfile_conns_vol.write("<"+"CellData"\
|
|
1929
|
+
+" "+"Tensors="+'"'+""+'"'\
|
|
1930
|
+
+" "+"Vectors="+'"'+""+'"'\
|
|
1931
|
+
+" "+"Scalars="+'"'+"Connector_width"+'"'+">"+'\n')
|
|
1932
|
+
|
|
1933
|
+
vtkfile_conns_vol.write("<"+"DataArray"+" "+"type="+'"'+"Float32"+'"'+" "+"Name="+'"Connector_width"'+" "+"format="+'"'+"ascii"+'"'+">"+'\n')
|
|
1934
|
+
for i in range(0,ncell_conns_vol):
|
|
1935
|
+
X = Quad_width_vtk[i]
|
|
1936
|
+
vtkfile_conns_vol.write('%11.8e'%X+'\n')
|
|
1937
|
+
vtkfile_conns_vol.write('</DataArray>'+'\n')
|
|
1938
|
+
|
|
1939
|
+
vtkfile_conns_vol.write('</CellData>'+'\n')
|
|
1940
|
+
|
|
1941
|
+
vtkfile_conns_vol.write('</Piece>'+'\n')
|
|
1942
|
+
#</Piece>
|
|
1943
|
+
|
|
1944
|
+
vtkfile_conns_vol.write('</UnstructuredGrid>'+'\n')
|
|
1945
|
+
#</UnstructuredGrid>
|
|
1946
|
+
|
|
1947
|
+
vtkfile_conns_vol.write('</VTKFile>'+'\n')
|
|
1948
|
+
#</VTKFile>
|
|
1949
|
+
|
|
1950
|
+
vtkfile_conns_vol.close()
|
|
1951
|
+
|
|
1952
|
+
|
|
1953
|
+
def BezierExtraction(NURBS_degree,nctrlpt_per_beam,nbeam_total):
|
|
1954
|
+
knotVec_per_beam = np.concatenate((np.zeros(NURBS_degree),(np.linspace(0,1,int((nctrlpt_per_beam-1)/NURBS_degree+1))),np.ones(NURBS_degree)))
|
|
1955
|
+
knotVec = np.tile(knotVec_per_beam, (nbeam_total,1))
|
|
1956
|
+
return knotVec
|
|
1957
|
+
|
|
1958
|
+
|
|
1959
|
+
def BezierBeamFile(geoName,NURBS_degree,nctrlpt_per_beam,\
|
|
1960
|
+
nconnector_t_per_beam,npatch,knotVec):
|
|
1961
|
+
############################# Abaqus txt File #################################
|
|
1962
|
+
txtfile = open (Path('meshes/' + geoName + '/' + geoName + 'IGA.txt'),'w')
|
|
1963
|
+
txtfile.write('# Dimension of beam elements \n')
|
|
1964
|
+
txtfile.write('1 \n')
|
|
1965
|
+
txtfile.write('# Order of basis function \n')
|
|
1966
|
+
txtfile.write('{:d} \n'.format(NURBS_degree))
|
|
1967
|
+
txtfile.write('# Number of control points per patch \n')
|
|
1968
|
+
txtfile.write('{:d} \n'.format(nctrlpt_per_beam))
|
|
1969
|
+
txtfile.write('# Number of elements per patch \n')
|
|
1970
|
+
txtfile.write('{:d} \n'.format(nconnector_t_per_beam-1))
|
|
1971
|
+
txtfile.write('# Number of Patches \n')
|
|
1972
|
+
txtfile.write('{:d} \n'.format(npatch))
|
|
1973
|
+
# Loop over patches
|
|
1974
|
+
for ipatch in range(0,npatch):
|
|
1975
|
+
txtfile.write('{:s} \n'.format('PATCH-'+str(ipatch+1)))
|
|
1976
|
+
txtfile.write('Size of knot vectors \n')
|
|
1977
|
+
txtfile.write('{:d} \n'.format(knotVec.shape[1]))
|
|
1978
|
+
txtfile.write('knot vectors \n')
|
|
1979
|
+
for j in range(0,knotVec.shape[1]):
|
|
1980
|
+
txtfile.write('{:f} '.format(knotVec[ipatch,j]))
|
|
1981
|
+
txtfile.write('\n')
|
|
1982
|
+
txtfile.close()
|
|
1983
|
+
|
|
1984
|
+
|
|
1985
|
+
def ModelInfo(nbeam_per_grain,fiberlength,NURBS_degree,nctrlpt_per_beam,\
|
|
1986
|
+
props_beam,props_connector_t_bot,props_connector_t_reg,\
|
|
1987
|
+
props_connector_t_top,props_connector_l,iprops_beam,\
|
|
1988
|
+
beam_connectivity,connector_t_bot_connectivity,\
|
|
1989
|
+
connector_t_reg_connectivity,connector_t_top_connectivity,\
|
|
1990
|
+
connector_l_connectivity,MeshData):
|
|
1991
|
+
|
|
1992
|
+
beam_length = fiberlength/nbeam_per_grain
|
|
1993
|
+
nelem_beam = beam_connectivity.shape[0]
|
|
1994
|
+
nelem_connector_t_bot = connector_t_bot_connectivity.shape[0]
|
|
1995
|
+
nelem_connector_t_reg = connector_t_reg_connectivity.shape[0]
|
|
1996
|
+
nelem_connector_t_top = connector_t_top_connectivity.shape[0]
|
|
1997
|
+
nelem_connector_l = connector_l_connectivity.shape[0]
|
|
1998
|
+
|
|
1999
|
+
density_beam = props_beam[0]
|
|
2000
|
+
density_connector_t_bot = props_connector_t_bot[0]
|
|
2001
|
+
density_connector_t_top = props_connector_t_top[0]
|
|
2002
|
+
density_connector_t_reg = props_connector_t_reg[0]
|
|
2003
|
+
density_connector_l = props_connector_l[0]
|
|
2004
|
+
mass = 0
|
|
2005
|
+
|
|
2006
|
+
# mass of beams
|
|
2007
|
+
# for i in range(0,nelem_beam):
|
|
2008
|
+
mass += nelem_beam*density_beam*props_beam[3]*props_beam[4]*beam_length
|
|
2009
|
+
# mass of bot transverse connector
|
|
2010
|
+
for i in range(0,nelem_connector_t_bot):
|
|
2011
|
+
mass += density_connector_t_bot*props_connector_t_bot[3]*MeshData[i,11]*np.linalg.norm(MeshData[i,5:8] - MeshData[i,8:11])
|
|
2012
|
+
# mass of reg transverse connector
|
|
2013
|
+
for i in range(nelem_connector_t_bot,nelem_connector_t_bot+nelem_connector_t_reg):
|
|
2014
|
+
mass += density_connector_t_reg*props_connector_t_reg[3]*MeshData[i,11]*np.linalg.norm(MeshData[i,5:8] - MeshData[i,8:11])
|
|
2015
|
+
# mass of top transverse connector
|
|
2016
|
+
for i in range(nelem_connector_t_bot+nelem_connector_t_reg,nelem_connector_t_bot+nelem_connector_t_reg+nelem_connector_t_top):
|
|
2017
|
+
mass += density_connector_t_top*props_connector_t_top[3]*MeshData[i,11]*np.linalg.norm(MeshData[i,5:8] - MeshData[i,8:11])
|
|
2018
|
+
# mass of longitudinal connector
|
|
2019
|
+
for i in range(nelem_connector_t_bot+nelem_connector_t_reg+nelem_connector_t_top,MeshData.shape[0]):
|
|
2020
|
+
mass += density_connector_l*props_connector_l[3]*np.linalg.norm(MeshData[i,5:8] - MeshData[i,8:11])
|
|
2021
|
+
return mass
|