elasticipy 4.2.0__py3-none-any.whl → 5.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/gui.py +101 -2
- Elasticipy/interfaces/FEPX.py +3 -3
- Elasticipy/plasticity.py +179 -34
- Elasticipy/spherical_function.py +43 -4
- Elasticipy/tensors/elasticity.py +667 -141
- Elasticipy/tensors/fourth_order.py +895 -172
- Elasticipy/tensors/mapping.py +48 -0
- Elasticipy/tensors/second_order.py +49 -7
- Elasticipy/tensors/stress_strain.py +209 -3
- Elasticipy/tensors/thermal_expansion.py +6 -1
- {elasticipy-4.2.0.dist-info → elasticipy-5.0.0.dist-info}/METADATA +40 -21
- elasticipy-5.0.0.dist-info/RECORD +24 -0
- elasticipy-5.0.0.dist-info/entry_points.txt +2 -0
- elasticipy-4.2.0.dist-info/RECORD +0 -23
- {elasticipy-4.2.0.dist-info → elasticipy-5.0.0.dist-info}/WHEEL +0 -0
- {elasticipy-4.2.0.dist-info → elasticipy-5.0.0.dist-info}/licenses/LICENSE +0 -0
- {elasticipy-4.2.0.dist-info → elasticipy-5.0.0.dist-info}/top_level.txt +0 -0
Elasticipy/tensors/elasticity.py
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
from Elasticipy.tensors.fourth_order import SymmetricFourthOrderTensor
|
|
1
|
+
from Elasticipy.tensors.fourth_order import SymmetricFourthOrderTensor, kelvin_mapping, _isotropic_matrix
|
|
2
2
|
from Elasticipy.spherical_function import SphericalFunction, HyperSphericalFunction
|
|
3
3
|
from Elasticipy.crystal_symmetries import SYMMETRIES
|
|
4
|
+
from Elasticipy.tensors.second_order import SymmetricSecondOrderTensor
|
|
4
5
|
from Elasticipy.tensors.stress_strain import StrainTensor, StressTensor
|
|
5
|
-
from Elasticipy.tensors.mapping import VoigtMapping
|
|
6
|
+
from Elasticipy.tensors.mapping import VoigtMapping
|
|
7
|
+
from functools import wraps
|
|
6
8
|
import numpy as np
|
|
7
9
|
import re
|
|
8
10
|
from warnings import warn
|
|
9
11
|
|
|
12
|
+
def _elementwise_property(func):
|
|
13
|
+
@property
|
|
14
|
+
@wraps(func)
|
|
15
|
+
def wrapper(self, *args, **kwargs):
|
|
16
|
+
if getattr(self, "ndim", 0) != 0:
|
|
17
|
+
flat = self.flatten()
|
|
18
|
+
result = [getattr(e, func.__name__) for e in flat]
|
|
19
|
+
return np.array(result).reshape(self.shape)
|
|
20
|
+
return func(self, *args, **kwargs)
|
|
21
|
+
return wrapper
|
|
22
|
+
|
|
23
|
+
|
|
10
24
|
def _parse_tensor_components(prefix, **kwargs):
|
|
11
25
|
pattern = r'^{}(\d{{2}})$'.format(prefix)
|
|
12
26
|
value = dict()
|
|
@@ -19,15 +33,6 @@ def _parse_tensor_components(prefix, **kwargs):
|
|
|
19
33
|
def _indices2str(ij):
|
|
20
34
|
return f'{ij[0] + 1}{ij[1] + 1}'
|
|
21
35
|
|
|
22
|
-
def _isotropic_matrix(C11, C12, C44):
|
|
23
|
-
return np.array([[C11, C12, C12, 0, 0, 0],
|
|
24
|
-
[C12, C11, C12, 0, 0, 0],
|
|
25
|
-
[C12, C12, C11, 0, 0, 0],
|
|
26
|
-
[0, 0, 0, C44, 0, 0],
|
|
27
|
-
[0, 0, 0, 0, C44, 0],
|
|
28
|
-
[0, 0, 0, 0, 0, C44]])
|
|
29
|
-
|
|
30
|
-
|
|
31
36
|
def _check_definite_positive(mat):
|
|
32
37
|
try:
|
|
33
38
|
np.linalg.cholesky(mat)
|
|
@@ -47,30 +52,33 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
47
52
|
"""
|
|
48
53
|
Class for manipulating fourth-order stiffness tensors.
|
|
49
54
|
"""
|
|
50
|
-
|
|
55
|
+
_tensor_name = 'Stiffness'
|
|
51
56
|
_C11_C12_factor = 0.5
|
|
52
57
|
_C46_C56_factor = 1.0
|
|
53
58
|
_component_prefix = 'C'
|
|
54
59
|
|
|
55
|
-
def __init__(self, M,
|
|
60
|
+
def __init__(self, M, check_positive_definite=True, phase_name= None, mapping=VoigtMapping(), **kwargs):
|
|
56
61
|
"""
|
|
57
|
-
Construct
|
|
62
|
+
Construct a stiffness tensor or an array of stiffness tensors.
|
|
58
63
|
|
|
59
|
-
The
|
|
64
|
+
The stiffness tensor can be constructed from a (6,6) matrix or slices of (6,6) matrices. These matrices must be
|
|
65
|
+
symmetric. An error is thrown if this matrix in not definite positive (except if
|
|
66
|
+
``check_positive_definite==False``, see below). The input argument can also be the full tensor (array of shape
|
|
67
|
+
(...,3,3,3,3)).
|
|
60
68
|
|
|
61
69
|
Parameters
|
|
62
70
|
----------
|
|
63
|
-
M : np.ndarray
|
|
64
|
-
(6,6) matrix corresponding to the stiffness tensor, written using the Voigt notation, or array of shape
|
|
65
|
-
(3,3,3,3).
|
|
66
|
-
phase_name : str,
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
71
|
+
M : list or np.ndarray
|
|
72
|
+
(...,6,6) matrix corresponding to the stiffness tensor, written using the Voigt notation, or array of shape
|
|
73
|
+
(...,3,3,3,3).
|
|
74
|
+
phase_name : str, optional
|
|
75
|
+
Phase name to display
|
|
76
|
+
check_positive_definite : bool, optional
|
|
77
|
+
Whether to check if the input matrix is positive definite or not. True by default.
|
|
70
78
|
check_symmetry : bool, optional
|
|
71
79
|
Whether to check or not that the input matrix is symmetric.
|
|
72
80
|
force_symmetry : bool, optional
|
|
73
|
-
If true, the major symmetry of the tensor is
|
|
81
|
+
If true, the major symmetry of the tensor is forced
|
|
74
82
|
mapping : str or MappingConvention
|
|
75
83
|
mapping convention to use. Default is VoigtMapping.
|
|
76
84
|
|
|
@@ -80,11 +88,94 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
80
88
|
results you will get when performing operations (Young's modulus, "product" with strain tensor etc.) will be
|
|
81
89
|
consistent with these units. For instance, if the stiffness tensor is defined in GPa, the computed stress will
|
|
82
90
|
be given in GPa as well.
|
|
91
|
+
|
|
92
|
+
Examples
|
|
93
|
+
--------
|
|
94
|
+
Create a stiffness tensor for cubic symmetry:
|
|
95
|
+
|
|
96
|
+
>>> matrix = [[200, 40, 40, 0, 0, 0 ],
|
|
97
|
+
... [40, 200, 40, 0, 0, 0 ],
|
|
98
|
+
... [40, 40, 200, 0, 0, 0 ],
|
|
99
|
+
... [0, 0, 0, 20, 0, 0 ],
|
|
100
|
+
... [0, 0, 0, 0, 20, 0 ],
|
|
101
|
+
... [0, 0, 0, 0, 0, 20]]
|
|
102
|
+
>>> from Elasticipy.tensors.elasticity import StiffnessTensor
|
|
103
|
+
>>> C = StiffnessTensor(matrix)
|
|
104
|
+
>>> print(C)
|
|
105
|
+
Stiffness tensor (in Voigt mapping):
|
|
106
|
+
[[200. 40. 40. 0. 0. 0.]
|
|
107
|
+
[ 40. 200. 40. 0. 0. 0.]
|
|
108
|
+
[ 40. 40. 200. 0. 0. 0.]
|
|
109
|
+
[ 0. 0. 0. 20. 0. 0.]
|
|
110
|
+
[ 0. 0. 0. 0. 20. 0.]
|
|
111
|
+
[ 0. 0. 0. 0. 0. 20.]]
|
|
112
|
+
|
|
113
|
+
Create a stiffness tensor from full (3,3,3,3) array:
|
|
114
|
+
|
|
115
|
+
>>> C_full = C.full_tensor # (3,3,3,3) numpy array
|
|
116
|
+
>>> print((type(C_full), C_full.shape))
|
|
117
|
+
(<class 'numpy.ndarray'>, (3, 3, 3, 3))
|
|
118
|
+
|
|
119
|
+
>>> StiffnessTensor(C_full)
|
|
120
|
+
Stiffness tensor (in Voigt mapping):
|
|
121
|
+
[[200. 40. 40. 0. 0. 0.]
|
|
122
|
+
[ 40. 200. 40. 0. 0. 0.]
|
|
123
|
+
[ 40. 40. 200. 0. 0. 0.]
|
|
124
|
+
[ 0. 0. 0. 20. 0. 0.]
|
|
125
|
+
[ 0. 0. 0. 0. 20. 0.]
|
|
126
|
+
[ 0. 0. 0. 0. 0. 20.]]
|
|
127
|
+
|
|
128
|
+
Create an array of stiffness tensors:
|
|
129
|
+
|
|
130
|
+
First, we create two slices of (6,6) matrices, corresponding to two stiffness values:
|
|
131
|
+
|
|
132
|
+
>>> slices = [[[200, 40, 40, 0, 0, 0 ],
|
|
133
|
+
... [40, 200, 40, 0, 0, 0 ],
|
|
134
|
+
... [40, 40, 200, 0, 0, 0 ],
|
|
135
|
+
... [0, 0, 0, 20, 0, 0 ],
|
|
136
|
+
... [0, 0, 0, 0, 20, 0 ],
|
|
137
|
+
... [0, 0, 0, 0, 0, 20]],
|
|
138
|
+
... [[250, 80, 80, 0, 0, 0 ],
|
|
139
|
+
... [80, 250, 80, 0, 0, 0 ],
|
|
140
|
+
... [80, 80, 250, 0, 0, 0 ],
|
|
141
|
+
... [0, 0, 0, 40, 0, 0 ],
|
|
142
|
+
... [0, 0, 0, 0, 40, 0 ],
|
|
143
|
+
... [0, 0, 0, 0, 0, 40]]]
|
|
144
|
+
|
|
145
|
+
Then, one can create an array of stiffness tensors:
|
|
146
|
+
|
|
147
|
+
>>> C_array=StiffnessTensor(slices)
|
|
148
|
+
>>> print(C_array)
|
|
149
|
+
Stiffness tensor (in Voigt mapping):
|
|
150
|
+
[[[200. 40. 40. 0. 0. 0.]
|
|
151
|
+
[ 40. 200. 40. 0. 0. 0.]
|
|
152
|
+
[ 40. 40. 200. 0. 0. 0.]
|
|
153
|
+
[ 0. 0. 0. 20. 0. 0.]
|
|
154
|
+
[ 0. 0. 0. 0. 20. 0.]
|
|
155
|
+
[ 0. 0. 0. 0. 0. 20.]]
|
|
156
|
+
<BLANKLINE>
|
|
157
|
+
[[250. 80. 80. 0. 0. 0.]
|
|
158
|
+
[ 80. 250. 80. 0. 0. 0.]
|
|
159
|
+
[ 80. 80. 250. 0. 0. 0.]
|
|
160
|
+
[ 0. 0. 0. 40. 0. 0.]
|
|
161
|
+
[ 0. 0. 0. 0. 40. 0.]
|
|
162
|
+
[ 0. 0. 0. 0. 0. 40.]]]
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
This array can be subindexed. E.g.:
|
|
166
|
+
|
|
167
|
+
>>> C_array[0]
|
|
168
|
+
Stiffness tensor (in Voigt mapping):
|
|
169
|
+
[[200. 40. 40. 0. 0. 0.]
|
|
170
|
+
[ 40. 200. 40. 0. 0. 0.]
|
|
171
|
+
[ 40. 40. 200. 0. 0. 0.]
|
|
172
|
+
[ 0. 0. 0. 20. 0. 0.]
|
|
173
|
+
[ 0. 0. 0. 0. 20. 0.]
|
|
174
|
+
[ 0. 0. 0. 0. 0. 20.]]
|
|
83
175
|
"""
|
|
84
176
|
super().__init__(M, mapping=mapping, **kwargs)
|
|
85
177
|
if check_positive_definite:
|
|
86
178
|
_check_definite_positive(self._matrix)
|
|
87
|
-
self.symmetry = symmetry
|
|
88
179
|
self.phase_name = phase_name
|
|
89
180
|
|
|
90
181
|
def __mul__(self, other):
|
|
@@ -100,7 +191,6 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
100
191
|
string = super().__repr__()
|
|
101
192
|
if self.phase_name is not None:
|
|
102
193
|
string += '\nPhase: {}'.format(self.phase_name)
|
|
103
|
-
string += '\nSymmetry: {}'.format(self.symmetry)
|
|
104
194
|
return string
|
|
105
195
|
|
|
106
196
|
def inv(self):
|
|
@@ -111,9 +201,82 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
111
201
|
-------
|
|
112
202
|
ComplianceTensor
|
|
113
203
|
Reciprocal tensor
|
|
204
|
+
|
|
205
|
+
Examples
|
|
206
|
+
--------
|
|
207
|
+
Create a stiffness tensor for cubic symmetry:
|
|
208
|
+
|
|
209
|
+
>>> C = StiffnessTensor.cubic(C11=186, C12=134, C44=77)
|
|
210
|
+
>>> print(C)
|
|
211
|
+
Stiffness tensor (in Voigt mapping):
|
|
212
|
+
[[186. 134. 134. 0. 0. 0.]
|
|
213
|
+
[134. 186. 134. 0. 0. 0.]
|
|
214
|
+
[134. 134. 186. 0. 0. 0.]
|
|
215
|
+
[ 0. 0. 0. 77. 0. 0.]
|
|
216
|
+
[ 0. 0. 0. 0. 77. 0.]
|
|
217
|
+
[ 0. 0. 0. 0. 0. 77.]]
|
|
218
|
+
|
|
219
|
+
The reciprocal (compliance) tensor is given by:
|
|
220
|
+
|
|
221
|
+
>>> S = C.inv()
|
|
222
|
+
>>> print(S)
|
|
223
|
+
Compliance tensor (in Voigt mapping):
|
|
224
|
+
[[ 0.01355473 -0.00567604 -0.00567604 0. 0. 0. ]
|
|
225
|
+
[-0.00567604 0.01355473 -0.00567604 0. 0. 0. ]
|
|
226
|
+
[-0.00567604 -0.00567604 0.01355473 0. 0. 0. ]
|
|
227
|
+
[ 0. 0. 0. 0.01298701 0. 0. ]
|
|
228
|
+
[ 0. 0. 0. 0. 0.01298701 0. ]
|
|
229
|
+
[ 0. 0. 0. 0. 0. 0.01298701]]
|
|
230
|
+
|
|
231
|
+
This also works for tensor arrays. In this case, the returned value will be a compliance tensor array of the
|
|
232
|
+
same shape as the tensor array. For instance:
|
|
233
|
+
|
|
234
|
+
>>> slices = [[[200, 40, 40, 0, 0, 0 ],
|
|
235
|
+
... [40, 200, 40, 0, 0, 0 ],
|
|
236
|
+
... [40, 40, 200, 0, 0, 0 ],
|
|
237
|
+
... [0, 0, 0, 20, 0, 0 ],
|
|
238
|
+
... [0, 0, 0, 0, 20, 0 ],
|
|
239
|
+
... [0, 0, 0, 0, 0, 20]],
|
|
240
|
+
... [[250, 80, 80, 0, 0, 0 ],
|
|
241
|
+
... [80, 250, 80, 0, 0, 0 ],
|
|
242
|
+
... [80, 80, 250, 0, 0, 0 ],
|
|
243
|
+
... [0, 0, 0, 40, 0, 0 ],
|
|
244
|
+
... [0, 0, 0, 0, 40, 0 ],
|
|
245
|
+
... [0, 0, 0, 0, 0, 40]]]
|
|
246
|
+
>>> C = StiffnessTensor(slices)
|
|
247
|
+
>>> S = C.inv()
|
|
248
|
+
>>> print(S)
|
|
249
|
+
Compliance tensor (in Voigt mapping):
|
|
250
|
+
[[[ 0.00535714 -0.00089286 -0.00089286 0. 0.
|
|
251
|
+
0. ]
|
|
252
|
+
[-0.00089286 0.00535714 -0.00089286 0. 0.
|
|
253
|
+
0. ]
|
|
254
|
+
[-0.00089286 -0.00089286 0.00535714 0. 0.
|
|
255
|
+
0. ]
|
|
256
|
+
[ 0. 0. 0. 0.05 0.
|
|
257
|
+
0. ]
|
|
258
|
+
[ 0. 0. 0. 0. 0.05
|
|
259
|
+
0. ]
|
|
260
|
+
[ 0. 0. 0. 0. 0.
|
|
261
|
+
0.05 ]]
|
|
262
|
+
<BLANKLINE>
|
|
263
|
+
[[ 0.00473458 -0.00114778 -0.00114778 0. 0.
|
|
264
|
+
0. ]
|
|
265
|
+
[-0.00114778 0.00473458 -0.00114778 0. 0.
|
|
266
|
+
0. ]
|
|
267
|
+
[-0.00114778 -0.00114778 0.00473458 0. 0.
|
|
268
|
+
0. ]
|
|
269
|
+
[ 0. 0. 0. 0.025 0.
|
|
270
|
+
0. ]
|
|
271
|
+
[ 0. 0. 0. 0. 0.025
|
|
272
|
+
0. ]
|
|
273
|
+
[ 0. 0. 0. 0. 0.
|
|
274
|
+
0.025 ]]]
|
|
114
275
|
"""
|
|
115
276
|
C = np.linalg.inv(self._matrix)
|
|
116
|
-
|
|
277
|
+
t2 = ComplianceTensor(C, mapping=kelvin_mapping, phase_name=self.phase_name)
|
|
278
|
+
t2.mapping = self.mapping.mapping_inverse
|
|
279
|
+
return t2
|
|
117
280
|
|
|
118
281
|
@classmethod
|
|
119
282
|
def from_txt_file(cls, filename):
|
|
@@ -150,16 +313,11 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
150
313
|
phase_name = lines[0].split(": ", 1)[1].strip()
|
|
151
314
|
matrix_start_index += 1
|
|
152
315
|
|
|
153
|
-
# Parse symmetry if available
|
|
154
|
-
if len(lines) > matrix_start_index and lines[matrix_start_index].startswith("Symmetry:"):
|
|
155
|
-
symmetry = lines[matrix_start_index].split(": ", 1)[1].strip()
|
|
156
|
-
matrix_start_index += 1
|
|
157
|
-
|
|
158
316
|
# Parse matrix
|
|
159
317
|
matrix = np.loadtxt(lines[matrix_start_index:])
|
|
160
318
|
|
|
161
319
|
# Return the reconstructed object
|
|
162
|
-
return cls(matrix, phase_name=phase_name
|
|
320
|
+
return cls(matrix, phase_name=phase_name)
|
|
163
321
|
|
|
164
322
|
def save_to_txt(self, filename, matrix_only=False):
|
|
165
323
|
"""
|
|
@@ -170,7 +328,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
170
328
|
filename : str
|
|
171
329
|
Filename to save the tensor to.
|
|
172
330
|
matrix_only : bool, False
|
|
173
|
-
If true, only the components of
|
|
331
|
+
If true, only the components of the stiffness tensor is saved (no data about phase nor symmetry)
|
|
174
332
|
|
|
175
333
|
See Also
|
|
176
334
|
--------
|
|
@@ -181,8 +339,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
181
339
|
if not matrix_only:
|
|
182
340
|
if self.phase_name is not None:
|
|
183
341
|
f.write(f"Phase Name: {self.phase_name}\n")
|
|
184
|
-
|
|
185
|
-
for row in self._matrix:
|
|
342
|
+
for row in self.matrix():
|
|
186
343
|
f.write(" " + " ".join(f"{value:8.2f}" for value in row) + "\n")
|
|
187
344
|
|
|
188
345
|
@classmethod
|
|
@@ -272,11 +429,11 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
272
429
|
|
|
273
430
|
Notes
|
|
274
431
|
-----
|
|
275
|
-
The relationships between the tensor's components depend on the crystallogrpahic symmetry [
|
|
432
|
+
The relationships between the tensor's components depend on the crystallogrpahic symmetry [Nye]_.
|
|
276
433
|
|
|
277
434
|
References
|
|
278
435
|
----------
|
|
279
|
-
.. [
|
|
436
|
+
.. [Nye] Nye, J. F. Physical Properties of Crystals. London: Oxford University Press, 1959.
|
|
280
437
|
|
|
281
438
|
Examples
|
|
282
439
|
--------
|
|
@@ -294,7 +451,6 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
294
451
|
[-18. 1. -3. 0. 11. 0.]
|
|
295
452
|
[ 0. 0. 0. 3. 0. 85.]]
|
|
296
453
|
Phase: TiNi
|
|
297
|
-
Symmetry: monoclinic
|
|
298
454
|
|
|
299
455
|
>>> from Elasticipy.tensors.elasticity import ComplianceTensor\n
|
|
300
456
|
>>> ComplianceTensor.fromCrystalSymmetry(symmetry='monoclinic', diad='y', phase_name='TiNi',
|
|
@@ -310,7 +466,6 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
310
466
|
[ 14. -8. 0. 0. 116. 0.]
|
|
311
467
|
[ 0. 0. 0. 0. 0. 12.]]
|
|
312
468
|
Phase: TiNi
|
|
313
|
-
Symmetry: monoclinic
|
|
314
469
|
"""
|
|
315
470
|
warn('This function will be removed in a future release. Use {}.{}() instead'.format(cls.__name__,symmetry), DeprecationWarning, stacklevel=2)
|
|
316
471
|
return cls._fromCrystalSymmetry(symmetry=symmetry, point_group=point_group, diad=diad, phase_name=phase_name,
|
|
@@ -319,7 +474,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
319
474
|
@classmethod
|
|
320
475
|
def _fromCrystalSymmetry(cls, symmetry, phase_name, **kwargs):
|
|
321
476
|
matrix = cls._matrixFromCrystalSymmetry(symmetry=symmetry, **kwargs)
|
|
322
|
-
return cls(matrix, phase_name=phase_name
|
|
477
|
+
return cls(matrix, phase_name=phase_name)
|
|
323
478
|
|
|
324
479
|
|
|
325
480
|
@classmethod
|
|
@@ -541,12 +696,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
541
696
|
[C16, C26, C36, C46, C56, C66]])
|
|
542
697
|
return cls(matrix, phase_name=phase_name)
|
|
543
698
|
|
|
544
|
-
|
|
545
|
-
if self.ndim:
|
|
546
|
-
err_msg = fun_name + ' is not suitable for tensor array. Consider subscripting (e.g. C[0].{}).'.format(fun_name)
|
|
547
|
-
raise ValueError(err_msg)
|
|
548
|
-
|
|
549
|
-
@property
|
|
699
|
+
@_elementwise_property
|
|
550
700
|
def Young_modulus(self):
|
|
551
701
|
"""
|
|
552
702
|
Directional Young's modulus
|
|
@@ -556,18 +706,17 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
556
706
|
SphericalFunction
|
|
557
707
|
Young's modulus
|
|
558
708
|
"""
|
|
559
|
-
self._single_tensor_only('Young_modulus')
|
|
560
709
|
if isinstance(self, ComplianceTensor):
|
|
561
710
|
S = self
|
|
562
711
|
else:
|
|
563
712
|
S = self.inv()
|
|
564
713
|
def compute_young_modulus(u):
|
|
565
|
-
a = np.einsum('ijkl,...i,...j,...k,...l->...', S.full_tensor
|
|
714
|
+
a = np.einsum('ijkl,...i,...j,...k,...l->...', S.full_tensor, u, u, u, u)
|
|
566
715
|
return 1 / a
|
|
567
716
|
|
|
568
717
|
return SphericalFunction(compute_young_modulus)
|
|
569
718
|
|
|
570
|
-
@
|
|
719
|
+
@_elementwise_property
|
|
571
720
|
def shear_modulus(self):
|
|
572
721
|
"""
|
|
573
722
|
Directional shear modulus
|
|
@@ -577,18 +726,17 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
577
726
|
HyperSphericalFunction
|
|
578
727
|
Shear modulus
|
|
579
728
|
"""
|
|
580
|
-
self._single_tensor_only('shear_modulus')
|
|
581
729
|
if isinstance(self, ComplianceTensor):
|
|
582
730
|
S = self
|
|
583
731
|
else:
|
|
584
732
|
S = self.inv()
|
|
585
733
|
def compute_shear_modulus(u, v):
|
|
586
|
-
G = 0.25/np.einsum('ijkl,...i,...j,...k,...l->...',S.full_tensor
|
|
734
|
+
G = 0.25/np.einsum('ijkl,...i,...j,...k,...l->...',S.full_tensor,u,v,u,v)
|
|
587
735
|
return G
|
|
588
736
|
|
|
589
737
|
return HyperSphericalFunction(compute_shear_modulus)
|
|
590
738
|
|
|
591
|
-
@
|
|
739
|
+
@_elementwise_property
|
|
592
740
|
def Poisson_ratio(self):
|
|
593
741
|
"""
|
|
594
742
|
Directional Poisson's ratio
|
|
@@ -609,11 +757,10 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
609
757
|
|
|
610
758
|
where :math:`\\varepsilon_{jj}` denotes the (compressive) longitudinal strain along the j-th direction.
|
|
611
759
|
"""
|
|
612
|
-
self._single_tensor_only('Poisson_ratio')
|
|
613
760
|
if isinstance(self, ComplianceTensor):
|
|
614
|
-
Sfull = self.full_tensor
|
|
761
|
+
Sfull = self.full_tensor
|
|
615
762
|
else:
|
|
616
|
-
Sfull = self.inv().full_tensor
|
|
763
|
+
Sfull = self.inv().full_tensor
|
|
617
764
|
def compute_PoissonRatio(u, v):
|
|
618
765
|
numer = np.einsum('ijkl,...i,...j,...k,...l->...',Sfull,v,v,u,u)
|
|
619
766
|
denom = np.einsum('ijkl,...i,...j,...k,...l->...',Sfull,u,u,u,u)
|
|
@@ -621,7 +768,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
621
768
|
|
|
622
769
|
return HyperSphericalFunction(compute_PoissonRatio)
|
|
623
770
|
|
|
624
|
-
@
|
|
771
|
+
@_elementwise_property
|
|
625
772
|
def linear_compressibility(self):
|
|
626
773
|
"""
|
|
627
774
|
Compute the directional linear compressibility.
|
|
@@ -635,13 +782,12 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
635
782
|
--------
|
|
636
783
|
bulk_modulus : bulk modulus of the material
|
|
637
784
|
"""
|
|
638
|
-
self._single_tensor_only('linear_compressibility')
|
|
639
785
|
if isinstance(self, ComplianceTensor):
|
|
640
786
|
S = self
|
|
641
787
|
else:
|
|
642
788
|
S = self.inv()
|
|
643
789
|
def compute_linear_compressibility(u):
|
|
644
|
-
return np.einsum('ijkk,...i,...j->...',S.full_tensor
|
|
790
|
+
return np.einsum('ijkk,...i,...j->...',S.full_tensor,u,u)
|
|
645
791
|
|
|
646
792
|
return SphericalFunction(compute_linear_compressibility)
|
|
647
793
|
|
|
@@ -661,9 +807,9 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
661
807
|
"""
|
|
662
808
|
return self.inv().bulk_modulus
|
|
663
809
|
|
|
664
|
-
@
|
|
810
|
+
@_elementwise_property
|
|
665
811
|
def lame1(self):
|
|
666
|
-
"""
|
|
812
|
+
"""
|
|
667
813
|
Compute the first Lamé's parameter (only for isotropic materials).
|
|
668
814
|
|
|
669
815
|
If the stiffness/compliance tensor is not isotropic, NaN is returned.
|
|
@@ -677,16 +823,15 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
677
823
|
--------
|
|
678
824
|
lame2 : second Lamé's parameter
|
|
679
825
|
"""
|
|
680
|
-
self._single_tensor_only('lame1')
|
|
681
826
|
if self.is_isotropic():
|
|
682
827
|
C11 = (self.C11 + self.C22 + self.C33) / 3
|
|
683
828
|
return C11 - 2 * self.lame2
|
|
684
829
|
else:
|
|
685
830
|
return np.nan
|
|
686
831
|
|
|
687
|
-
@
|
|
832
|
+
@_elementwise_property
|
|
688
833
|
def lame2(self):
|
|
689
|
-
"""
|
|
834
|
+
"""
|
|
690
835
|
Compute the second Lamé's parameter (only for isotropic materials).
|
|
691
836
|
|
|
692
837
|
If the stiffness/compliance tensor is not isotropic, NaN is returned.
|
|
@@ -700,7 +845,6 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
700
845
|
--------
|
|
701
846
|
lame1 : first Lamé's parameter
|
|
702
847
|
"""
|
|
703
|
-
self._single_tensor_only('lame2')
|
|
704
848
|
if self.is_isotropic():
|
|
705
849
|
return (self.C44 + self.C55 + self.C66) / 3
|
|
706
850
|
else:
|
|
@@ -708,10 +852,10 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
708
852
|
|
|
709
853
|
def Voigt_average(self, axis=None):
|
|
710
854
|
"""
|
|
711
|
-
Compute the Voigt average
|
|
855
|
+
Compute the Voigt average (from the mean of stiffness tensors).
|
|
712
856
|
|
|
713
|
-
If the
|
|
714
|
-
isotropic
|
|
857
|
+
If the object is a single tensor, the returned tensor corresponds to the average over an infinite set of random
|
|
858
|
+
rotations, resulting in an isotropic behavior. Otherwise, the mean is computed over the given axis.
|
|
715
859
|
|
|
716
860
|
Parameters
|
|
717
861
|
----------
|
|
@@ -728,24 +872,67 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
728
872
|
Reuss_average : compute the Reuss average
|
|
729
873
|
Hill_average : compute the Voigt-Reuss-Hill average
|
|
730
874
|
average : generic function for calling either the Voigt, Reuss or Hill average
|
|
875
|
+
|
|
876
|
+
Examples
|
|
877
|
+
--------
|
|
878
|
+
Let us consider the stiffness of pure copper:
|
|
879
|
+
|
|
880
|
+
>>> from Elasticipy.tensors.elasticity import StiffnessTensor
|
|
881
|
+
>>> C = StiffnessTensor.cubic(C11=186, C12=134, C44=77)
|
|
882
|
+
|
|
883
|
+
If we assume that an aggregate is composed of an infinite set grains whose orientations are uniformly
|
|
884
|
+
distributed, the Voigt average is:
|
|
885
|
+
|
|
886
|
+
>>> C.Voigt_average()
|
|
887
|
+
Stiffness tensor (in Voigt mapping):
|
|
888
|
+
[[226.8 113.6 113.6 0. 0. 0. ]
|
|
889
|
+
[113.6 226.8 113.6 0. 0. 0. ]
|
|
890
|
+
[113.6 113.6 226.8 0. 0. 0. ]
|
|
891
|
+
[ 0. 0. 0. 56.6 0. 0. ]
|
|
892
|
+
[ 0. 0. 0. 0. 56.6 0. ]
|
|
893
|
+
[ 0. 0. 0. 0. 0. 56.6]]
|
|
894
|
+
|
|
895
|
+
Now, we consider an aggregate of grains whose orientations follow a pure fibre texture along the X axis. First,
|
|
896
|
+
generate a (large) random set of rotation corresponding to this texture:
|
|
897
|
+
|
|
898
|
+
>>> from scipy.spatial.transform import Rotation
|
|
899
|
+
>>> import numpy as np
|
|
900
|
+
>>> g = Rotation.from_euler('X', np.linspace(0, 90, 1000), degrees=True) # 1000 rotations around X
|
|
901
|
+
|
|
902
|
+
Now apply rotations to compute the array of stiffness tensors:
|
|
903
|
+
|
|
904
|
+
>>> C_rotated = C * g
|
|
905
|
+
>>> C_rotated
|
|
906
|
+
Stiffness tensor array of shape (1000,)
|
|
907
|
+
|
|
908
|
+
Finally, one can check that the Voigt average, computed from the rotated stiffness tensors is:
|
|
909
|
+
|
|
910
|
+
>>> C_rotated.Voigt_average()
|
|
911
|
+
Stiffness tensor (in Voigt mapping):
|
|
912
|
+
[[ 1.86000000e+02 1.34000000e+02 1.34000000e+02 -4.39648318e-17
|
|
913
|
+
0.00000000e+00 0.00000000e+00]
|
|
914
|
+
[ 1.34000000e+02 2.11474500e+02 1.08525500e+02 0.00000000e+00
|
|
915
|
+
0.00000000e+00 0.00000000e+00]
|
|
916
|
+
[ 1.34000000e+02 1.08525500e+02 2.11474500e+02 0.00000000e+00
|
|
917
|
+
0.00000000e+00 0.00000000e+00]
|
|
918
|
+
[ 0.00000000e+00 0.00000000e+00 0.00000000e+00 5.15255000e+01
|
|
919
|
+
0.00000000e+00 0.00000000e+00]
|
|
920
|
+
[ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
|
|
921
|
+
7.70000000e+01 -7.54674101e-17]
|
|
922
|
+
[ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
|
|
923
|
+
7.54674101e-17 7.70000000e+01]]
|
|
731
924
|
"""
|
|
732
925
|
if self.ndim:
|
|
733
926
|
return self.mean(axis=axis)
|
|
734
927
|
else:
|
|
735
|
-
|
|
736
|
-
A = c[0, 0] + c[1, 1] + c[2, 2]
|
|
737
|
-
B = c[0, 1] + c[0, 2] + c[1, 2]
|
|
738
|
-
C = c[3, 3] + c[4, 4] + c[5, 5]
|
|
739
|
-
C11 = 1 / 5 * A + 2 / 15 * B + 4 / 15 * C
|
|
740
|
-
C12 = 1 / 15 * A + 4 / 15 * B - 2 / 15 * C
|
|
741
|
-
C44 = (A - B) / 15 + C / 5
|
|
742
|
-
mat = _isotropic_matrix(C11, C12, C44)
|
|
743
|
-
return StiffnessTensor(mat, symmetry='isotropic', phase_name=self.phase_name)
|
|
928
|
+
return self.infinite_random_average()
|
|
744
929
|
|
|
745
930
|
def Reuss_average(self, axis=None):
|
|
746
931
|
"""
|
|
747
|
-
Compute the Reuss average
|
|
748
|
-
|
|
932
|
+
Compute the Reuss average (from the mean of compliance tensors).
|
|
933
|
+
|
|
934
|
+
If the object is a single tensor, the returned tensor corresponds to the average over an infinite set of random
|
|
935
|
+
rotations, resulting in an isotropic behavior. Otherwise, the mean is computed over the given axis.
|
|
749
936
|
|
|
750
937
|
Parameters
|
|
751
938
|
----------
|
|
@@ -762,13 +949,70 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
762
949
|
Voigt_average : compute the Voigt average
|
|
763
950
|
Hill_average : compute the Voigt-Reuss-Hill average
|
|
764
951
|
average : generic function for calling either the Voigt, Reuss or Hill average
|
|
952
|
+
|
|
953
|
+
Examples
|
|
954
|
+
--------
|
|
955
|
+
Let us consider the stiffness of pure copper:
|
|
956
|
+
|
|
957
|
+
>>> from Elasticipy.tensors.elasticity import StiffnessTensor
|
|
958
|
+
>>> C = StiffnessTensor.cubic(C11=186, C12=134, C44=77)
|
|
959
|
+
|
|
960
|
+
If we assume that an aggregate is composed of an infinite set grains whose orientations are uniformly
|
|
961
|
+
distributed, the Voigt average is:
|
|
962
|
+
|
|
963
|
+
>>> C.Reuss_average()
|
|
964
|
+
Stiffness tensor (in Voigt mapping):
|
|
965
|
+
[[208.86206897 122.56896552 122.56896552 0. 0.
|
|
966
|
+
0. ]
|
|
967
|
+
[122.56896552 208.86206897 122.56896552 0. 0.
|
|
968
|
+
0. ]
|
|
969
|
+
[122.56896552 122.56896552 208.86206897 0. 0.
|
|
970
|
+
0. ]
|
|
971
|
+
[ 0. 0. 0. 43.14655172 0.
|
|
972
|
+
0. ]
|
|
973
|
+
[ 0. 0. 0. 0. 43.14655172
|
|
974
|
+
0. ]
|
|
975
|
+
[ 0. 0. 0. 0. 0.
|
|
976
|
+
43.14655172]]
|
|
977
|
+
|
|
978
|
+
Now, we consider an aggregate of grains whose orientations follow a pure fibre texture along the X axis. First,
|
|
979
|
+
generate a (large) random set of rotation corresponding to this texture:
|
|
980
|
+
|
|
981
|
+
>>> from scipy.spatial.transform import Rotation
|
|
982
|
+
>>> import numpy as np
|
|
983
|
+
>>> g = Rotation.from_euler('X', np.linspace(0, 90, 1000), degrees=True) # 1000 rotations around X
|
|
984
|
+
|
|
985
|
+
Now apply rotations to compute the array of stiffness tensors:
|
|
986
|
+
|
|
987
|
+
>>> C_rotated = C * g
|
|
988
|
+
>>> C_rotated
|
|
989
|
+
Stiffness tensor array of shape (1000,)
|
|
990
|
+
|
|
991
|
+
Finally, one can check that the Voigt average, computed from the rotated stiffness tensors is:
|
|
992
|
+
|
|
993
|
+
>>> C_rotated.Reuss_average()
|
|
994
|
+
Stiffness tensor (in Voigt mapping):
|
|
995
|
+
[[ 1.86000000e+02 1.34000000e+02 1.34000000e+02 -1.68008952e-15
|
|
996
|
+
0.00000000e+00 0.00000000e+00]
|
|
997
|
+
[ 1.34000000e+02 1.98854548e+02 1.21145452e+02 -1.09777845e-15
|
|
998
|
+
0.00000000e+00 0.00000000e+00]
|
|
999
|
+
[ 1.34000000e+02 1.21145452e+02 1.98854548e+02 -2.89552069e-15
|
|
1000
|
+
0.00000000e+00 0.00000000e+00]
|
|
1001
|
+
[-1.61936693e-16 1.69241946e-16 -7.96730255e-16 3.88930441e+01
|
|
1002
|
+
0.00000000e+00 0.00000000e+00]
|
|
1003
|
+
[ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
|
|
1004
|
+
7.70000000e+01 -7.54674101e-17]
|
|
1005
|
+
[ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
|
|
1006
|
+
7.54674101e-17 7.70000000e+01]]
|
|
765
1007
|
"""
|
|
766
1008
|
return self.inv().Reuss_average(axis=axis).inv()
|
|
767
1009
|
|
|
768
1010
|
def Hill_average(self, axis=None):
|
|
769
1011
|
"""
|
|
770
|
-
Compute the
|
|
771
|
-
|
|
1012
|
+
Compute the Voigt-Reuss-Hill average (mean of Voigt and Reuss averages for stiffness tensors).
|
|
1013
|
+
|
|
1014
|
+
If the object is a single tensor, the returned tensor corresponds to the average over an infinite set of random
|
|
1015
|
+
rotations, resulting in an isotropic behavior. Otherwise, the mean is computed over the given axis.
|
|
772
1016
|
|
|
773
1017
|
Parameters
|
|
774
1018
|
----------
|
|
@@ -785,6 +1029,61 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
785
1029
|
Voigt_average : compute the Voigt average
|
|
786
1030
|
Reuss_average : compute the Reuss average
|
|
787
1031
|
average : generic function for calling either the Voigt, Reuss or Hill average
|
|
1032
|
+
|
|
1033
|
+
Examples
|
|
1034
|
+
--------
|
|
1035
|
+
Let us consider the stiffness of pure copper:
|
|
1036
|
+
|
|
1037
|
+
>>> from Elasticipy.tensors.elasticity import StiffnessTensor
|
|
1038
|
+
>>> C = StiffnessTensor.cubic(C11=186, C12=134, C44=77)
|
|
1039
|
+
|
|
1040
|
+
If we assume that an aggregate is composed of an infinite set grains whose orientations are uniformly
|
|
1041
|
+
distributed, the Voigt average is:
|
|
1042
|
+
|
|
1043
|
+
>>> C.Hill_average()
|
|
1044
|
+
Stiffness tensor (in Voigt mapping):
|
|
1045
|
+
[[217.83103448 118.08448276 118.08448276 0. 0.
|
|
1046
|
+
0. ]
|
|
1047
|
+
[118.08448276 217.83103448 118.08448276 0. 0.
|
|
1048
|
+
0. ]
|
|
1049
|
+
[118.08448276 118.08448276 217.83103448 0. 0.
|
|
1050
|
+
0. ]
|
|
1051
|
+
[ 0. 0. 0. 49.87327586 0.
|
|
1052
|
+
0. ]
|
|
1053
|
+
[ 0. 0. 0. 0. 49.87327586
|
|
1054
|
+
0. ]
|
|
1055
|
+
[ 0. 0. 0. 0. 0.
|
|
1056
|
+
49.87327586]]
|
|
1057
|
+
|
|
1058
|
+
Now, we consider an aggregate of grains whose orientations follow a pure fibre texture along the X axis. First,
|
|
1059
|
+
generate a (large) random set of rotation corresponding to this texture:
|
|
1060
|
+
|
|
1061
|
+
>>> from scipy.spatial.transform import Rotation
|
|
1062
|
+
>>> import numpy as np
|
|
1063
|
+
>>> g = Rotation.from_euler('X', np.linspace(0, 90, 1000), degrees=True) # 1000 rotations around X
|
|
1064
|
+
|
|
1065
|
+
Now apply rotations to compute the array of stiffness tensors:
|
|
1066
|
+
|
|
1067
|
+
>>> C_rotated = C * g
|
|
1068
|
+
>>> C_rotated
|
|
1069
|
+
Stiffness tensor array of shape (1000,)
|
|
1070
|
+
|
|
1071
|
+
Finally, one can check that the Voigt average, computed from the rotated stiffness tensors is:
|
|
1072
|
+
|
|
1073
|
+
>>> C_rotated.Hill_average()
|
|
1074
|
+
Stiffness tensor (in Voigt mapping):
|
|
1075
|
+
[[ 1.86000000e+02 1.34000000e+02 1.34000000e+02 -8.62027175e-16
|
|
1076
|
+
0.00000000e+00 0.00000000e+00]
|
|
1077
|
+
[ 1.34000000e+02 2.05164524e+02 1.14835476e+02 -5.48889226e-16
|
|
1078
|
+
0.00000000e+00 0.00000000e+00]
|
|
1079
|
+
[ 1.34000000e+02 1.14835476e+02 2.05164524e+02 -1.44776034e-15
|
|
1080
|
+
0.00000000e+00 0.00000000e+00]
|
|
1081
|
+
[-8.09683464e-17 8.46209730e-17 -3.98365128e-16 4.52092721e+01
|
|
1082
|
+
0.00000000e+00 0.00000000e+00]
|
|
1083
|
+
[ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
|
|
1084
|
+
7.70000000e+01 -7.54674101e-17]
|
|
1085
|
+
[ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
|
|
1086
|
+
7.54674101e-17 7.70000000e+01]]
|
|
788
1087
|
"""
|
|
789
1088
|
Reuss = self.Reuss_average(axis=axis)
|
|
790
1089
|
Voigt = self.Voigt_average(axis=axis)
|
|
@@ -823,22 +1122,25 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
823
1122
|
@classmethod
|
|
824
1123
|
def isotropic(cls, E=None, nu=None, G=None, lame1=None, lame2=None, K=None, phase_name=None):
|
|
825
1124
|
"""
|
|
826
|
-
Create an isotropic stiffness tensor
|
|
827
|
-
|
|
1125
|
+
Create an isotropic stiffness tensor.
|
|
1126
|
+
|
|
1127
|
+
The stiffness tensor must be constructed from exactly two elasticity coefficients, namely: E, nu, G, lame1, or
|
|
1128
|
+
lame2. Note that lame2 is just an alias for G. Each of these coefficient can be a list; in this case, the
|
|
1129
|
+
returned object is a tensor array (see examples).
|
|
828
1130
|
|
|
829
1131
|
Parameters
|
|
830
1132
|
----------
|
|
831
|
-
E : float, None
|
|
1133
|
+
E : float or list, None
|
|
832
1134
|
Young modulus
|
|
833
|
-
nu : float, None
|
|
1135
|
+
nu : float or list, None
|
|
834
1136
|
Poisson ratio
|
|
835
|
-
G : float, None
|
|
1137
|
+
G : float or list, None
|
|
836
1138
|
Shear modulus
|
|
837
|
-
lame1 : float, None
|
|
1139
|
+
lame1 : float or list, None
|
|
838
1140
|
First Lamé coefficient
|
|
839
|
-
lame2 : float, None
|
|
1141
|
+
lame2 : float or list, None
|
|
840
1142
|
Second Lamé coefficient (alias for G)
|
|
841
|
-
K : float, None
|
|
1143
|
+
K : float or list, None
|
|
842
1144
|
Bulk modulus
|
|
843
1145
|
phase_name : str, None
|
|
844
1146
|
Name to print
|
|
@@ -866,6 +1168,30 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
866
1168
|
>>> C.shear_modulus
|
|
867
1169
|
Hyperspherical function
|
|
868
1170
|
Min=82031.24999999991, Max=82031.25000000006
|
|
1171
|
+
|
|
1172
|
+
Similarly, the same tensor can be defined from another pair of parameters, e.g. Young and shear moduli:
|
|
1173
|
+
|
|
1174
|
+
>>> C=StiffnessTensor.isotropic(E=210e3, G=82031)
|
|
1175
|
+
>>> C.Young_modulus
|
|
1176
|
+
Spherical function
|
|
1177
|
+
Min=210000.0000000001, Max=210000.00000000032
|
|
1178
|
+
|
|
1179
|
+
One can build a tensor array by providing a list of values for the input arguments, instead of floats. For
|
|
1180
|
+
instance:
|
|
1181
|
+
|
|
1182
|
+
>>> C = StiffnessTensor.isotropic(E=(210, 70), nu=(0.28, 0.35)) # Elastic moduli for steel and aluminium
|
|
1183
|
+
>>> C.shape
|
|
1184
|
+
(2,)
|
|
1185
|
+
|
|
1186
|
+
We can easily check that the shear moduli for steel and aluminium are:
|
|
1187
|
+
|
|
1188
|
+
>>> C[0].shear_modulus
|
|
1189
|
+
Hyperspherical function
|
|
1190
|
+
Min=82.0312499999999, Max=82.03125000000006
|
|
1191
|
+
|
|
1192
|
+
>>> C[1].shear_modulus
|
|
1193
|
+
Hyperspherical function
|
|
1194
|
+
Min=25.925925925925895, Max=25.925925925925952
|
|
869
1195
|
"""
|
|
870
1196
|
return ComplianceTensor.isotropic(E=E, nu=nu, G=G, lame1=lame1, lame2=lame2, K=K, phase_name=phase_name).inv()
|
|
871
1197
|
|
|
@@ -997,8 +1323,8 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
997
1323
|
|
|
998
1324
|
Returns
|
|
999
1325
|
-------
|
|
1000
|
-
|
|
1001
|
-
|
|
1326
|
+
SymmetricSecondOrderTensor
|
|
1327
|
+
Christoffel tensor(s). if u is a list of directions, Gamma[i] is the Christoffel tensor for
|
|
1002
1328
|
direction u[i].
|
|
1003
1329
|
|
|
1004
1330
|
See Also
|
|
@@ -1007,14 +1333,24 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1007
1333
|
|
|
1008
1334
|
Notes
|
|
1009
1335
|
-----
|
|
1010
|
-
For a given stiffness tensor **C** and a given unit vector **u**, the Christoffel tensor is defined as [
|
|
1336
|
+
For a given stiffness tensor **C** and a given unit vector **u**, the Christoffel tensor is defined as [Jaeken]_ :
|
|
1011
1337
|
|
|
1012
1338
|
.. math:: M_{ij} = C_{iklj}.u_k.u_l
|
|
1013
1339
|
|
|
1340
|
+
Examples
|
|
1341
|
+
--------
|
|
1342
|
+
>>> from Elasticipy.tensors.elasticity import StiffnessTensor
|
|
1343
|
+
>>> C = StiffnessTensor.cubic(C11=186, C12=134, C44=77) # Cubic Cu
|
|
1344
|
+
>>> C.Christoffel_tensor([0, 0, 1])
|
|
1345
|
+
Symmetric second-order tensor
|
|
1346
|
+
[[ 77. 0. 0.]
|
|
1347
|
+
[ 0. 77. 0.]
|
|
1348
|
+
[ 0. 0. 186.]]
|
|
1014
1349
|
"""
|
|
1015
1350
|
u_vec = np.atleast_2d(u)
|
|
1016
1351
|
u_vec = (u_vec.T / np.linalg.norm(u_vec, axis=1)).T
|
|
1017
|
-
|
|
1352
|
+
G = np.einsum('inmj,pn,pm->pij', self.full_tensor, u_vec, u_vec)
|
|
1353
|
+
return SymmetricSecondOrderTensor(np.squeeze(G))
|
|
1018
1354
|
|
|
1019
1355
|
def wave_velocity(self, rho):
|
|
1020
1356
|
"""
|
|
@@ -1040,7 +1376,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1040
1376
|
|
|
1041
1377
|
Notes
|
|
1042
1378
|
-----
|
|
1043
|
-
The estimation of the wave velocities is made by finding the eigenvalues of the Christoffel tensor [
|
|
1379
|
+
The estimation of the wave velocities is made by finding the eigenvalues of the Christoffel tensor [Jaeken]_.
|
|
1044
1380
|
|
|
1045
1381
|
One should double-check the units. The table below provides hints about the unit you get, depending on the units
|
|
1046
1382
|
you use for stiffness and the mass density:
|
|
@@ -1061,17 +1397,51 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1061
1397
|
|
|
1062
1398
|
References
|
|
1063
1399
|
----------
|
|
1064
|
-
.. [
|
|
1400
|
+
.. [Jaeken] J. W. Jaeken, S. Cottenier, Solving the Christoffel equation: Phase and group velocities, Computer Physics
|
|
1065
1401
|
Communications (207), 2016, https://doi.org/10.1016/j.cpc.2016.06.014.
|
|
1066
1402
|
|
|
1403
|
+
Examples
|
|
1404
|
+
--------
|
|
1405
|
+
We will investigate the wave velocities in cubic Cu. First, define the stiffness tensor:
|
|
1406
|
+
|
|
1407
|
+
>>> C = StiffnessTensor.cubic(C11=186e3, C12=134e3, C44=77e3) # Stiffness in MPa
|
|
1408
|
+
>>> rho = 8960 # mass density in kg/m³
|
|
1409
|
+
>>> c_p, c_s1, c_s2 = C.wave_velocity(rho)
|
|
1410
|
+
|
|
1411
|
+
The range of velocities of the (fast) primary wave range can be printed with:
|
|
1412
|
+
|
|
1413
|
+
>>> print(c_p)
|
|
1414
|
+
Spherical function
|
|
1415
|
+
Min=4.556196722204669, Max=5.324304112812698
|
|
1416
|
+
|
|
1417
|
+
As the stiffness is given in MPa and the mass desity is given in kg/m³, the velocities are returned in km/s
|
|
1418
|
+
(see Notes). If one wants to know the direction for min/max values:
|
|
1419
|
+
|
|
1420
|
+
>>> val_min, u_min = c_p.min()
|
|
1421
|
+
>>> print(u_min)
|
|
1422
|
+
[[0. 0. 1.]]
|
|
1423
|
+
|
|
1424
|
+
>>> val_max, u_max = c_p.max()
|
|
1425
|
+
>>> print(u_max)
|
|
1426
|
+
[[0.57735022 0.57735033 0.57735026]]
|
|
1427
|
+
|
|
1428
|
+
The secondary wave velocities are given by ``cs_1`` and ``cs_2``:
|
|
1429
|
+
|
|
1430
|
+
>>> c_s1
|
|
1431
|
+
Spherical function
|
|
1432
|
+
Min=2.1906864565414192, Max=2.9315098498896446
|
|
1433
|
+
|
|
1434
|
+
>>> c_s2
|
|
1435
|
+
Spherical function
|
|
1436
|
+
Min=1.7034628596749235, Max=2.9315098498896437
|
|
1067
1437
|
"""
|
|
1068
|
-
self.
|
|
1438
|
+
if self.ndim:
|
|
1439
|
+
raise ValueError('This function is not suitable for tensor array. Consider subscripting (e.g. C[0].wave_velocity()).')
|
|
1069
1440
|
def make_fun(index):
|
|
1070
1441
|
def fun(n):
|
|
1071
1442
|
Gamma = self.Christoffel_tensor(n)
|
|
1072
|
-
eig
|
|
1073
|
-
|
|
1074
|
-
return np.sqrt(eig_of_interest / rho)
|
|
1443
|
+
eig = Gamma.eigvals()
|
|
1444
|
+
return np.sqrt(eig[...,index] / rho)
|
|
1075
1445
|
|
|
1076
1446
|
return fun
|
|
1077
1447
|
|
|
@@ -1112,9 +1482,8 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1112
1482
|
key = str(material.material_id)
|
|
1113
1483
|
if material.elastic_tensor is not None:
|
|
1114
1484
|
matrix = material.elastic_tensor.ieee_format
|
|
1115
|
-
symmetry = material.symmetry.crystal_system.value
|
|
1116
1485
|
phase_name = material.formula_pretty
|
|
1117
|
-
C = StiffnessTensor(np.asarray(matrix),
|
|
1486
|
+
C = StiffnessTensor(np.asarray(matrix), phase_name=phase_name)
|
|
1118
1487
|
else:
|
|
1119
1488
|
C = None
|
|
1120
1489
|
Cdict[key] = C
|
|
@@ -1149,7 +1518,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1149
1518
|
if np.all([isinstance(a, ComplianceTensor) for a in Cs]):
|
|
1150
1519
|
Cs = [C.inv() for C in Cs]
|
|
1151
1520
|
if np.all([isinstance(a, StiffnessTensor) for a in Cs]):
|
|
1152
|
-
C_stack = np.array([C.
|
|
1521
|
+
C_stack = np.array([C.matrix() for C in Cs])
|
|
1153
1522
|
method = method.capitalize()
|
|
1154
1523
|
if method == 'Voigt':
|
|
1155
1524
|
C_avg = np.average(C_stack, weights=volume_fractions, axis=0)
|
|
@@ -1193,13 +1562,16 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1193
1562
|
.. [3] S. I. Ranganathan and M. Ostoja-Starzewski, Universal Elastic Anisotropy Index,
|
|
1194
1563
|
*Phys. Rev. Lett.*, 101(5), 055504, 2008. https://doi.org/10.1103/PhysRevLett.101.055504
|
|
1195
1564
|
"""
|
|
1196
|
-
self
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1565
|
+
if isinstance(self, StiffnessTensor):
|
|
1566
|
+
C_voigt = self.infinite_random_average()
|
|
1567
|
+
S_reuss = self.inv().infinite_random_average()
|
|
1568
|
+
else:
|
|
1569
|
+
S_reuss = self.infinite_random_average()
|
|
1570
|
+
C_voigt = self.inv().infinite_random_average()
|
|
1571
|
+
Kv = C_voigt.bulk_modulus
|
|
1572
|
+
Kr = S_reuss.bulk_modulus
|
|
1573
|
+
Gv = C_voigt._matrix[..., 3, 3] / 2
|
|
1574
|
+
Gr = 1 / S_reuss._matrix[..., 3, 3] / 2
|
|
1203
1575
|
return 5 * Gv / Gr + Kv / Kr - 6
|
|
1204
1576
|
|
|
1205
1577
|
def Zener_ratio(self, tol=1e-4):
|
|
@@ -1264,7 +1636,6 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1264
1636
|
0. ]
|
|
1265
1637
|
[ 25.98076211 -25.98076211 0. 0. 0.
|
|
1266
1638
|
65. ]]
|
|
1267
|
-
Symmetry: cubic
|
|
1268
1639
|
|
|
1269
1640
|
Still, we have
|
|
1270
1641
|
>>> C_rot.Zener_ratio()
|
|
@@ -1293,7 +1664,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1293
1664
|
from pymatgen.analysis.elasticity import elastic as matgenElast
|
|
1294
1665
|
except ImportError:
|
|
1295
1666
|
raise ModuleNotFoundError('pymatgen module is required for this function.')
|
|
1296
|
-
return matgenElast.ElasticTensor(self.full_tensor
|
|
1667
|
+
return matgenElast.ElasticTensor(self.full_tensor)
|
|
1297
1668
|
|
|
1298
1669
|
def to_Kelvin(self):
|
|
1299
1670
|
"""
|
|
@@ -1311,7 +1682,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1311
1682
|
|
|
1312
1683
|
Notes
|
|
1313
1684
|
-----
|
|
1314
|
-
This mapping convention is defined as follows [
|
|
1685
|
+
This mapping convention is defined as follows [Helbig]_:
|
|
1315
1686
|
|
|
1316
1687
|
.. math::
|
|
1317
1688
|
|
|
@@ -1327,11 +1698,32 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1327
1698
|
|
|
1328
1699
|
References
|
|
1329
1700
|
----------
|
|
1330
|
-
.. [
|
|
1331
|
-
doi: 10.1111/j.1365-2478.2011.01049.x
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1701
|
+
.. [Helbig] Helbig, K. (2013). What Kelvin might have written about Elasticity. Geophysical Prospecting, 61(1), 1-20.
|
|
1702
|
+
doi: `10.1111/j.1365-2478.2011.01049.x`_
|
|
1703
|
+
|
|
1704
|
+
.. _10.1111/j.1365-2478.2011.01049.x: https://doi.org/10.1111/j.1365-2478.2011.01049.x
|
|
1705
|
+
|
|
1706
|
+
Examples
|
|
1707
|
+
--------
|
|
1708
|
+
>>> from Elasticipy.tensors.elasticity import StiffnessTensor
|
|
1709
|
+
>>> C = StiffnessTensor.cubic(C11=200, C12=40, C44=20)
|
|
1710
|
+
>>> print(C)
|
|
1711
|
+
Stiffness tensor (in Voigt mapping):
|
|
1712
|
+
[[200. 40. 40. 0. 0. 0.]
|
|
1713
|
+
[ 40. 200. 40. 0. 0. 0.]
|
|
1714
|
+
[ 40. 40. 200. 0. 0. 0.]
|
|
1715
|
+
[ 0. 0. 0. 20. 0. 0.]
|
|
1716
|
+
[ 0. 0. 0. 0. 20. 0.]
|
|
1717
|
+
[ 0. 0. 0. 0. 0. 20.]]
|
|
1718
|
+
>>> C.to_Kelvin()
|
|
1719
|
+
array([[200., 40., 40., 0., 0., 0.],
|
|
1720
|
+
[ 40., 200., 40., 0., 0., 0.],
|
|
1721
|
+
[ 40., 40., 200., 0., 0., 0.],
|
|
1722
|
+
[ 0., 0., 0., 40., 0., 0.],
|
|
1723
|
+
[ 0., 0., 0., 0., 40., 0.],
|
|
1724
|
+
[ 0., 0., 0., 0., 0., 40.]])
|
|
1725
|
+
"""
|
|
1726
|
+
return self._matrix
|
|
1335
1727
|
|
|
1336
1728
|
def eig(self):
|
|
1337
1729
|
"""
|
|
@@ -1354,9 +1746,9 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1354
1746
|
|
|
1355
1747
|
Notes
|
|
1356
1748
|
-----
|
|
1357
|
-
The definition for eigenstiffnesses and the eigenstrains are introduced in [
|
|
1749
|
+
The definition for eigenstiffnesses and the eigenstrains are introduced in [Helbig]_.
|
|
1358
1750
|
"""
|
|
1359
|
-
return np.linalg.eigh(self.
|
|
1751
|
+
return np.linalg.eigh(self._matrix)
|
|
1360
1752
|
|
|
1361
1753
|
@property
|
|
1362
1754
|
def eig_stiffnesses(self):
|
|
@@ -1373,8 +1765,22 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1373
1765
|
eig : returns the eigenstiffnesses and the eigenstrains
|
|
1374
1766
|
eig_strains : returns the eigenstrains only
|
|
1375
1767
|
eig_stiffnesses_multiplicity : returns the unique values of eigenstiffnesses with multiplicity
|
|
1768
|
+
|
|
1769
|
+
Notes
|
|
1770
|
+
-----
|
|
1771
|
+
The eigenstiffnesses are defined in [Helbig]_.
|
|
1772
|
+
|
|
1773
|
+
Examples
|
|
1774
|
+
--------
|
|
1775
|
+
>>> from Elasticipy.tensors.elasticity import StiffnessTensor
|
|
1776
|
+
>>> C = StiffnessTensor.cubic(C11=200, C12=40, C44=20)
|
|
1777
|
+
>>> C.eig_stiffnesses
|
|
1778
|
+
array([ 40., 40., 40., 160., 160., 280.])
|
|
1779
|
+
|
|
1780
|
+
These values actually correspond to 2*C44 (with multiplicity = 3), C11-C12 (with multiplicity = 2) and C11+2C12
|
|
1781
|
+
(no multiplicity); see [Helbig]_.
|
|
1376
1782
|
"""
|
|
1377
|
-
return np.linalg.eigvalsh(self.
|
|
1783
|
+
return np.linalg.eigvalsh(self._matrix)
|
|
1378
1784
|
|
|
1379
1785
|
@property
|
|
1380
1786
|
def eig_strains(self):
|
|
@@ -1428,10 +1834,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1428
1834
|
--------
|
|
1429
1835
|
to_Kelvin : return the components as a (6,6) matrix following the Kelvin convention
|
|
1430
1836
|
"""
|
|
1431
|
-
|
|
1432
|
-
t = cls(matrix / kelvin_mapping.matrix, **kwargs)
|
|
1433
|
-
t._matrix *= t.mapping.matrix
|
|
1434
|
-
return t
|
|
1837
|
+
return cls(matrix, mapping=kelvin_mapping, **kwargs)
|
|
1435
1838
|
|
|
1436
1839
|
def eig_stiffnesses_multiplicity(self, tol=1e-4):
|
|
1437
1840
|
"""
|
|
@@ -1553,9 +1956,9 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1553
1956
|
8.1901353 ]
|
|
1554
1957
|
[-20.34233446 0.99714278 19.34519167 -6.52548033 8.1901353
|
|
1555
1958
|
39.41409344]]
|
|
1556
|
-
Symmetry: cubic
|
|
1557
1959
|
|
|
1558
|
-
Once rotated, it is not clear if the stiffness tensors has cubic symmetry. Yet:
|
|
1960
|
+
Once rotated, it is not clear if the stiffness tensors has cubic symmetry on sight. Yet:
|
|
1961
|
+
|
|
1559
1962
|
>>> C_rotated.is_cubic()
|
|
1560
1963
|
True
|
|
1561
1964
|
|
|
@@ -1591,12 +1994,127 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1591
1994
|
"""
|
|
1592
1995
|
Class for manipulating compliance tensors
|
|
1593
1996
|
"""
|
|
1594
|
-
|
|
1997
|
+
_tensor_name = 'Compliance'
|
|
1595
1998
|
_C11_C12_factor = 2.0
|
|
1596
1999
|
_component_prefix = 'S'
|
|
1597
2000
|
_C46_C56_factor = 2.0
|
|
1598
2001
|
|
|
1599
2002
|
def __init__(self, C, check_positive_definite=True, mapping=VoigtMapping(tensor='Compliance'), **kwargs):
|
|
2003
|
+
"""
|
|
2004
|
+
Construct a compliance tensor or an array of commpliance tensors.
|
|
2005
|
+
|
|
2006
|
+
The compliance tensor can be constructed from a (6,6) matrix or slices of (6,6) matrices. These matrices must be
|
|
2007
|
+
symmetric. An error is thrown if this matrix in not definite positive (except if
|
|
2008
|
+
``check_positive_definite==False``, see below). The input argument can also be the full tensor (array of shape
|
|
2009
|
+
(...,3,3,3,3)).
|
|
2010
|
+
|
|
2011
|
+
Parameters
|
|
2012
|
+
----------
|
|
2013
|
+
M : list or np.ndarray
|
|
2014
|
+
(...,6,6) matrix corresponding to the compliance tensor, written using the Voigt notation, or array of shape
|
|
2015
|
+
(...,3,3,3,3).
|
|
2016
|
+
phase_name : str, optional
|
|
2017
|
+
Phase name to display
|
|
2018
|
+
check_positive_definite : bool, optional
|
|
2019
|
+
Whether to check if the input matrix is positive definite or not. True by default.
|
|
2020
|
+
check_symmetry : bool, optional
|
|
2021
|
+
Whether to check or not that the input matrix is symmetric.
|
|
2022
|
+
force_symmetry : bool, optional
|
|
2023
|
+
If true, the major symmetry of the tensor is forced
|
|
2024
|
+
mapping : str or MappingConvention
|
|
2025
|
+
mapping convention to use. Default is VoigtMapping.
|
|
2026
|
+
|
|
2027
|
+
Notes
|
|
2028
|
+
-----
|
|
2029
|
+
The units used when building the comliance tensor are up to the user (/GPa, /MPa, /psi etc.). Therefore, the
|
|
2030
|
+
results you will get when performing operations (Young's modulus, "product" with strain tensor etc.) will be
|
|
2031
|
+
consistent with these units. For instance, if the compliance tensor is defined in /GPa, the computed stress will
|
|
2032
|
+
be given in GPa.
|
|
2033
|
+
|
|
2034
|
+
Examples
|
|
2035
|
+
--------
|
|
2036
|
+
Create a compliance tensor for cubic symmetry:
|
|
2037
|
+
|
|
2038
|
+
>>> matrix = [[200, 40, 40, 0, 0, 0 ],
|
|
2039
|
+
... [40, 200, 40, 0, 0, 0 ],
|
|
2040
|
+
... [40, 40, 200, 0, 0, 0 ],
|
|
2041
|
+
... [0, 0, 0, 20, 0, 0 ],
|
|
2042
|
+
... [0, 0, 0, 0, 20, 0 ],
|
|
2043
|
+
... [0, 0, 0, 0, 0, 20]]
|
|
2044
|
+
>>> from Elasticipy.tensors.elasticity import ComplianceTensor
|
|
2045
|
+
>>> S = ComplianceTensor(matrix)
|
|
2046
|
+
>>> print(S)
|
|
2047
|
+
Compliance tensor (in Voigt mapping):
|
|
2048
|
+
[[200. 40. 40. 0. 0. 0.]
|
|
2049
|
+
[ 40. 200. 40. 0. 0. 0.]
|
|
2050
|
+
[ 40. 40. 200. 0. 0. 0.]
|
|
2051
|
+
[ 0. 0. 0. 20. 0. 0.]
|
|
2052
|
+
[ 0. 0. 0. 0. 20. 0.]
|
|
2053
|
+
[ 0. 0. 0. 0. 0. 20.]]
|
|
2054
|
+
|
|
2055
|
+
Create a stiffness tensor from full (3,3,3,3) array:
|
|
2056
|
+
|
|
2057
|
+
>>> S_full = S.full_tensor # (3,3,3,3) numpy array
|
|
2058
|
+
>>> print((type(S_full), S_full.shape))
|
|
2059
|
+
(<class 'numpy.ndarray'>, (3, 3, 3, 3))
|
|
2060
|
+
|
|
2061
|
+
>>> ComplianceTensor(S_full)
|
|
2062
|
+
Compliance tensor (in Voigt mapping):
|
|
2063
|
+
[[200. 40. 40. 0. 0. 0.]
|
|
2064
|
+
[ 40. 200. 40. 0. 0. 0.]
|
|
2065
|
+
[ 40. 40. 200. 0. 0. 0.]
|
|
2066
|
+
[ 0. 0. 0. 20. 0. 0.]
|
|
2067
|
+
[ 0. 0. 0. 0. 20. 0.]
|
|
2068
|
+
[ 0. 0. 0. 0. 0. 20.]]
|
|
2069
|
+
|
|
2070
|
+
Create an array of compliance tensors:
|
|
2071
|
+
|
|
2072
|
+
First, we create two slices of (6,6) matrices, corresponding to two compliance values:
|
|
2073
|
+
|
|
2074
|
+
>>> slices = [[[200, 40, 40, 0, 0, 0 ],
|
|
2075
|
+
... [40, 200, 40, 0, 0, 0 ],
|
|
2076
|
+
... [40, 40, 200, 0, 0, 0 ],
|
|
2077
|
+
... [0, 0, 0, 20, 0, 0 ],
|
|
2078
|
+
... [0, 0, 0, 0, 20, 0 ],
|
|
2079
|
+
... [0, 0, 0, 0, 0, 20]],
|
|
2080
|
+
... [[250, 80, 80, 0, 0, 0 ],
|
|
2081
|
+
... [80, 250, 80, 0, 0, 0 ],
|
|
2082
|
+
... [80, 80, 250, 0, 0, 0 ],
|
|
2083
|
+
... [0, 0, 0, 40, 0, 0 ],
|
|
2084
|
+
... [0, 0, 0, 0, 40, 0 ],
|
|
2085
|
+
... [0, 0, 0, 0, 0, 40]]]
|
|
2086
|
+
|
|
2087
|
+
Then, one can create an array of compliance tensors:
|
|
2088
|
+
|
|
2089
|
+
>>> S_array=ComplianceTensor(slices)
|
|
2090
|
+
>>> print(S_array)
|
|
2091
|
+
Compliance tensor (in Voigt mapping):
|
|
2092
|
+
[[[200. 40. 40. 0. 0. 0.]
|
|
2093
|
+
[ 40. 200. 40. 0. 0. 0.]
|
|
2094
|
+
[ 40. 40. 200. 0. 0. 0.]
|
|
2095
|
+
[ 0. 0. 0. 20. 0. 0.]
|
|
2096
|
+
[ 0. 0. 0. 0. 20. 0.]
|
|
2097
|
+
[ 0. 0. 0. 0. 0. 20.]]
|
|
2098
|
+
<BLANKLINE>
|
|
2099
|
+
[[250. 80. 80. 0. 0. 0.]
|
|
2100
|
+
[ 80. 250. 80. 0. 0. 0.]
|
|
2101
|
+
[ 80. 80. 250. 0. 0. 0.]
|
|
2102
|
+
[ 0. 0. 0. 40. 0. 0.]
|
|
2103
|
+
[ 0. 0. 0. 0. 40. 0.]
|
|
2104
|
+
[ 0. 0. 0. 0. 0. 40.]]]
|
|
2105
|
+
|
|
2106
|
+
|
|
2107
|
+
This array can be subindexed. E.g.:
|
|
2108
|
+
|
|
2109
|
+
>>> S_array[0]
|
|
2110
|
+
Compliance tensor (in Voigt mapping):
|
|
2111
|
+
[[200. 40. 40. 0. 0. 0.]
|
|
2112
|
+
[ 40. 200. 40. 0. 0. 0.]
|
|
2113
|
+
[ 40. 40. 200. 0. 0. 0.]
|
|
2114
|
+
[ 0. 0. 0. 20. 0. 0.]
|
|
2115
|
+
[ 0. 0. 0. 0. 20. 0.]
|
|
2116
|
+
[ 0. 0. 0. 0. 0. 20.]]
|
|
2117
|
+
"""
|
|
1600
2118
|
super().__init__(C, check_positive_definite=check_positive_definite, mapping=mapping, **kwargs)
|
|
1601
2119
|
self.mapping_name = 'Voigt'
|
|
1602
2120
|
|
|
@@ -1619,21 +2137,15 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1619
2137
|
Reciprocal tensor
|
|
1620
2138
|
"""
|
|
1621
2139
|
S = np.linalg.inv(self._matrix)
|
|
1622
|
-
|
|
2140
|
+
t = StiffnessTensor(S, mapping=kelvin_mapping, phase_name=self.phase_name)
|
|
2141
|
+
t.mapping = self.mapping.mapping_inverse
|
|
2142
|
+
return t
|
|
1623
2143
|
|
|
1624
2144
|
def Reuss_average(self, axis=None):
|
|
1625
2145
|
if self.ndim:
|
|
1626
2146
|
return self.mean(axis=axis)
|
|
1627
2147
|
else:
|
|
1628
|
-
|
|
1629
|
-
A = s[0, 0] + s[1, 1] + s[2, 2]
|
|
1630
|
-
B = s[0, 1] + s[0, 2] + s[1, 2]
|
|
1631
|
-
C = s[3, 3] + s[4, 4] + s[5, 5]
|
|
1632
|
-
S11 = 1 / 5 * A + 2 / 15 * B + 1 / 15 * C
|
|
1633
|
-
S12 = 1 / 15 * A + 4 / 15 * B - 1 / 30 * C
|
|
1634
|
-
S44 = 4 / 15 * (A - B) + 1 / 5 * C
|
|
1635
|
-
mat = _isotropic_matrix(S11, S12, S44)
|
|
1636
|
-
return ComplianceTensor(mat, symmetry='isotropic', phase_name=self.phase_name)
|
|
2148
|
+
return self.infinite_random_average()
|
|
1637
2149
|
|
|
1638
2150
|
def Voigt_average(self, axis=None):
|
|
1639
2151
|
return self.inv().Voigt_average(axis=axis).inv()
|
|
@@ -1652,42 +2164,56 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1652
2164
|
if n_specified != 2:
|
|
1653
2165
|
raise ValueError("Exactly two values are required among E, nu, G, K, lame1 and lame2.")
|
|
1654
2166
|
if K is not None:
|
|
2167
|
+
K = np.asarray(K)
|
|
1655
2168
|
if E is not None:
|
|
2169
|
+
E = np.asarray(E, dtype=float)
|
|
1656
2170
|
G = 3 * K * E / (9 * K - E)
|
|
1657
2171
|
nu = (3 * K - E) / 6 / K
|
|
1658
2172
|
elif lame1 is not None:
|
|
2173
|
+
lame1 = np.asarray(lame1, dtype=float)
|
|
1659
2174
|
E = 9 * K * (K - lame1) / (3 * K -lame1)
|
|
1660
2175
|
G= 3 * (K - lame1) / 2
|
|
1661
2176
|
nu = lame1 / (3*K-lame1)
|
|
1662
2177
|
elif G is not None:
|
|
2178
|
+
G = np.asarray(G, dtype=float)
|
|
1663
2179
|
E = 9 * K * G / (3 * K + G)
|
|
1664
2180
|
nu = (3 * K - 2 * G) / 2 / (3 * K + G)
|
|
1665
2181
|
elif nu is not None:
|
|
2182
|
+
nu = np.asarray(nu, dtype=float)
|
|
1666
2183
|
E = 3 * K * (1 - 2 * nu)
|
|
1667
2184
|
G = E / 2 / (1 + nu)
|
|
1668
2185
|
elif E is not None:
|
|
2186
|
+
E = np.asarray(E, dtype=float)
|
|
1669
2187
|
if lame1 is not None:
|
|
1670
|
-
|
|
2188
|
+
lame1 = np.asarray(lame1, dtype=float)
|
|
2189
|
+
R = np.sqrt(E**2 + 9*lame1**2 + 2*E*lame1)
|
|
1671
2190
|
G = (E - 3 * lame1 + R) / 4
|
|
1672
2191
|
nu = 2 * lame1 / (E + lame1 + R)
|
|
1673
2192
|
elif G is not None:
|
|
2193
|
+
G = np.asarray(G, dtype=float)
|
|
1674
2194
|
nu = E / 2 / G - 1
|
|
1675
2195
|
elif nu is not None:
|
|
2196
|
+
nu = np.asarray(nu, dtype=float)
|
|
1676
2197
|
G = E / 2 / (1 + nu)
|
|
1677
2198
|
elif lame1 is not None:
|
|
2199
|
+
lame1 = np.asarray(lame1, dtype=float)
|
|
1678
2200
|
if G is not None:
|
|
2201
|
+
G = np.asarray(G, dtype=float)
|
|
1679
2202
|
E = G * (3 * lame1 + 2 * G) / (lame1 + G)
|
|
1680
2203
|
nu = lame1 / 2 / (lame1 + G)
|
|
1681
2204
|
elif nu is not None:
|
|
2205
|
+
nu = np.asarray(nu, dtype=float)
|
|
1682
2206
|
E = lame1 * ( 1 + nu) * (1 - 2 * nu) / nu
|
|
1683
2207
|
G = lame1 * (1 - 2 * nu) / 2 / nu
|
|
1684
2208
|
elif (nu is not None) and (G is not None):
|
|
2209
|
+
nu = np.asarray(nu, dtype=float)
|
|
2210
|
+
G = np.asarray(G)
|
|
1685
2211
|
E = 2 * G * (1 + nu)
|
|
1686
2212
|
S11 = 1/E
|
|
1687
2213
|
S12 = -nu/E
|
|
1688
2214
|
S44 = 1 / G
|
|
1689
2215
|
S_mat = _isotropic_matrix(S11, S12, S44)
|
|
1690
|
-
return ComplianceTensor(S_mat,
|
|
2216
|
+
return ComplianceTensor(S_mat, phase_name=phase_name)
|
|
1691
2217
|
|
|
1692
2218
|
@classmethod
|
|
1693
2219
|
def orthotropic(cls, *, Ex, Ey, Ez, Gxy, Gxz, Gyz,
|
|
@@ -1703,7 +2229,7 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1703
2229
|
[0, 0, 0, 0, 1 / Gxz, 0],
|
|
1704
2230
|
[0, 0, 0, 0, 0, 1 / Gxy]])
|
|
1705
2231
|
S = tri_sup + np.tril(tri_sup.T, -1)
|
|
1706
|
-
return ComplianceTensor(S,
|
|
2232
|
+
return ComplianceTensor(S, **kwargs)
|
|
1707
2233
|
|
|
1708
2234
|
@classmethod
|
|
1709
2235
|
def transverse_isotropic(cls, *args, **kwargs):
|
|
@@ -1746,7 +2272,7 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1746
2272
|
from pymatgen.analysis.elasticity import elastic as matgenElast
|
|
1747
2273
|
except ImportError:
|
|
1748
2274
|
raise ModuleNotFoundError('pymatgen module is required for this function.')
|
|
1749
|
-
return matgenElast.ComplianceTensor(self.full_tensor
|
|
2275
|
+
return matgenElast.ComplianceTensor(self.full_tensor)
|
|
1750
2276
|
|
|
1751
2277
|
def eig(self):
|
|
1752
2278
|
"""
|
|
@@ -1769,7 +2295,7 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1769
2295
|
|
|
1770
2296
|
Notes
|
|
1771
2297
|
-----
|
|
1772
|
-
The definition for eigencompliances and the eigenstresses are introduced in [
|
|
2298
|
+
The definition for eigencompliances and the eigenstresses are introduced in [Helbig]_.
|
|
1773
2299
|
"""
|
|
1774
2300
|
return np.linalg.eigh(self.to_Kelvin())
|
|
1775
2301
|
|