File: deformableelementsutils.py

package info (click to toggle)
yade 2025.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 33,308 kB
  • sloc: cpp: 93,298; python: 50,409; sh: 577; makefile: 162
file content (388 lines) | stat: -rw-r--r-- 12,509 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
# encoding: utf-8
#
# utility functions for deformable cohesive elements
#
# burak er buraker88@yandex.com

import math, random, doctest, geom, numpy
from yade import *
from yade.wrapper import *
from yade.utils import *
try:  # use psyco if available
	import psyco
	psyco.full()
except ImportError:
	pass

from yade.minieigenHP import *

# c++ implementations for performance reasons
from yade._utils import *
from yade.utils import _commonBodySetup, randomColor


#Deformable Element Node
def node(center, radius, dynamic=None, fixed=True, wire=False, color=None, highlight=False, material=-1, mask=1):
	"""Create sphere with given parameters; mass and inertia computed automatically.

	Last assigned material is used by default (*material* = -1), and utils.defaultMaterial() will be used if no material is defined at all.

	:param Vector3 center: center
	:param float radius: radius
	:param float dynamic: deprecated, see "fixed"
	:param float fixed: generate the body with all DOFs blocked?
	:param material:
		specify :yref:`Body.material`; different types are accepted:
			* int: O.materials[material] will be used; as a special case, if material==-1 and there is no shared materials defined, utils.defaultMaterial() will be assigned to O.materials[0]
			* string: label of an existing material that will be used
			* :yref:`Material` instance: this instance will be used
			* callable: will be called without arguments; returned Material value will be used (Material factory object, if you like)
	:param int mask: :yref:`Body.mask` for the body
	:param wire: display as wire sphere?
	:param highlight: highlight this body in the viewer?
	:param Vector3-or-None: body's color, as normalized RGB; random color will be assigned if ``None``.
	
	:return:
		A Body instance with desired characteristics.


	Creating default shared material if none exists neither is given::

		>>> O.reset()
		>>> from yade import utils
		>>> len(O.materials)
		0
		>>> s0=utils.sphere([2,0,0],1)
		>>> len(O.materials)
		1

	Instance of material can be given::

		>>> s1=utils.sphere([0,0,0],1,wire=False,color=(0,1,0),material=ElastMat(young=30e9,density=2e3))
		>>> s1.shape.wire
		False
		>>> s1.shape.color
		Vector3(0,1,0)
		>>> s1.mat.density
		2000.0

	Material can be given by label::

		>>> O.materials.append(FrictMat(young=10e9,poisson=.11,label='myMaterial'))
		1
		>>> s2=utils.sphere([0,0,2],1,material='myMaterial')
		>>> s2.mat.label
		'myMaterial'
		>>> s2.mat.poisson
		0.11

	Finally, material can be a callable object (taking no arguments), which returns a Material instance.
	Use this if you don't call this function directly (for instance, through yade.pack.randomDensePack), passing
	only 1 *material* parameter, but you don't want material to be shared.

	For instance, randomized material properties can be created like this:

		>>> import random
		>>> def matFactory(): return ElastMat(young=1e10*random.random(),density=1e3+1e3*random.random())
		...
		>>> s3=utils.sphere([0,2,0],1,material=matFactory)
		>>> s4=utils.sphere([1,2,0],1,material=matFactory)

	"""
	b = Body()
	b.shape = Node(radius=radius, color=color if color else randomColor(), wire=wire, highlight=highlight)
	V = (4. / 3) * math.pi * radius**3
	geomInert = (2. / 5.) * V * radius**2
	_commonBodySetup(b, V, Vector3(geomInert, geomInert, geomInert), material, pos=center, dynamic=dynamic, fixed=fixed, blockedDOFs='XYZ')
	b.aspherical = False
	b.mask = mask
	b.bounded = True
	return b


# Meshing
def clear_mesh(gmshmeshhandle_):
	sceneelements = gmshmeshhandle_[1]

	#first remove elements and nodes from the scene
	for elem in sceneelements:
		bdy = elem[0]
		nodes = elem[1]
		O.bodies.erase(bdy.id)
		for node in nodes:
			O.bodies.erase(node.id)


def tetrahedronvolume(nodes):
	V = 0.166666666 * numpy.linalg.det(
	        [
	                [nodes[0][0], nodes[1][0], nodes[2][0], nodes[3][0]], [nodes[0][1], nodes[1][1], nodes[2][1], nodes[3][1]],
	                [nodes[0][2], nodes[1][2], nodes[2][2], nodes[3][2]], [1, 1, 1, 1]
	        ]
	)
	return V


def tetrahedral_mesh_generator(filename, tetrahedralelementshape, elementmat, interfaceelementshape, interfacemat):

	[nodeList, elementList, interactionList] = mshreader(filename)

	nodes = nodeList

	meshelements = elementList

	interfacepairs = interactionList

	scenebodies = []

	for elem in elementList:
		#Create the corresponding mesh element
		nodesofelement = [nodes[elem[0]], nodes[elem[1]], nodes[elem[2]], nodes[elem[3]]]
		noderadius = 0.1 * (0.75 * tetrahedronvolume(nodesofelement))**0.33333333

		[elbody, nodebodies] = tetrahedral_element(elementmat, nodesofelement, tetrahedralelementshape, radius=noderadius)

		scenebodies.append([elbody, nodebodies])

	#Create the interfacing element pairs
	for interpair in interfacepairs:

		#get element pair
		elementpair = interpair[0]
		#get node pairs
		pairofnodes = interpair[1]
		interfaceelementpairs = []

		for pair in pairofnodes:

			firstid = pair[0]
			secondid = pair[1]
			el1nodebodies = scenebodies[elementpair[0]][1]
			el2nodebodies = scenebodies[elementpair[1]][1]
			node1 = el1nodebodies[firstid]
			node2 = el2nodebodies[secondid]
			interfaceelementpairs.append([node1, node2])

		[elbody, nodebodies] = interaction_element(interfacemat, interfaceelementpairs, interfaceelementshape)
		scenebodies.append([elbody, nodebodies])

	return scenebodies


#Read mesh file and get elements nodes and interactions
def mshreader(meshfile="file.mesh", shift=Vector3.Zero, scale=1.0, orientation=Quaternion.Identity, **kw):
	""" Imports volume mesh from gmsh2 file
	:Parameters:
		`shift`: [float,float,float]
			[X,Y,Z] parameter moves the specimen.
		`scale`: float
			factor scales the given data.
		`orientation`: quaternion
			orientation of the imported mesh
		`**kw`: (unused keyword arguments)
				is passed to :yref:`yade.utils.facet`
	:Returns: list of nodes and elements for the specimen.
	"""
	infile = open(meshfile, "r")
	lines = infile.readlines()
	infile.close()

	nodelistVector3 = []
	elementList = []
	interactionList = []
	findVerticesString = 0

	while (lines[findVerticesString].split()[0] != '$Nodes'):  #Find the string with the number of Vertices
		findVerticesString += 1
	findVerticesString += 1
	numNodes = int(lines[findVerticesString].split()[0])

	for line in lines[findVerticesString + 1:numNodes + findVerticesString + 1]:
		data = line.split()
		nodelistVector3.append(orientation * Vector3(float(data[1]) * scale, float(data[2]) * scale, float(data[3]) * scale) + shift)

	findElementString = findVerticesString + numNodes
	while (lines[findElementString].split()[0] != '$Elements'):  #Find the string with the number of Elements
		findElementString += 1
	findElementString += 1
	numElements = int(lines[findElementString].split()[0])

	for line in lines[findElementString + 1:findElementString + numElements + 1]:
		data = line.split()
		numberofnodes = data[1]

		if (int(numberofnodes) == 4):  #tetrahedral element

			id1 = int(data[5]) - 1
			id2 = int(data[6]) - 1
			id3 = int(data[7]) - 1
			id4 = int(data[8]) - 1
			elementList.append([id1, id2, id3, id4])
	#Create the interaction list
	i = 0
	numVolElements = elementList.__len__()
	for elem in elementList:
		j = i + 1

		while j < numVolElements:

			pair = check_coinciding_nodes(elem, elementList[j])
			if (pair.__len__() > 0):
				interactionList.append([Vector2i(i, j), pair])
			j = j + 1
		i = i + 1
	return [nodelistVector3, elementList, interactionList]


def check_coinciding_nodes(element1, element2):

	pairstemp = []
	#for each node of the element1
	i = 0
	j = 0
	for node1 in element1:

		for node2 in element2:

			if (node1 == node2):
				pairstemp.append([i, j])
			j = j + 1
		i = i + 1
		j = 0
	if (3 <= pairstemp.__len__()):
		pairs = pairstemp
	else:
		pairs = []
	return pairs


def interaction_element(
        material, nodepairs, elementshape, radius=0.0015, dynamic=None, fixed=True, wire=False, color=Vector3(1, 0, 0), highlight=False, mask=1
):

	# triangles for drawing(node indices)
	#faces=[Vector3(0,1,3),Vector3(1,2,3),Vector3(2,0,3),Vector3(0,1,2)]
	faces = []
	shape = elementshape()
	shape.color = color if color else randomColor()
	[body_, nodes_] = finite_element(material, shape, nodepairs, faces, radius, interface=True)

	return [body_, nodes_]


def tetrahedral_element(material, nodes, elementshape, radius=0.0015, dynamic=None, fixed=True, wire=False, color=Vector3(1, 0, 0), highlight=False, mask=1):

	# triangles for drawing(node indices)
	#faces are in the format [face_vertex0,face_vertex1,face_vertex2,opposite_vertex]
	faces = [Vector3(1, 2, 0), Vector3(2, 1, 3), Vector3(2, 0, 3), Vector3(3, 1, 0)]

	shape = elementshape()
	shape.color = color if color else randomColor()
	[body_, nodes_] = finite_element(material, shape, nodes, faces, radius)

	return [body_, nodes_]


def finite_element(material, shape, nodes, faces, radius, dynamic=None, fixed=True, wire=False, Color=None, highlight=False, mask=1, interface=False):
	"""Create sphere with given parameters; mass and inertia computed automatically.

	Last assigned material is used by default (*material* = -1), and utils.defaultMaterial() will be used if no material is defined at all.

	:param Vector3 center: center
	:param float radius: radius
	:param float dynamic: deprecated, see "fixed"
	:param float fixed: generate the body with all DOFs blocked?
	:param material:
		specify :yref:`Body.material`; different types are accepted:
			* int: O.materials[material] will be used; as a special case, if material==-1 and there is no shared materials defined, utils.defaultMaterial() will be assigned to O.materials[0]
			* string: label of an existing material that will be used
			* :yref:`Material` instance: this instance will be used
			* callable: will be called without arguments; returned Material value will be used (Material factory object, if you like)
	:param int mask: :yref:`Body.mask` for the body
	:param wire: display as wire sphere?
	:param highlight: highlight this body in the viewer?
	:param Vector3-or-None: body's color, as normalized RGB; random color will be assigned if ``None``.
	
	:return:
		A Body instance with desired characteristics.


	Creating default shared material if none exists neither is given::

		>>> O.reset()
		>>> from yade import utils
		>>> len(O.materials)
		0
		>>> s0=utils.sphere([2,0,0],1)
		>>> len(O.materials)
		1

	Instance of material can be given::

		>>> s1=utils.sphere([0,0,0],1,wire=False,color=(0,1,0),material=ElastMat(young=30e9,density=2e3))
		>>> s1.shape.wire
		False
		>>> s1.shape.color
		Vector3(0,1,0)
		>>> s1.mat.density
		2000.0

	Material can be given by label::

		>>> O.materials.append(FrictMat(young=10e9,poisson=.11,label='myMaterial'))
		1
		>>> s2=utils.sphere([0,0,2],1,material='myMaterial')
		>>> s2.mat.label
		'myMaterial'
		>>> s2.mat.poisson
		0.11

	Finally, material can be a callable object (taking no arguments), which returns a Material instance.
	Use this if you don't call this function directly (for instance, through yade.pack.randomDensePack), passing
	only 1 *material* parameter, but you don't want material to be shared.

	For instance, randomized material properties can be created like this:

		>>> import random
		>>> def matFactory(): return ElastMat(young=1e10*random.random(),density=1e3+1e3*random.random())
		...
		>>> s3=utils.sphere([0,2,0],1,material=matFactory)
		>>> s4=utils.sphere([1,2,0],1,material=matFactory)

	"""
	b = Body()
	b.shape = shape
	O.bodies.append(b)
	nodes_ret = []
	#b.shape.color=Color;
	#add nodes to the scene and keep them in the nodes list
	if (interface == False):
		#Deformable Element
		for vec in nodes:  # Second Example
			nod = node(vec, radius)
			O.bodies.append(nod)
			b.shape.addNode(nod)
			nodes_ret.append(nod)
		#add faces for drawing purposes
		for face in faces:
			b.shape.addFace(face)
	else:
		#Deformable Cohesive Element
		for nodepair in nodes:  # Second Example
			b.shape.addPair(nodepair[0], nodepair[1])
		#add faces for drawing purposes
		for face in faces:
			b.shape.addFace(face)

	V = 1
	#For symbolical
	geomInert = Vector3(1, 1, 1)  #For symbolical
	#Only add body to draw it not for integration, therefore block all dof
	_commonBodySetup(b, V, geomInert, material, pos=Vector3(0, 0, 0), dynamic=False, fixed=True, blockedDOFs='xyzXYZ')
	b.aspherical = False
	b.mask = mask
	b.bounded = False
	return [b, nodes_ret]


##**********************End of Deformable Elements*********************************