elasticipy 2.8.10__py3-none-any.whl → 3.0.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.
- Elasticipy/FourthOrderTensor.py +360 -44
- Elasticipy/Plasticity.py +222 -15
- Elasticipy/SecondOrderTensor.py +338 -66
- Elasticipy/StressStrainTensors.py +13 -8
- Elasticipy/ThermalExpansion.py +88 -17
- {elasticipy-2.8.10.dist-info → elasticipy-3.0.0.dist-info}/METADATA +2 -1
- elasticipy-3.0.0.dist-info/RECORD +15 -0
- {elasticipy-2.8.10.dist-info → elasticipy-3.0.0.dist-info}/WHEEL +1 -1
- elasticipy-2.8.10.dist-info/RECORD +0 -15
- {elasticipy-2.8.10.dist-info → elasticipy-3.0.0.dist-info}/LICENSE +0 -0
- {elasticipy-2.8.10.dist-info → elasticipy-3.0.0.dist-info}/top_level.txt +0 -0
Elasticipy/Plasticity.py
CHANGED
|
@@ -1,9 +1,73 @@
|
|
|
1
1
|
import numpy as np
|
|
2
|
+
from Elasticipy.StressStrainTensors import StrainTensor, StressTensor
|
|
2
3
|
|
|
3
4
|
|
|
5
|
+
class IsotropicHardening:
|
|
6
|
+
"""
|
|
7
|
+
Template class for isotropic hardening plasticity models
|
|
8
|
+
"""
|
|
9
|
+
def __init__(self, criterion='von Mises'):
|
|
10
|
+
"""
|
|
11
|
+
Create an instance of a plastic model, assuming isotropic hardening
|
|
12
|
+
|
|
13
|
+
Parameters
|
|
14
|
+
----------
|
|
15
|
+
criterion : str or PlasticityCriterion
|
|
16
|
+
Plasticity criterion to use. Can be 'von Mises', 'Tresca' or 'J2'. J2 is the same as von Mises.
|
|
17
|
+
"""
|
|
18
|
+
if isinstance(criterion, str):
|
|
19
|
+
criterion = criterion.lower()
|
|
20
|
+
if criterion in ('von mises', 'mises', 'vonmises', 'j2'):
|
|
21
|
+
self.criterion = VonMisesPlasticity
|
|
22
|
+
elif criterion == 'tresca':
|
|
23
|
+
self.criterion = TrescaPlasticity
|
|
24
|
+
else:
|
|
25
|
+
raise ValueError('The criterion can be "Tresca", "von Mises" or "J2".')
|
|
26
|
+
else:
|
|
27
|
+
self.criterion = criterion
|
|
28
|
+
self.plastic_strain = 0.0
|
|
29
|
+
|
|
30
|
+
def flow_stress(self, strain, **kwargs):
|
|
31
|
+
pass
|
|
32
|
+
|
|
33
|
+
def apply_strain(self, strain, **kwargs):
|
|
34
|
+
"""
|
|
35
|
+
Apply strain to the current JC model.
|
|
36
|
+
|
|
37
|
+
This function updates the internal variable to store hardening state.
|
|
38
|
+
|
|
39
|
+
Parameters
|
|
40
|
+
----------
|
|
41
|
+
strain : float or StrainTensor
|
|
42
|
+
kwargs : dict
|
|
43
|
+
Keyword arguments passed to flow_stress()
|
|
44
|
+
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
float
|
|
48
|
+
Associated flow stress (positive)
|
|
4
49
|
|
|
5
|
-
|
|
6
|
-
|
|
50
|
+
See Also
|
|
51
|
+
--------
|
|
52
|
+
flow_stress : compute the flow stress, given a cumulative equivalent strain
|
|
53
|
+
"""
|
|
54
|
+
if isinstance(strain, float):
|
|
55
|
+
self.plastic_strain += np.abs(strain)
|
|
56
|
+
elif isinstance(strain, StrainTensor):
|
|
57
|
+
self.plastic_strain += strain.eq_strain()
|
|
58
|
+
else:
|
|
59
|
+
raise ValueError('The applied strain must be float of StrainTensor')
|
|
60
|
+
return self.flow_stress(self.plastic_strain, **kwargs)
|
|
61
|
+
|
|
62
|
+
def compute_strain_increment(self, stress, **kwargs):
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
def reset_strain(self):
|
|
66
|
+
self.plastic_strain = 0.0
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class JohnsonCook(IsotropicHardening):
|
|
70
|
+
def __init__(self, A, B, n, C=None, eps_dot_ref=1.0, m=None, T0=25, Tm=None, criterion='von Mises'):
|
|
7
71
|
"""
|
|
8
72
|
Constructor for a Jonhson-Cook (JC) model.
|
|
9
73
|
|
|
@@ -28,6 +92,8 @@ class JohnsonCook:
|
|
|
28
92
|
Reference temperature
|
|
29
93
|
Tm : float, optional
|
|
30
94
|
Melting temperature (at which the flow stress is zero)
|
|
95
|
+
criterion : str or PlasticityCriterion, optional
|
|
96
|
+
Plasticity criterion to use. It can be 'von Mises' or 'Tresca'.
|
|
31
97
|
|
|
32
98
|
Notes
|
|
33
99
|
-----
|
|
@@ -50,6 +116,7 @@ class JohnsonCook:
|
|
|
50
116
|
1 & \\text{otherwise}
|
|
51
117
|
\\end{cases}
|
|
52
118
|
"""
|
|
119
|
+
super().__init__(criterion=criterion)
|
|
53
120
|
self.A = A
|
|
54
121
|
self.B = B
|
|
55
122
|
self.C = C
|
|
@@ -74,7 +141,7 @@ class JohnsonCook:
|
|
|
74
141
|
Temperature. If float, the temperature is supposed to be homogeneous for every value of eps_p.
|
|
75
142
|
Returns
|
|
76
143
|
-------
|
|
77
|
-
numpy.ndarray
|
|
144
|
+
float or numpy.ndarray
|
|
78
145
|
Flow stress
|
|
79
146
|
"""
|
|
80
147
|
eps_p = np.asarray(eps_p)
|
|
@@ -97,29 +164,169 @@ class JohnsonCook:
|
|
|
97
164
|
return stress
|
|
98
165
|
|
|
99
166
|
|
|
100
|
-
|
|
167
|
+
|
|
168
|
+
def compute_strain_increment(self, stress, T=None, apply_strain=True, criterion='von Mises'):
|
|
101
169
|
"""
|
|
102
|
-
Given the equivalent stress, compute the strain
|
|
170
|
+
Given the equivalent stress, compute the strain increment with respect to the normality rule.
|
|
103
171
|
|
|
104
172
|
Parameters
|
|
105
173
|
----------
|
|
106
|
-
stress : float or
|
|
107
|
-
Equivalent stress
|
|
108
|
-
T : float
|
|
174
|
+
stress : float or StressTensor
|
|
175
|
+
Equivalent stress to compute the stress from, or full stress tensor.
|
|
176
|
+
T : float
|
|
109
177
|
Temperature
|
|
178
|
+
apply_strain : bool, optional
|
|
179
|
+
If true, the JC model will be updated to account for the applied strain (hardening)
|
|
180
|
+
criterion : str, optional
|
|
181
|
+
Plasticity criterion to consider to compute the equivalent stress and apply the normality rule.
|
|
182
|
+
It can be 'von Mises', 'Tresca' or 'J2'. 'J2' is equivalent to 'von Mises'.
|
|
110
183
|
|
|
111
184
|
Returns
|
|
112
185
|
-------
|
|
113
|
-
|
|
114
|
-
|
|
186
|
+
StrainTensor or float
|
|
187
|
+
Increment of plastic strain. If the input stress is float, only the magnitude of the increment will be
|
|
188
|
+
returned (float value). If the stress is of type StressTensor, the returned value will be a full
|
|
189
|
+
StrainTensor.
|
|
190
|
+
|
|
191
|
+
See Also
|
|
192
|
+
--------
|
|
193
|
+
apply_strain : apply strain to the JC model and updates its hardening value
|
|
115
194
|
"""
|
|
116
|
-
|
|
195
|
+
if isinstance(stress, StressTensor):
|
|
196
|
+
eq_stress = self.criterion.eq_stress(stress)
|
|
197
|
+
else:
|
|
198
|
+
eq_stress = stress
|
|
117
199
|
if T is None:
|
|
118
|
-
|
|
200
|
+
if eq_stress > self.A:
|
|
201
|
+
k = eq_stress - self.A
|
|
202
|
+
total_strain = (1 / self.B * k) ** (1 / self.n)
|
|
203
|
+
strain_increment = np.max((total_strain - self.plastic_strain, 0))
|
|
204
|
+
else:
|
|
205
|
+
strain_increment = 0.0
|
|
119
206
|
else:
|
|
120
207
|
if self.T0 is None or self.Tm is None or self.m is None:
|
|
121
208
|
raise ValueError('T0, Tm and m must be defined for using a temperature-dependent model')
|
|
122
209
|
else:
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
210
|
+
if T >= self.Tm:
|
|
211
|
+
strain_increment = np.inf
|
|
212
|
+
else:
|
|
213
|
+
theta = (T - self.T0) / (self.Tm - self.T0)
|
|
214
|
+
theta_m = theta**self.m
|
|
215
|
+
k = (eq_stress / (1 - theta_m) - self.A)
|
|
216
|
+
if k<0:
|
|
217
|
+
strain_increment = 0.0
|
|
218
|
+
else:
|
|
219
|
+
total_strain = (1/self.B * k)**(1/self.n)
|
|
220
|
+
strain_increment = np.max((total_strain - self.plastic_strain, 0))
|
|
221
|
+
if apply_strain:
|
|
222
|
+
self.apply_strain(strain_increment)
|
|
223
|
+
|
|
224
|
+
if isinstance(stress, StressTensor):
|
|
225
|
+
n = self.criterion.normal(stress)
|
|
226
|
+
return n * strain_increment
|
|
227
|
+
else:
|
|
228
|
+
return strain_increment
|
|
229
|
+
|
|
230
|
+
def reset_strain(self):
|
|
231
|
+
"""
|
|
232
|
+
Reinitialize the plastic strain to 0
|
|
233
|
+
"""
|
|
234
|
+
self.plastic_strain = 0.0
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
class PlasticityCriterion:
|
|
238
|
+
@staticmethod
|
|
239
|
+
def eq_stress(stress, **kwargs):
|
|
240
|
+
"""
|
|
241
|
+
Return the equivalent stress, with respect to the plasticity criterion.
|
|
242
|
+
|
|
243
|
+
Parameters
|
|
244
|
+
----------
|
|
245
|
+
stress : StressTensor
|
|
246
|
+
Stress to compute the equivalent stress from
|
|
247
|
+
kwargs : dict
|
|
248
|
+
keyword arguments passed to the function
|
|
249
|
+
Returns
|
|
250
|
+
-------
|
|
251
|
+
float or numpy.ndarray
|
|
252
|
+
"""
|
|
253
|
+
pass
|
|
254
|
+
|
|
255
|
+
def normal(self, stress, **kwargs):
|
|
256
|
+
"""
|
|
257
|
+
Apply the normality rule
|
|
258
|
+
|
|
259
|
+
Parameters
|
|
260
|
+
----------
|
|
261
|
+
stress : StressTensor
|
|
262
|
+
Stress tensor to apply the normality rule
|
|
263
|
+
kwargs : dict
|
|
264
|
+
Keyword arguments passed to the function
|
|
265
|
+
|
|
266
|
+
Returns
|
|
267
|
+
-------
|
|
268
|
+
StrainTensor
|
|
269
|
+
Normalized direction of plastic flow
|
|
270
|
+
"""
|
|
271
|
+
pass
|
|
272
|
+
|
|
273
|
+
class VonMisesPlasticity(PlasticityCriterion):
|
|
274
|
+
@staticmethod
|
|
275
|
+
def eq_stress(stress, **kwargs):
|
|
276
|
+
return stress.vonMises()
|
|
277
|
+
|
|
278
|
+
@staticmethod
|
|
279
|
+
def normal(stress, **kwargs):
|
|
280
|
+
eq_stress = stress.vonMises()
|
|
281
|
+
dev_stress = stress.deviatoric_part()
|
|
282
|
+
gradient_tensor = dev_stress / eq_stress
|
|
283
|
+
return StrainTensor(3 / 2 * gradient_tensor.matrix)
|
|
284
|
+
|
|
285
|
+
class TrescaPlasticity(PlasticityCriterion):
|
|
286
|
+
@staticmethod
|
|
287
|
+
def eq_stress(stress, **kwargs):
|
|
288
|
+
return stress.Tresca()
|
|
289
|
+
|
|
290
|
+
@staticmethod
|
|
291
|
+
def normal(stress, **kwargs):
|
|
292
|
+
vals, dirs = stress.eig()
|
|
293
|
+
u1 = dirs[..., 0]
|
|
294
|
+
u3 = dirs[..., 2]
|
|
295
|
+
s1 = vals[..., 0]
|
|
296
|
+
s2 = vals[..., 1]
|
|
297
|
+
s3 = vals[..., 2]
|
|
298
|
+
a = np.einsum('...i,...j->...ij', u1, u1)
|
|
299
|
+
b = np.einsum('...i,...j->...ij', u3, u3)
|
|
300
|
+
normal = a - b
|
|
301
|
+
singular_points = np.logical_or(s2 == s1, s2 == s3)
|
|
302
|
+
normal[singular_points] = VonMisesPlasticity().normal(stress[singular_points]).matrix
|
|
303
|
+
normal[np.logical_and(s2 == s1, s2 == s3)] = 0.0
|
|
304
|
+
strain = StrainTensor(normal)
|
|
305
|
+
return strain / strain.eq_strain()
|
|
306
|
+
|
|
307
|
+
class DruckerPrager(PlasticityCriterion):
|
|
308
|
+
def __init__(self, alpha):
|
|
309
|
+
"""
|
|
310
|
+
Create a Drucker-Prager (DG) plasticity criterion.
|
|
311
|
+
|
|
312
|
+
Parameters
|
|
313
|
+
----------
|
|
314
|
+
alpha : float
|
|
315
|
+
Pressure dependence parameters (see notes for details)
|
|
316
|
+
|
|
317
|
+
Notes
|
|
318
|
+
-----
|
|
319
|
+
The pressure-dependent DG plasticity criterion assumes that the equivalent stress is defined as:
|
|
320
|
+
|
|
321
|
+
|
|
322
|
+
"""
|
|
323
|
+
self.alpha = alpha
|
|
324
|
+
|
|
325
|
+
def eq_stress(self, stress, **kwargs):
|
|
326
|
+
return (stress.J2**0.5 + self.alpha * stress.I1) / (1/3**0.5 + self.alpha)
|
|
327
|
+
|
|
328
|
+
def normal(self, stress, **kwargs):
|
|
329
|
+
J2 = stress.J2
|
|
330
|
+
gradient = stress.deviatoric_part() / (2 * J2**0.5) + self.alpha * StressTensor.eye(stress.shape)
|
|
331
|
+
strain = StrainTensor(gradient.matrix)
|
|
332
|
+
return strain / strain.eq_strain()
|