PeriDEM 0.2.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
geom_util.py
Go to the documentation of this file.
1import os
2import sys
3import numpy as np
4import gmsh
5import math
6
7def rotate(p, theta, axis):
8 """
9 Returns rotation of vector about specified axis by specified angle
10
11 Parameters
12 ----------
13 p: list
14 Coordinates of vector
15 theta : float
16 Angle of rotation
17 axis : list
18 Axis of rotation
19
20 Returns
21 -------
22 list
23 Coordinates of rotated vector
24 """
25 p_np, axis_np = np.array(p), np.array(axis)
26 ct, st = np.cos(theta), np.sin(theta)
27 p_dot_n = np.dot(p_np,axis_np)
28 n_cross_p = np.cross(axis_np, p_np)
29
30 return (1. - ct) * p_dot_n * axis_np + ct * p_np + st * n_cross_p
31
32def get_ref_rect_points(center, Lx, Ly, add_center = False):
33 """
34 Returns size points on reference rectangle
35
36 Reference rectangle:
37
38 +-------------------+
39 | |
40 | |
41 | |
42 +-------------------+
43
44 Parameters
45 ----------
46 center: list
47 Coordinates of center of rectangle
48 Lx : float
49 Length of rectangle in x-direction
50 Ly : float
51 Length of rectangle in y-direction
52 add_center : bool
53 True if we include the center to the returned list (first point in the returned list will be the center if the flag is true)
54
55 Returns
56 -------
57 list
58 Coordinates of points
59 """
60
61 p1 = [center[0] - Lx/2., center[1] - Ly/2., center[2]]
62 p2 = [center[0] + Lx/2., center[1] - Ly/2., center[2]]
63 p3 = [center[0] + Lx/2., center[1] + Ly/2., center[2]]
64 p4 = [center[0] - Lx/2., center[1] + Ly/2., center[2]]
65
66 return [p1, p2, p3, p4] if not add_center else [center, p1, p2, p3, p4]
67
68def get_ref_triangle_points(center, radius, add_center = False):
69 """
70 Returns size points on reference triangle
71
72 Reference triangle:
73
74 + v2
75 | \
76 | \
77 | \
78 | o + v1
79 | /
80 | /
81 | /
82 + v3
83
84 Radius: o-v1
85 Axis vector: o-v1
86 Rotation axis: [0, 0, 1]
87
88 Parameters
89 ----------
90 center: list
91 Coordinates of center of triangle
92 radius : float
93 Radius of triangle
94 add_center : bool
95 True if we include the center to the returned list (first point in the returned list will be the center if the flag is true)
96
97 Returns
98 -------
99 list
100 Coordinates of points
101 """
102 axis = [1., 0., 0.]
103 rotate_axis = [0., 0., 1.]
104
105 points = []
106 if add_center:
107 points.append(center)
108
109 for i in range(3):
110 xi = rotate(axis, i*2*np.pi/3., rotate_axis)
111 points.append([center[i] + radius * xi[i] for i in range(3)])
112
113 return points
114
115def get_ref_hex_points(center, radius, add_center = False):
116 """
117 Returns size points on reference hexagon
118
119 Reference hexagon:
120
121 v3 v2
122 + +
123
124
125 + o +
126 v4 x v1
127
128 + +
129 v5 v6
130
131 Radius: x-v1
132 Axis vector: x-v1
133 Rotation axis: [0, 0, 1]
134
135 Parameters
136 ----------
137 center: list
138 Coordinates of center of hexagon
139 radius : float
140 Radius of hexagon
141 add_center : bool
142 True if we include the center to the returned list (first point in the returned list will be the center if the flag is true)
143
144 Returns
145 -------
146 list
147 Coordinates of points
148 """
149 axis = [1., 0., 0.]
150 rotate_axis = [0., 0., 1.]
151 points = []
152 if add_center:
153 points.append(center)
154
155 for i in range(6):
156 xi = rotate(axis, i*np.pi/3., rotate_axis)
157 points.append([center[i] + radius * xi[i] for i in range(3)])
158
159 return points
160
161def get_ref_drum_points(center, radius, width, add_center = False):
162 """
163 Returns size points on reference concave polygon (we refer to it as drum)
164
165 Reference concave polygon:
166
167 v3 v2
168 + -------------------------------- +
169 \ /
170 \ /
171 + o +
172 /v4 x v1 \
173 / \
174 + -------------------------------- +
175 v5 v6
176
177 Radius: x-v2
178 Width: v1-v4
179 Axis vector: x-v1
180 Rotation axis: [0, 0, 1]
181
182 Parameters
183 ----------
184 center: list
185 Coordinates of center of polygon
186 radius : float
187 Radius of polygon (distance between center x and vertex v2)
188 width : float
189 Width of neck, i.e. distance between vertex v2 and v4
190 add_center : bool
191 True if we include the center to the returned list (first point in the returned list will be the center if the flag is true)
192
193 Returns
194 -------
195 list
196 Coordinates of points
197 """
198 axis = [1., 0., 0.]
199 rotate_axis = [0., 0., 1.]
200
201 x1 = rotate(axis, np.pi/3., rotate_axis)
202 x2 = rotate(axis, -np.pi/3., rotate_axis)
203
204 points = []
205 if add_center:
206 points.append(center)
207
208 # v1
209 points.append([center[i] + width*0.5*axis[i] for i in range(3)])
210 # v2
211 points.append([center[i] + radius*x1[i] for i in range(3)])
212 # v3
213 points.append([center[i] + radius*x1[i] - radius*axis[i] for i in range(3)])
214 # v4
215 points.append([center[i] - width*0.5*axis[i] for i in range(3)])
216 # v5
217 v6 = [center[i] + radius*x2[i] for i in range(3)]
218 points.append([v6[i] - radius*axis[i] for i in range(3)])
219 # v6
220 points.append(v6)
221
222 return points
223
224
225def does_intersect(p, particles, padding):
226 """
227 Returns true if particle p intersects or is near enough to existing particles
228
229 Parameters
230 ----------
231 p : list
232 Coordinates of center and radius of particle [x,y,z,r]
233 particles : list
234 List of center + radius of multiple particles. E.g. particles[0] is a list containing coordinates of center and radius.
235 padding: float
236 Minimum distance between circle boundaries such that if two circles
237
238 Returns
239 -------
240 bool
241 True if particle intersects or is near enough to one of the particle in the list
242 """
243 if len(p) < 4: raise Exception('p = {} must have atleast 4 elements'.format(p))
244 if len(particles) == 0: raise Exception('particles = {} can not be empty'.format(particles))
245 if padding < 0.: raise Exception('padding = {} can not be negative'.format(padding))
246
247 for q in particles:
248 if len(q) < 4: raise Exception('q = {} in particles must have atleast 4 elements'.format(q))
249 pq = np.array([p[i] - q[i] for i in range(3)])
250 if np.linalg.norm(pq) <= p[3] + q[3] + padding:
251 return True
252 return False
253
254def does_intersect_rect(p, particles, padding, rect, is_3d = False):
255 """
256 Returns true if particle p is sufficiently close or outside the rectangle (in 2d) or cuboid (in 3d)
257
258 Parameters
259 ----------
260 p : list
261 Coordinates of center and radius of particle [x,y,z,r]
262 particles : list
263 List of center + radius of multiple particles. E.g. particles[0] is a list containing coordinates of center and radius.
264 padding: float
265 Minimum distance between circle boundaries such that if two circles
266 rect: list
267 Coordinates of left-bottom and right-top corner points of rectangle (2d) or cuboid (3d). E.g. [x1 y1, z1, x2, y2, z2]
268 is_3d: bool
269 True if we are dealing with cuboid
270
271 Returns
272 -------
273 bool
274 True if particle intersects or is near enough to the rectangle
275 """
276 if len(p) < 4: raise Exception('p = {} must have atleast 4 elements'.format(p))
277 if len(particles) == 0: raise Exception('particles = {} can not be empty'.format(particles))
278 if padding < 0.: raise Exception('padding = {} can not be negative'.format(padding))
279 if len(rect) < 6: raise Exception('rect = {} must have 6 elements'.format(rect))
280
281 pr = [p[0] - p[3], p[1] - p[3], p[2], p[0] + p[3], p[1] + p[3], p[2]]
282 if is_3d:
283 pr[2] -= p[3]
284 pr[5] += p[3]
285
286 if pr[0] < rect[0] + padding or pr[1] < rect[1] + padding or pr[3] > rect[3] - padding or pr[4] > rect[4] - padding:
287 if is_3d:
288 if pr[2] < rect[2] + padding or pr[5] > rect[5] - padding:
289 return True
290 else:
291 return True
292 return False
293
294
296 m = {}
297 for e in gmsh.model.getEntities():
298 bnd = gmsh.model.getBoundary([e])
299 nod = gmsh.model.mesh.getNodes(e[0], e[1])
300 ele = gmsh.model.mesh.getElements(e[0], e[1])
301 m[e] = (bnd, nod, ele)
302
303 return m
304
305# transform the mesh and create new discrete entities to store it
306# source - https://gitlab.onelab.info/gmsh/gmsh/-/blob/master/examples/api/mirror_mesh.py
307def gmsh_transform(m, offset_entity, offset_node, offset_element, tx, ty, tz):
308 for e in sorted(m):
309 gmsh.model.addDiscreteEntity(
310 e[0], e[1] + offset_entity,
311 [(abs(b[1]) + offset_entity) * int(math.copysign(1, b[1])) for b in m[e][0]])
312 coord = []
313 for i in range(0, len(m[e][1][1]), 3):
314 x = m[e][1][1][i] * tx
315 y = m[e][1][1][i + 1] * ty
316 z = m[e][1][1][i + 2] * tz
317 coord.append(x)
318 coord.append(y)
319 coord.append(z)
320 gmsh.model.mesh.addNodes(e[0], e[1] + offset_entity,
321 [n + offset_node for n in m[e][1][0]], coord)
322 gmsh.model.mesh.addElements(e[0], e[1] + offset_entity, m[e][2][0],
323 [[t + offset_element for t in typ] for typ in m[e][2][1]],
324 [[n + offset_node for n in typ] for typ in m[e][2][2]])
325 if (tx * ty * tz) < 0: # reverse the orientation
326 gmsh.model.mesh.reverse([(e[0], e[1] + offset_entity)])
327
328def gmsh_transform_general(m, offset_entity, offset_node, offset_element, angle, axis=[0, 0, 1]):
329 """Rotate mesh by angle (in radians) around specified axis
330
331 Parameters:
332 m - mesh data dictionary
333 offset_entity - offset for entity numbers
334 offset_node - offset for node numbers
335 offset_element - offset for element numbers
336 angle - rotation angle in radians
337 axis - rotation axis vector (default is z-axis [0,0,1])
338 """
339
340 for e in sorted(m):
341 gmsh.model.addDiscreteEntity(
342 e[0], e[1] + offset_entity,
343 [(abs(b[1]) + offset_entity) * int(math.copysign(1, b[1])) for b in m[e][0]])
344 coord = []
345 # Apply rotation to each point
346 for i in range(0, len(m[e][1][1]), 3):
347 xi = np.array([m[e][1][1][i], m[e][1][1][i + 1], m[e][1][1][i + 2]])
348
349 xi_rot = rotate(xi, angle, axis)
350
351 coord.append(xi_rot[0])
352 coord.append(xi_rot[1])
353 coord.append(xi_rot[2])
354
355 gmsh.model.mesh.addNodes(e[0], e[1] + offset_entity,
356 [n + offset_node for n in m[e][1][0]], coord)
357 gmsh.model.mesh.addElements(e[0], e[1] + offset_entity, m[e][2][0],
358 [[t + offset_element for t in typ] for typ in m[e][2][1]],
359 [[n + offset_node for n in typ] for typ in m[e][2][2]])
360
362 """Translate mesh by vector xc
363
364 Parameters:
365 xc - translation vector [x, y, z]
366 """
367 # Get the nodes
368 nodeTags, coord, paramCoord = gmsh.model.mesh.getNodes()
369 numNodes = len(nodeTags)
370
371 # Translation vector
372 translation_vector = np.array([xc[0], xc[1], xc[2]])
373
374 # Update each node individually
375 for i in range(numNodes):
376 nodeTag = nodeTags[i]
377 old_coord = coord[3*i:3*(i+1)]
378 new_coord = old_coord + translation_vector
379 # Handle paramCoord properly - use empty list if paramCoord is None or empty
380 param = paramCoord[3*i:3*(i+1)] if paramCoord is not None and len(paramCoord) > 0 else []
381 gmsh.model.mesh.setNode(nodeTag, new_coord, param)
gmsh_translate(xc)
Definition geom_util.py:361
get_ref_drum_points(center, radius, width, add_center=False)
Definition geom_util.py:161
does_intersect_rect(p, particles, padding, rect, is_3d=False)
Definition geom_util.py:254
get_gmsh_entities()
Definition geom_util.py:295
gmsh_transform(m, offset_entity, offset_node, offset_element, tx, ty, tz)
Definition geom_util.py:307
get_ref_triangle_points(center, radius, add_center=False)
Definition geom_util.py:68
gmsh_transform_general(m, offset_entity, offset_node, offset_element, angle, axis=[0, 0, 1])
Definition geom_util.py:328
get_ref_rect_points(center, Lx, Ly, add_center=False)
Definition geom_util.py:32
get_ref_hex_points(center, radius, add_center=False)
Definition geom_util.py:115
rotate(p, theta, axis)
Definition geom_util.py:7
does_intersect(p, particles, padding)
Definition geom_util.py:225