elasticipy 4.1.1__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 +1032 -203
- 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.1.1.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.1.1.dist-info/RECORD +0 -23
- {elasticipy-4.1.1.dist-info → elasticipy-5.0.0.dist-info}/WHEEL +0 -0
- {elasticipy-4.1.1.dist-info → elasticipy-5.0.0.dist-info}/licenses/LICENSE +0 -0
- {elasticipy-4.1.1.dist-info → elasticipy-5.0.0.dist-info}/top_level.txt +0 -0
Elasticipy/tensors/elasticity.py
CHANGED
|
@@ -1,10 +1,25 @@
|
|
|
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
|
|
10
|
+
from warnings import warn
|
|
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
|
+
|
|
8
23
|
|
|
9
24
|
def _parse_tensor_components(prefix, **kwargs):
|
|
10
25
|
pattern = r'^{}(\d{{2}})$'.format(prefix)
|
|
@@ -18,15 +33,6 @@ def _parse_tensor_components(prefix, **kwargs):
|
|
|
18
33
|
def _indices2str(ij):
|
|
19
34
|
return f'{ij[0] + 1}{ij[1] + 1}'
|
|
20
35
|
|
|
21
|
-
def _isotropic_matrix(C11, C12, C44):
|
|
22
|
-
return np.array([[C11, C12, C12, 0, 0, 0],
|
|
23
|
-
[C12, C11, C12, 0, 0, 0],
|
|
24
|
-
[C12, C12, C11, 0, 0, 0],
|
|
25
|
-
[0, 0, 0, C44, 0, 0],
|
|
26
|
-
[0, 0, 0, 0, C44, 0],
|
|
27
|
-
[0, 0, 0, 0, 0, C44]])
|
|
28
|
-
|
|
29
|
-
|
|
30
36
|
def _check_definite_positive(mat):
|
|
31
37
|
try:
|
|
32
38
|
np.linalg.cholesky(mat)
|
|
@@ -46,30 +52,33 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
46
52
|
"""
|
|
47
53
|
Class for manipulating fourth-order stiffness tensors.
|
|
48
54
|
"""
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
55
|
+
_tensor_name = 'Stiffness'
|
|
56
|
+
_C11_C12_factor = 0.5
|
|
57
|
+
_C46_C56_factor = 1.0
|
|
58
|
+
_component_prefix = 'C'
|
|
53
59
|
|
|
54
|
-
def __init__(self, M,
|
|
60
|
+
def __init__(self, M, check_positive_definite=True, phase_name= None, mapping=VoigtMapping(), **kwargs):
|
|
55
61
|
"""
|
|
56
|
-
Construct
|
|
62
|
+
Construct a stiffness tensor or an array of stiffness tensors.
|
|
57
63
|
|
|
58
|
-
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)).
|
|
59
68
|
|
|
60
69
|
Parameters
|
|
61
70
|
----------
|
|
62
|
-
M : np.ndarray
|
|
63
|
-
(6,6) matrix corresponding to the stiffness tensor, written using the Voigt notation, or array of shape
|
|
64
|
-
(3,3,3,3).
|
|
65
|
-
phase_name : str,
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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.
|
|
69
78
|
check_symmetry : bool, optional
|
|
70
79
|
Whether to check or not that the input matrix is symmetric.
|
|
71
80
|
force_symmetry : bool, optional
|
|
72
|
-
If true, the major symmetry of the tensor is
|
|
81
|
+
If true, the major symmetry of the tensor is forced
|
|
73
82
|
mapping : str or MappingConvention
|
|
74
83
|
mapping convention to use. Default is VoigtMapping.
|
|
75
84
|
|
|
@@ -79,11 +88,94 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
79
88
|
results you will get when performing operations (Young's modulus, "product" with strain tensor etc.) will be
|
|
80
89
|
consistent with these units. For instance, if the stiffness tensor is defined in GPa, the computed stress will
|
|
81
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.]]
|
|
82
175
|
"""
|
|
83
176
|
super().__init__(M, mapping=mapping, **kwargs)
|
|
84
177
|
if check_positive_definite:
|
|
85
178
|
_check_definite_positive(self._matrix)
|
|
86
|
-
self.symmetry = symmetry
|
|
87
179
|
self.phase_name = phase_name
|
|
88
180
|
|
|
89
181
|
def __mul__(self, other):
|
|
@@ -99,7 +191,6 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
99
191
|
string = super().__repr__()
|
|
100
192
|
if self.phase_name is not None:
|
|
101
193
|
string += '\nPhase: {}'.format(self.phase_name)
|
|
102
|
-
string += '\nSymmetry: {}'.format(self.symmetry)
|
|
103
194
|
return string
|
|
104
195
|
|
|
105
196
|
def inv(self):
|
|
@@ -110,9 +201,82 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
110
201
|
-------
|
|
111
202
|
ComplianceTensor
|
|
112
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 ]]]
|
|
113
275
|
"""
|
|
114
276
|
C = np.linalg.inv(self._matrix)
|
|
115
|
-
|
|
277
|
+
t2 = ComplianceTensor(C, mapping=kelvin_mapping, phase_name=self.phase_name)
|
|
278
|
+
t2.mapping = self.mapping.mapping_inverse
|
|
279
|
+
return t2
|
|
116
280
|
|
|
117
281
|
@classmethod
|
|
118
282
|
def from_txt_file(cls, filename):
|
|
@@ -149,16 +313,11 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
149
313
|
phase_name = lines[0].split(": ", 1)[1].strip()
|
|
150
314
|
matrix_start_index += 1
|
|
151
315
|
|
|
152
|
-
# Parse symmetry if available
|
|
153
|
-
if len(lines) > matrix_start_index and lines[matrix_start_index].startswith("Symmetry:"):
|
|
154
|
-
symmetry = lines[matrix_start_index].split(": ", 1)[1].strip()
|
|
155
|
-
matrix_start_index += 1
|
|
156
|
-
|
|
157
316
|
# Parse matrix
|
|
158
317
|
matrix = np.loadtxt(lines[matrix_start_index:])
|
|
159
318
|
|
|
160
319
|
# Return the reconstructed object
|
|
161
|
-
return cls(matrix, phase_name=phase_name
|
|
320
|
+
return cls(matrix, phase_name=phase_name)
|
|
162
321
|
|
|
163
322
|
def save_to_txt(self, filename, matrix_only=False):
|
|
164
323
|
"""
|
|
@@ -169,7 +328,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
169
328
|
filename : str
|
|
170
329
|
Filename to save the tensor to.
|
|
171
330
|
matrix_only : bool, False
|
|
172
|
-
If true, only the components of
|
|
331
|
+
If true, only the components of the stiffness tensor is saved (no data about phase nor symmetry)
|
|
173
332
|
|
|
174
333
|
See Also
|
|
175
334
|
--------
|
|
@@ -180,14 +339,13 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
180
339
|
if not matrix_only:
|
|
181
340
|
if self.phase_name is not None:
|
|
182
341
|
f.write(f"Phase Name: {self.phase_name}\n")
|
|
183
|
-
|
|
184
|
-
for row in self._matrix:
|
|
342
|
+
for row in self.matrix():
|
|
185
343
|
f.write(" " + " ".join(f"{value:8.2f}" for value in row) + "\n")
|
|
186
344
|
|
|
187
345
|
@classmethod
|
|
188
346
|
def _matrixFromCrystalSymmetry(cls, symmetry='Triclinic', point_group=None, diad='y', prefix=None, **kwargs):
|
|
189
347
|
if prefix is None:
|
|
190
|
-
prefix = cls.
|
|
348
|
+
prefix = cls._component_prefix
|
|
191
349
|
values = _parse_tensor_components(prefix, **kwargs)
|
|
192
350
|
C = np.zeros((6, 6))
|
|
193
351
|
symmetry = symmetry.capitalize()
|
|
@@ -228,17 +386,17 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
228
386
|
C11_C12 = symmetry_description.C11_C12
|
|
229
387
|
if C11_C12:
|
|
230
388
|
for index in C11_C12:
|
|
231
|
-
C[index] = (C[0, 0] - C[0, 1]) * cls.
|
|
389
|
+
C[index] = (C[0, 0] - C[0, 1]) * cls._C11_C12_factor
|
|
232
390
|
|
|
233
391
|
if symmetry == 'Trigonal':
|
|
234
|
-
C[3, 5] = cls.
|
|
235
|
-
C[4, 5] = cls.
|
|
392
|
+
C[3, 5] = cls._C46_C56_factor * C[3, 5]
|
|
393
|
+
C[4, 5] = cls._C46_C56_factor * C[4, 5]
|
|
236
394
|
|
|
237
395
|
return C + np.tril(C.T, -1)
|
|
238
396
|
|
|
239
397
|
@classmethod
|
|
240
398
|
def fromCrystalSymmetry(cls, symmetry='Triclinic', point_group=None, diad='y', phase_name=None, prefix=None,
|
|
241
|
-
|
|
399
|
+
**kwargs):
|
|
242
400
|
"""
|
|
243
401
|
Create a fourth-order tensor from limited number of components, taking advantage of crystallographic symmetries
|
|
244
402
|
|
|
@@ -271,11 +429,11 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
271
429
|
|
|
272
430
|
Notes
|
|
273
431
|
-----
|
|
274
|
-
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]_.
|
|
275
433
|
|
|
276
434
|
References
|
|
277
435
|
----------
|
|
278
|
-
.. [
|
|
436
|
+
.. [Nye] Nye, J. F. Physical Properties of Crystals. London: Oxford University Press, 1959.
|
|
279
437
|
|
|
280
438
|
Examples
|
|
281
439
|
--------
|
|
@@ -293,7 +451,6 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
293
451
|
[-18. 1. -3. 0. 11. 0.]
|
|
294
452
|
[ 0. 0. 0. 3. 0. 85.]]
|
|
295
453
|
Phase: TiNi
|
|
296
|
-
Symmetry: monoclinic
|
|
297
454
|
|
|
298
455
|
>>> from Elasticipy.tensors.elasticity import ComplianceTensor\n
|
|
299
456
|
>>> ComplianceTensor.fromCrystalSymmetry(symmetry='monoclinic', diad='y', phase_name='TiNi',
|
|
@@ -309,11 +466,15 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
309
466
|
[ 14. -8. 0. 0. 116. 0.]
|
|
310
467
|
[ 0. 0. 0. 0. 0. 12.]]
|
|
311
468
|
Phase: TiNi
|
|
312
|
-
Symmetry: monoclinic
|
|
313
469
|
"""
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
470
|
+
warn('This function will be removed in a future release. Use {}.{}() instead'.format(cls.__name__,symmetry), DeprecationWarning, stacklevel=2)
|
|
471
|
+
return cls._fromCrystalSymmetry(symmetry=symmetry, point_group=point_group, diad=diad, phase_name=phase_name,
|
|
472
|
+
prefix=prefix, **kwargs)
|
|
473
|
+
|
|
474
|
+
@classmethod
|
|
475
|
+
def _fromCrystalSymmetry(cls, symmetry, phase_name, **kwargs):
|
|
476
|
+
matrix = cls._matrixFromCrystalSymmetry(symmetry=symmetry, **kwargs)
|
|
477
|
+
return cls(matrix, phase_name=phase_name)
|
|
317
478
|
|
|
318
479
|
|
|
319
480
|
@classmethod
|
|
@@ -337,7 +498,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
337
498
|
cubic : create a tensor from cubic symmetry
|
|
338
499
|
tetragonal : create a tensor from tetragonal symmetry
|
|
339
500
|
"""
|
|
340
|
-
return cls.
|
|
501
|
+
return cls._fromCrystalSymmetry(symmetry='hexagonal', C11=C11, C12=C12, C13=C13, C33=C33, C44=C44,
|
|
341
502
|
phase_name=phase_name, prefix='C')
|
|
342
503
|
|
|
343
504
|
@classmethod
|
|
@@ -362,8 +523,9 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
362
523
|
tetragonal : create a tensor from tetragonal symmetry
|
|
363
524
|
orthorhombic : create a tensor from orthorhombic symmetry
|
|
364
525
|
"""
|
|
365
|
-
return cls.
|
|
366
|
-
|
|
526
|
+
return cls._fromCrystalSymmetry(symmetry='trigonal', point_group='3',
|
|
527
|
+
C11=C11, C12=C12, C13=C13, C14=C14, C15=C15,
|
|
528
|
+
C33=C33, C44=C44, phase_name=phase_name, prefix='C')
|
|
367
529
|
|
|
368
530
|
@classmethod
|
|
369
531
|
def tetragonal(cls, *, C11=0., C12=0., C13=0., C33=0., C44=0., C16=0., C66=0., phase_name=None):
|
|
@@ -388,8 +550,9 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
388
550
|
trigonal : create a tensor from trigonal symmetry
|
|
389
551
|
orthorhombic : create a tensor from orthorhombic symmetry
|
|
390
552
|
"""
|
|
391
|
-
return cls.
|
|
392
|
-
|
|
553
|
+
return cls._fromCrystalSymmetry(symmetry='tetragonal', point_group='4',
|
|
554
|
+
C11=C11, C12=C12, C13=C13, C16=C16,
|
|
555
|
+
C33=C33, C44=C44, C66=C66, phase_name=phase_name, prefix='C')
|
|
393
556
|
|
|
394
557
|
@classmethod
|
|
395
558
|
def cubic(cls, *, C11=0., C12=0., C44=0., phase_name=None):
|
|
@@ -411,7 +574,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
411
574
|
hexagonal : create a tensor from hexagonal symmetry
|
|
412
575
|
orthorhombic : create a tensor from orthorhombic symmetry
|
|
413
576
|
"""
|
|
414
|
-
return cls.
|
|
577
|
+
return cls._fromCrystalSymmetry(symmetry='cubic', C11=C11, C12=C12, C44=C44, phase_name=phase_name, prefix='C')
|
|
415
578
|
|
|
416
579
|
@classmethod
|
|
417
580
|
def orthorhombic(cls, *, C11=0., C12=0., C13=0., C22=0., C23=0., C33=0., C44=0., C55=0., C66=0., phase_name=None):
|
|
@@ -434,7 +597,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
434
597
|
monoclinic : create a tensor from monoclinic symmetry
|
|
435
598
|
orthorhombic : create a tensor from orthorhombic symmetry
|
|
436
599
|
"""
|
|
437
|
-
return cls.
|
|
600
|
+
return cls._fromCrystalSymmetry(symmetry='orthorhombic',
|
|
438
601
|
C11=C11, C12=C12, C13=C13, C22=C22, C23=C23, C33=C33, C44=C44, C55=C55, C66=C66,
|
|
439
602
|
phase_name=phase_name, prefix='C')
|
|
440
603
|
|
|
@@ -487,12 +650,12 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
487
650
|
if diad_y and diad_z:
|
|
488
651
|
raise KeyError('Ambiguous diad. Provide either C15, C25, C35 and C46; or C16, C26, C36 and C45')
|
|
489
652
|
elif diad_y:
|
|
490
|
-
return cls.
|
|
653
|
+
return cls._fromCrystalSymmetry(symmetry='monoclinic', diad='y',
|
|
491
654
|
C11=C11, C12=C12, C13=C13, C22=C22, C23=C23, C33=C33, C44=C44, C55=C55,
|
|
492
655
|
C66=C66,
|
|
493
656
|
C15=C15, C25=C25, C35=C35, C46=C46, phase_name=phase_name, prefix='C')
|
|
494
657
|
elif diad_z:
|
|
495
|
-
return cls.
|
|
658
|
+
return cls._fromCrystalSymmetry(symmetry='monoclinic', diad='z',
|
|
496
659
|
C11=C11, C12=C12, C13=C13, C22=C22, C23=C23, C33=C33, C44=C44, C55=C55,
|
|
497
660
|
C66=C66,
|
|
498
661
|
C16=C16, C26=C26, C36=C36, C45=C45, phase_name=phase_name, prefix='C')
|
|
@@ -533,12 +696,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
533
696
|
[C16, C26, C36, C46, C56, C66]])
|
|
534
697
|
return cls(matrix, phase_name=phase_name)
|
|
535
698
|
|
|
536
|
-
|
|
537
|
-
if self.ndim:
|
|
538
|
-
err_msg = fun_name + ' is not suitable for tensor array. Consider subscripting (e.g. C[0].{}).'.format(fun_name)
|
|
539
|
-
raise ValueError(err_msg)
|
|
540
|
-
|
|
541
|
-
@property
|
|
699
|
+
@_elementwise_property
|
|
542
700
|
def Young_modulus(self):
|
|
543
701
|
"""
|
|
544
702
|
Directional Young's modulus
|
|
@@ -548,18 +706,17 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
548
706
|
SphericalFunction
|
|
549
707
|
Young's modulus
|
|
550
708
|
"""
|
|
551
|
-
self._single_tensor_only('Young_modulus')
|
|
552
709
|
if isinstance(self, ComplianceTensor):
|
|
553
710
|
S = self
|
|
554
711
|
else:
|
|
555
712
|
S = self.inv()
|
|
556
713
|
def compute_young_modulus(u):
|
|
557
|
-
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)
|
|
558
715
|
return 1 / a
|
|
559
716
|
|
|
560
717
|
return SphericalFunction(compute_young_modulus)
|
|
561
718
|
|
|
562
|
-
@
|
|
719
|
+
@_elementwise_property
|
|
563
720
|
def shear_modulus(self):
|
|
564
721
|
"""
|
|
565
722
|
Directional shear modulus
|
|
@@ -569,18 +726,17 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
569
726
|
HyperSphericalFunction
|
|
570
727
|
Shear modulus
|
|
571
728
|
"""
|
|
572
|
-
self._single_tensor_only('shear_modulus')
|
|
573
729
|
if isinstance(self, ComplianceTensor):
|
|
574
730
|
S = self
|
|
575
731
|
else:
|
|
576
732
|
S = self.inv()
|
|
577
733
|
def compute_shear_modulus(u, v):
|
|
578
|
-
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)
|
|
579
735
|
return G
|
|
580
736
|
|
|
581
737
|
return HyperSphericalFunction(compute_shear_modulus)
|
|
582
738
|
|
|
583
|
-
@
|
|
739
|
+
@_elementwise_property
|
|
584
740
|
def Poisson_ratio(self):
|
|
585
741
|
"""
|
|
586
742
|
Directional Poisson's ratio
|
|
@@ -601,11 +757,10 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
601
757
|
|
|
602
758
|
where :math:`\\varepsilon_{jj}` denotes the (compressive) longitudinal strain along the j-th direction.
|
|
603
759
|
"""
|
|
604
|
-
self._single_tensor_only('Poisson_ratio')
|
|
605
760
|
if isinstance(self, ComplianceTensor):
|
|
606
|
-
Sfull = self.full_tensor
|
|
761
|
+
Sfull = self.full_tensor
|
|
607
762
|
else:
|
|
608
|
-
Sfull = self.inv().full_tensor
|
|
763
|
+
Sfull = self.inv().full_tensor
|
|
609
764
|
def compute_PoissonRatio(u, v):
|
|
610
765
|
numer = np.einsum('ijkl,...i,...j,...k,...l->...',Sfull,v,v,u,u)
|
|
611
766
|
denom = np.einsum('ijkl,...i,...j,...k,...l->...',Sfull,u,u,u,u)
|
|
@@ -613,7 +768,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
613
768
|
|
|
614
769
|
return HyperSphericalFunction(compute_PoissonRatio)
|
|
615
770
|
|
|
616
|
-
@
|
|
771
|
+
@_elementwise_property
|
|
617
772
|
def linear_compressibility(self):
|
|
618
773
|
"""
|
|
619
774
|
Compute the directional linear compressibility.
|
|
@@ -627,13 +782,12 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
627
782
|
--------
|
|
628
783
|
bulk_modulus : bulk modulus of the material
|
|
629
784
|
"""
|
|
630
|
-
self._single_tensor_only('linear_compressibility')
|
|
631
785
|
if isinstance(self, ComplianceTensor):
|
|
632
786
|
S = self
|
|
633
787
|
else:
|
|
634
788
|
S = self.inv()
|
|
635
789
|
def compute_linear_compressibility(u):
|
|
636
|
-
return np.einsum('ijkk,...i,...j->...',S.full_tensor
|
|
790
|
+
return np.einsum('ijkk,...i,...j->...',S.full_tensor,u,u)
|
|
637
791
|
|
|
638
792
|
return SphericalFunction(compute_linear_compressibility)
|
|
639
793
|
|
|
@@ -653,12 +807,55 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
653
807
|
"""
|
|
654
808
|
return self.inv().bulk_modulus
|
|
655
809
|
|
|
810
|
+
@_elementwise_property
|
|
811
|
+
def lame1(self):
|
|
812
|
+
"""
|
|
813
|
+
Compute the first Lamé's parameter (only for isotropic materials).
|
|
814
|
+
|
|
815
|
+
If the stiffness/compliance tensor is not isotropic, NaN is returned.
|
|
816
|
+
|
|
817
|
+
Returns
|
|
818
|
+
-------
|
|
819
|
+
float
|
|
820
|
+
First Lamé's parameter
|
|
821
|
+
|
|
822
|
+
See Also
|
|
823
|
+
--------
|
|
824
|
+
lame2 : second Lamé's parameter
|
|
825
|
+
"""
|
|
826
|
+
if self.is_isotropic():
|
|
827
|
+
C11 = (self.C11 + self.C22 + self.C33) / 3
|
|
828
|
+
return C11 - 2 * self.lame2
|
|
829
|
+
else:
|
|
830
|
+
return np.nan
|
|
831
|
+
|
|
832
|
+
@_elementwise_property
|
|
833
|
+
def lame2(self):
|
|
834
|
+
"""
|
|
835
|
+
Compute the second Lamé's parameter (only for isotropic materials).
|
|
836
|
+
|
|
837
|
+
If the stiffness/compliance tensor is not isotropic, NaN is returned.
|
|
838
|
+
|
|
839
|
+
Returns
|
|
840
|
+
-------
|
|
841
|
+
float
|
|
842
|
+
Second Lamé's parameter
|
|
843
|
+
|
|
844
|
+
See Also
|
|
845
|
+
--------
|
|
846
|
+
lame1 : first Lamé's parameter
|
|
847
|
+
"""
|
|
848
|
+
if self.is_isotropic():
|
|
849
|
+
return (self.C44 + self.C55 + self.C66) / 3
|
|
850
|
+
else:
|
|
851
|
+
return np.nan
|
|
852
|
+
|
|
656
853
|
def Voigt_average(self, axis=None):
|
|
657
854
|
"""
|
|
658
|
-
Compute the Voigt average
|
|
855
|
+
Compute the Voigt average (from the mean of stiffness tensors).
|
|
659
856
|
|
|
660
|
-
If the
|
|
661
|
-
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.
|
|
662
859
|
|
|
663
860
|
Parameters
|
|
664
861
|
----------
|
|
@@ -675,24 +872,67 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
675
872
|
Reuss_average : compute the Reuss average
|
|
676
873
|
Hill_average : compute the Voigt-Reuss-Hill average
|
|
677
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]]
|
|
678
924
|
"""
|
|
679
925
|
if self.ndim:
|
|
680
926
|
return self.mean(axis=axis)
|
|
681
927
|
else:
|
|
682
|
-
|
|
683
|
-
A = c[0, 0] + c[1, 1] + c[2, 2]
|
|
684
|
-
B = c[0, 1] + c[0, 2] + c[1, 2]
|
|
685
|
-
C = c[3, 3] + c[4, 4] + c[5, 5]
|
|
686
|
-
C11 = 1 / 5 * A + 2 / 15 * B + 4 / 15 * C
|
|
687
|
-
C12 = 1 / 15 * A + 4 / 15 * B - 2 / 15 * C
|
|
688
|
-
C44 = (A - B) / 15 + C / 5
|
|
689
|
-
mat = _isotropic_matrix(C11, C12, C44)
|
|
690
|
-
return StiffnessTensor(mat, symmetry='isotropic', phase_name=self.phase_name)
|
|
928
|
+
return self.infinite_random_average()
|
|
691
929
|
|
|
692
930
|
def Reuss_average(self, axis=None):
|
|
693
931
|
"""
|
|
694
|
-
Compute the Reuss average
|
|
695
|
-
|
|
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.
|
|
696
936
|
|
|
697
937
|
Parameters
|
|
698
938
|
----------
|
|
@@ -709,13 +949,70 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
709
949
|
Voigt_average : compute the Voigt average
|
|
710
950
|
Hill_average : compute the Voigt-Reuss-Hill average
|
|
711
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]]
|
|
712
1007
|
"""
|
|
713
1008
|
return self.inv().Reuss_average(axis=axis).inv()
|
|
714
1009
|
|
|
715
1010
|
def Hill_average(self, axis=None):
|
|
716
1011
|
"""
|
|
717
|
-
Compute the
|
|
718
|
-
|
|
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.
|
|
719
1016
|
|
|
720
1017
|
Parameters
|
|
721
1018
|
----------
|
|
@@ -732,6 +1029,61 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
732
1029
|
Voigt_average : compute the Voigt average
|
|
733
1030
|
Reuss_average : compute the Reuss average
|
|
734
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]]
|
|
735
1087
|
"""
|
|
736
1088
|
Reuss = self.Reuss_average(axis=axis)
|
|
737
1089
|
Voigt = self.Voigt_average(axis=axis)
|
|
@@ -768,21 +1120,28 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
768
1120
|
raise NotImplementedError('Only Voigt, Reus, and Hill are implemented.')
|
|
769
1121
|
|
|
770
1122
|
@classmethod
|
|
771
|
-
def isotropic(cls, E=None, nu=None, lame1=None, lame2=None, phase_name=None):
|
|
1123
|
+
def isotropic(cls, E=None, nu=None, G=None, lame1=None, lame2=None, K=None, phase_name=None):
|
|
772
1124
|
"""
|
|
773
|
-
Create an isotropic stiffness tensor
|
|
774
|
-
|
|
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).
|
|
775
1130
|
|
|
776
1131
|
Parameters
|
|
777
1132
|
----------
|
|
778
|
-
E : float, None
|
|
1133
|
+
E : float or list, None
|
|
779
1134
|
Young modulus
|
|
780
|
-
nu : float, None
|
|
1135
|
+
nu : float or list, None
|
|
781
1136
|
Poisson ratio
|
|
782
|
-
|
|
1137
|
+
G : float or list, None
|
|
1138
|
+
Shear modulus
|
|
1139
|
+
lame1 : float or list, None
|
|
783
1140
|
First Lamé coefficient
|
|
784
|
-
lame2 : float, None
|
|
785
|
-
Second Lamé coefficient
|
|
1141
|
+
lame2 : float or list, None
|
|
1142
|
+
Second Lamé coefficient (alias for G)
|
|
1143
|
+
K : float or list, None
|
|
1144
|
+
Bulk modulus
|
|
786
1145
|
phase_name : str, None
|
|
787
1146
|
Name to print
|
|
788
1147
|
|
|
@@ -808,34 +1167,33 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
808
1167
|
>>> C=StiffnessTensor.isotropic(E=210e3, nu=0.28)
|
|
809
1168
|
>>> C.shear_modulus
|
|
810
1169
|
Hyperspherical function
|
|
811
|
-
Min=82031.
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
return StiffnessTensor(np.array(matrix), symmetry='isotropic', phase_name=phase_name)
|
|
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
|
|
1195
|
+
"""
|
|
1196
|
+
return ComplianceTensor.isotropic(E=E, nu=nu, G=G, lame1=lame1, lame2=lame2, K=K, phase_name=phase_name).inv()
|
|
839
1197
|
|
|
840
1198
|
@classmethod
|
|
841
1199
|
def orthotropic(cls, *, Ex, Ey, Ez, Gxy, Gxz, Gyz,
|
|
@@ -894,17 +1252,9 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
894
1252
|
\\frac{\\nu_{xy}}{E_x} = \\frac{\\nu_{yx}}{E_y}
|
|
895
1253
|
|
|
896
1254
|
"""
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
tri_sup = np.array([[1 / Ex, -nu_yx / Ey, -nu_zx / Ez, 0, 0, 0],
|
|
901
|
-
[0, 1 / Ey, -nu_zy / Ez, 0, 0, 0],
|
|
902
|
-
[0, 0, 1 / Ez, 0, 0, 0],
|
|
903
|
-
[0, 0, 0, 1 / Gyz, 0, 0],
|
|
904
|
-
[0, 0, 0, 0, 1 / Gxz, 0],
|
|
905
|
-
[0, 0, 0, 0, 0, 1 / Gxy]])
|
|
906
|
-
S = tri_sup + np.tril(tri_sup.T, -1)
|
|
907
|
-
return StiffnessTensor(np.linalg.inv(S), symmetry='orthotropic', **kwargs)
|
|
1255
|
+
return ComplianceTensor.orthotropic(Ex=Ex, Ey=Ey, Ez=Ez, Gxy=Gxy, Gxz=Gxz, Gyz=Gyz,
|
|
1256
|
+
nu_yx=nu_yx, nu_zx=nu_zx, nu_zy=nu_zy,
|
|
1257
|
+
nu_xy=nu_xy, nu_xz=nu_xz, nu_yz=nu_yz).inv()
|
|
908
1258
|
|
|
909
1259
|
@classmethod
|
|
910
1260
|
def transverse_isotropic(cls, *, Ex, Ez, Gxz, nu_yx=None, nu_xy=None, nu_zx=None, nu_xz=None, **kwargs):
|
|
@@ -973,8 +1323,8 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
973
1323
|
|
|
974
1324
|
Returns
|
|
975
1325
|
-------
|
|
976
|
-
|
|
977
|
-
|
|
1326
|
+
SymmetricSecondOrderTensor
|
|
1327
|
+
Christoffel tensor(s). if u is a list of directions, Gamma[i] is the Christoffel tensor for
|
|
978
1328
|
direction u[i].
|
|
979
1329
|
|
|
980
1330
|
See Also
|
|
@@ -983,14 +1333,24 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
983
1333
|
|
|
984
1334
|
Notes
|
|
985
1335
|
-----
|
|
986
|
-
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]_ :
|
|
987
1337
|
|
|
988
1338
|
.. math:: M_{ij} = C_{iklj}.u_k.u_l
|
|
989
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.]]
|
|
990
1349
|
"""
|
|
991
1350
|
u_vec = np.atleast_2d(u)
|
|
992
1351
|
u_vec = (u_vec.T / np.linalg.norm(u_vec, axis=1)).T
|
|
993
|
-
|
|
1352
|
+
G = np.einsum('inmj,pn,pm->pij', self.full_tensor, u_vec, u_vec)
|
|
1353
|
+
return SymmetricSecondOrderTensor(np.squeeze(G))
|
|
994
1354
|
|
|
995
1355
|
def wave_velocity(self, rho):
|
|
996
1356
|
"""
|
|
@@ -1016,7 +1376,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1016
1376
|
|
|
1017
1377
|
Notes
|
|
1018
1378
|
-----
|
|
1019
|
-
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]_.
|
|
1020
1380
|
|
|
1021
1381
|
One should double-check the units. The table below provides hints about the unit you get, depending on the units
|
|
1022
1382
|
you use for stiffness and the mass density:
|
|
@@ -1037,22 +1397,51 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1037
1397
|
|
|
1038
1398
|
References
|
|
1039
1399
|
----------
|
|
1040
|
-
.. [
|
|
1400
|
+
.. [Jaeken] J. W. Jaeken, S. Cottenier, Solving the Christoffel equation: Phase and group velocities, Computer Physics
|
|
1041
1401
|
Communications (207), 2016, https://doi.org/10.1016/j.cpc.2016.06.014.
|
|
1042
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
|
|
1043
1437
|
"""
|
|
1044
|
-
self.
|
|
1438
|
+
if self.ndim:
|
|
1439
|
+
raise ValueError('This function is not suitable for tensor array. Consider subscripting (e.g. C[0].wave_velocity()).')
|
|
1045
1440
|
def make_fun(index):
|
|
1046
1441
|
def fun(n):
|
|
1047
1442
|
Gamma = self.Christoffel_tensor(n)
|
|
1048
|
-
eig
|
|
1049
|
-
|
|
1050
|
-
eig_of_interest = np.max(eig, axis=-1)
|
|
1051
|
-
elif index == 1:
|
|
1052
|
-
eig_of_interest = np.median(eig, axis=-1)
|
|
1053
|
-
else:
|
|
1054
|
-
eig_of_interest = np.min(eig, axis=-1)
|
|
1055
|
-
return np.sqrt(eig_of_interest / rho)
|
|
1443
|
+
eig = Gamma.eigvals()
|
|
1444
|
+
return np.sqrt(eig[...,index] / rho)
|
|
1056
1445
|
|
|
1057
1446
|
return fun
|
|
1058
1447
|
|
|
@@ -1093,9 +1482,8 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1093
1482
|
key = str(material.material_id)
|
|
1094
1483
|
if material.elastic_tensor is not None:
|
|
1095
1484
|
matrix = material.elastic_tensor.ieee_format
|
|
1096
|
-
symmetry = material.symmetry.crystal_system.value
|
|
1097
1485
|
phase_name = material.formula_pretty
|
|
1098
|
-
C = StiffnessTensor(np.asarray(matrix),
|
|
1486
|
+
C = StiffnessTensor(np.asarray(matrix), phase_name=phase_name)
|
|
1099
1487
|
else:
|
|
1100
1488
|
C = None
|
|
1101
1489
|
Cdict[key] = C
|
|
@@ -1130,7 +1518,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1130
1518
|
if np.all([isinstance(a, ComplianceTensor) for a in Cs]):
|
|
1131
1519
|
Cs = [C.inv() for C in Cs]
|
|
1132
1520
|
if np.all([isinstance(a, StiffnessTensor) for a in Cs]):
|
|
1133
|
-
C_stack = np.array([C.
|
|
1521
|
+
C_stack = np.array([C.matrix() for C in Cs])
|
|
1134
1522
|
method = method.capitalize()
|
|
1135
1523
|
if method == 'Voigt':
|
|
1136
1524
|
C_avg = np.average(C_stack, weights=volume_fractions, axis=0)
|
|
@@ -1174,13 +1562,16 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1174
1562
|
.. [3] S. I. Ranganathan and M. Ostoja-Starzewski, Universal Elastic Anisotropy Index,
|
|
1175
1563
|
*Phys. Rev. Lett.*, 101(5), 055504, 2008. https://doi.org/10.1103/PhysRevLett.101.055504
|
|
1176
1564
|
"""
|
|
1177
|
-
self
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
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
|
|
1184
1575
|
return 5 * Gv / Gr + Kv / Kr - 6
|
|
1185
1576
|
|
|
1186
1577
|
def Zener_ratio(self, tol=1e-4):
|
|
@@ -1245,7 +1636,6 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1245
1636
|
0. ]
|
|
1246
1637
|
[ 25.98076211 -25.98076211 0. 0. 0.
|
|
1247
1638
|
65. ]]
|
|
1248
|
-
Symmetry: cubic
|
|
1249
1639
|
|
|
1250
1640
|
Still, we have
|
|
1251
1641
|
>>> C_rot.Zener_ratio()
|
|
@@ -1274,7 +1664,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1274
1664
|
from pymatgen.analysis.elasticity import elastic as matgenElast
|
|
1275
1665
|
except ImportError:
|
|
1276
1666
|
raise ModuleNotFoundError('pymatgen module is required for this function.')
|
|
1277
|
-
return matgenElast.ElasticTensor(self.full_tensor
|
|
1667
|
+
return matgenElast.ElasticTensor(self.full_tensor)
|
|
1278
1668
|
|
|
1279
1669
|
def to_Kelvin(self):
|
|
1280
1670
|
"""
|
|
@@ -1292,7 +1682,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1292
1682
|
|
|
1293
1683
|
Notes
|
|
1294
1684
|
-----
|
|
1295
|
-
This mapping convention is defined as follows [
|
|
1685
|
+
This mapping convention is defined as follows [Helbig]_:
|
|
1296
1686
|
|
|
1297
1687
|
.. math::
|
|
1298
1688
|
|
|
@@ -1308,11 +1698,32 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1308
1698
|
|
|
1309
1699
|
References
|
|
1310
1700
|
----------
|
|
1311
|
-
.. [
|
|
1312
|
-
doi: 10.1111/j.1365-2478.2011.01049.x
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
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
|
|
1316
1727
|
|
|
1317
1728
|
def eig(self):
|
|
1318
1729
|
"""
|
|
@@ -1335,9 +1746,9 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1335
1746
|
|
|
1336
1747
|
Notes
|
|
1337
1748
|
-----
|
|
1338
|
-
The definition for eigenstiffnesses and the eigenstrains are introduced in [
|
|
1749
|
+
The definition for eigenstiffnesses and the eigenstrains are introduced in [Helbig]_.
|
|
1339
1750
|
"""
|
|
1340
|
-
return np.linalg.eigh(self.
|
|
1751
|
+
return np.linalg.eigh(self._matrix)
|
|
1341
1752
|
|
|
1342
1753
|
@property
|
|
1343
1754
|
def eig_stiffnesses(self):
|
|
@@ -1354,8 +1765,22 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1354
1765
|
eig : returns the eigenstiffnesses and the eigenstrains
|
|
1355
1766
|
eig_strains : returns the eigenstrains only
|
|
1356
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]_.
|
|
1357
1782
|
"""
|
|
1358
|
-
return np.linalg.eigvalsh(self.
|
|
1783
|
+
return np.linalg.eigvalsh(self._matrix)
|
|
1359
1784
|
|
|
1360
1785
|
@property
|
|
1361
1786
|
def eig_strains(self):
|
|
@@ -1409,10 +1834,7 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1409
1834
|
--------
|
|
1410
1835
|
to_Kelvin : return the components as a (6,6) matrix following the Kelvin convention
|
|
1411
1836
|
"""
|
|
1412
|
-
|
|
1413
|
-
t = cls(matrix / kelvin_mapping.matrix, **kwargs)
|
|
1414
|
-
t._matrix *= t.mapping.matrix
|
|
1415
|
-
return t
|
|
1837
|
+
return cls(matrix, mapping=kelvin_mapping, **kwargs)
|
|
1416
1838
|
|
|
1417
1839
|
def eig_stiffnesses_multiplicity(self, tol=1e-4):
|
|
1418
1840
|
"""
|
|
@@ -1534,9 +1956,9 @@ class StiffnessTensor(SymmetricFourthOrderTensor):
|
|
|
1534
1956
|
8.1901353 ]
|
|
1535
1957
|
[-20.34233446 0.99714278 19.34519167 -6.52548033 8.1901353
|
|
1536
1958
|
39.41409344]]
|
|
1537
|
-
Symmetry: cubic
|
|
1538
1959
|
|
|
1539
|
-
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
|
+
|
|
1540
1962
|
>>> C_rotated.is_cubic()
|
|
1541
1963
|
True
|
|
1542
1964
|
|
|
@@ -1572,12 +1994,127 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1572
1994
|
"""
|
|
1573
1995
|
Class for manipulating compliance tensors
|
|
1574
1996
|
"""
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1997
|
+
_tensor_name = 'Compliance'
|
|
1998
|
+
_C11_C12_factor = 2.0
|
|
1999
|
+
_component_prefix = 'S'
|
|
2000
|
+
_C46_C56_factor = 2.0
|
|
1579
2001
|
|
|
1580
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
|
+
"""
|
|
1581
2118
|
super().__init__(C, check_positive_definite=check_positive_definite, mapping=mapping, **kwargs)
|
|
1582
2119
|
self.mapping_name = 'Voigt'
|
|
1583
2120
|
|
|
@@ -1600,21 +2137,15 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1600
2137
|
Reciprocal tensor
|
|
1601
2138
|
"""
|
|
1602
2139
|
S = np.linalg.inv(self._matrix)
|
|
1603
|
-
|
|
2140
|
+
t = StiffnessTensor(S, mapping=kelvin_mapping, phase_name=self.phase_name)
|
|
2141
|
+
t.mapping = self.mapping.mapping_inverse
|
|
2142
|
+
return t
|
|
1604
2143
|
|
|
1605
2144
|
def Reuss_average(self, axis=None):
|
|
1606
2145
|
if self.ndim:
|
|
1607
2146
|
return self.mean(axis=axis)
|
|
1608
2147
|
else:
|
|
1609
|
-
|
|
1610
|
-
A = s[0, 0] + s[1, 1] + s[2, 2]
|
|
1611
|
-
B = s[0, 1] + s[0, 2] + s[1, 2]
|
|
1612
|
-
C = s[3, 3] + s[4, 4] + s[5, 5]
|
|
1613
|
-
S11 = 1 / 5 * A + 2 / 15 * B + 1 / 15 * C
|
|
1614
|
-
S12 = 1 / 15 * A + 4 / 15 * B - 1 / 30 * C
|
|
1615
|
-
S44 = 4 / 15 * (A - B) + 1 / 5 * C
|
|
1616
|
-
mat = _isotropic_matrix(S11, S12, S44)
|
|
1617
|
-
return ComplianceTensor(mat, symmetry='isotropic', phase_name=self.phase_name)
|
|
2148
|
+
return self.infinite_random_average()
|
|
1618
2149
|
|
|
1619
2150
|
def Voigt_average(self, axis=None):
|
|
1620
2151
|
return self.inv().Voigt_average(axis=axis).inv()
|
|
@@ -1623,12 +2154,82 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1623
2154
|
return self.inv().Hill_average(axis=axis).inv()
|
|
1624
2155
|
|
|
1625
2156
|
@classmethod
|
|
1626
|
-
def isotropic(cls, E=None, nu=None, lame1=None, lame2=None, phase_name=None):
|
|
1627
|
-
|
|
2157
|
+
def isotropic(cls, E=None, nu=None, G=None, lame1=None, lame2=None, K=None, phase_name=None):
|
|
2158
|
+
if lame2 is not None:
|
|
2159
|
+
if G is None:
|
|
2160
|
+
G = lame2
|
|
2161
|
+
else:
|
|
2162
|
+
raise ValueError('G and lame2 cannot be provided together.')
|
|
2163
|
+
n_specified = sum(v is not None for v in [E, nu, lame1, G, K])
|
|
2164
|
+
if n_specified != 2:
|
|
2165
|
+
raise ValueError("Exactly two values are required among E, nu, G, K, lame1 and lame2.")
|
|
2166
|
+
if K is not None:
|
|
2167
|
+
K = np.asarray(K)
|
|
2168
|
+
if E is not None:
|
|
2169
|
+
E = np.asarray(E, dtype=float)
|
|
2170
|
+
G = 3 * K * E / (9 * K - E)
|
|
2171
|
+
nu = (3 * K - E) / 6 / K
|
|
2172
|
+
elif lame1 is not None:
|
|
2173
|
+
lame1 = np.asarray(lame1, dtype=float)
|
|
2174
|
+
E = 9 * K * (K - lame1) / (3 * K -lame1)
|
|
2175
|
+
G= 3 * (K - lame1) / 2
|
|
2176
|
+
nu = lame1 / (3*K-lame1)
|
|
2177
|
+
elif G is not None:
|
|
2178
|
+
G = np.asarray(G, dtype=float)
|
|
2179
|
+
E = 9 * K * G / (3 * K + G)
|
|
2180
|
+
nu = (3 * K - 2 * G) / 2 / (3 * K + G)
|
|
2181
|
+
elif nu is not None:
|
|
2182
|
+
nu = np.asarray(nu, dtype=float)
|
|
2183
|
+
E = 3 * K * (1 - 2 * nu)
|
|
2184
|
+
G = E / 2 / (1 + nu)
|
|
2185
|
+
elif E is not None:
|
|
2186
|
+
E = np.asarray(E, dtype=float)
|
|
2187
|
+
if lame1 is not None:
|
|
2188
|
+
lame1 = np.asarray(lame1, dtype=float)
|
|
2189
|
+
R = np.sqrt(E**2 + 9*lame1**2 + 2*E*lame1)
|
|
2190
|
+
G = (E - 3 * lame1 + R) / 4
|
|
2191
|
+
nu = 2 * lame1 / (E + lame1 + R)
|
|
2192
|
+
elif G is not None:
|
|
2193
|
+
G = np.asarray(G, dtype=float)
|
|
2194
|
+
nu = E / 2 / G - 1
|
|
2195
|
+
elif nu is not None:
|
|
2196
|
+
nu = np.asarray(nu, dtype=float)
|
|
2197
|
+
G = E / 2 / (1 + nu)
|
|
2198
|
+
elif lame1 is not None:
|
|
2199
|
+
lame1 = np.asarray(lame1, dtype=float)
|
|
2200
|
+
if G is not None:
|
|
2201
|
+
G = np.asarray(G, dtype=float)
|
|
2202
|
+
E = G * (3 * lame1 + 2 * G) / (lame1 + G)
|
|
2203
|
+
nu = lame1 / 2 / (lame1 + G)
|
|
2204
|
+
elif nu is not None:
|
|
2205
|
+
nu = np.asarray(nu, dtype=float)
|
|
2206
|
+
E = lame1 * ( 1 + nu) * (1 - 2 * nu) / nu
|
|
2207
|
+
G = lame1 * (1 - 2 * nu) / 2 / nu
|
|
2208
|
+
elif (nu is not None) and (G is not None):
|
|
2209
|
+
nu = np.asarray(nu, dtype=float)
|
|
2210
|
+
G = np.asarray(G)
|
|
2211
|
+
E = 2 * G * (1 + nu)
|
|
2212
|
+
S11 = 1/E
|
|
2213
|
+
S12 = -nu/E
|
|
2214
|
+
S44 = 1 / G
|
|
2215
|
+
S_mat = _isotropic_matrix(S11, S12, S44)
|
|
2216
|
+
return ComplianceTensor(S_mat, phase_name=phase_name)
|
|
1628
2217
|
|
|
1629
2218
|
@classmethod
|
|
1630
|
-
def orthotropic(cls,
|
|
1631
|
-
|
|
2219
|
+
def orthotropic(cls, *, Ex, Ey, Ez, Gxy, Gxz, Gyz,
|
|
2220
|
+
nu_yx=None, nu_zx=None, nu_zy=None,
|
|
2221
|
+
nu_xy=None, nu_xz=None, nu_yz=None, **kwargs):
|
|
2222
|
+
nu_yx = _switch_poisson_ratios(nu_xy, nu_yx, Ex, Ey,'xy')
|
|
2223
|
+
nu_zx = _switch_poisson_ratios(nu_xz, nu_zx, Ex, Ez,'xz')
|
|
2224
|
+
nu_zy = _switch_poisson_ratios(nu_yz, nu_zy, Ey, Ez,'yz')
|
|
2225
|
+
tri_sup = np.array([[1 / Ex, -nu_yx / Ey, -nu_zx / Ez, 0, 0, 0],
|
|
2226
|
+
[0, 1 / Ey, -nu_zy / Ez, 0, 0, 0],
|
|
2227
|
+
[0, 0, 1 / Ez, 0, 0, 0],
|
|
2228
|
+
[0, 0, 0, 1 / Gyz, 0, 0],
|
|
2229
|
+
[0, 0, 0, 0, 1 / Gxz, 0],
|
|
2230
|
+
[0, 0, 0, 0, 0, 1 / Gxy]])
|
|
2231
|
+
S = tri_sup + np.tril(tri_sup.T, -1)
|
|
2232
|
+
return ComplianceTensor(S, **kwargs)
|
|
1632
2233
|
|
|
1633
2234
|
@classmethod
|
|
1634
2235
|
def transverse_isotropic(cls, *args, **kwargs):
|
|
@@ -1671,7 +2272,7 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1671
2272
|
from pymatgen.analysis.elasticity import elastic as matgenElast
|
|
1672
2273
|
except ImportError:
|
|
1673
2274
|
raise ModuleNotFoundError('pymatgen module is required for this function.')
|
|
1674
|
-
return matgenElast.ComplianceTensor(self.full_tensor
|
|
2275
|
+
return matgenElast.ComplianceTensor(self.full_tensor)
|
|
1675
2276
|
|
|
1676
2277
|
def eig(self):
|
|
1677
2278
|
"""
|
|
@@ -1694,7 +2295,7 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1694
2295
|
|
|
1695
2296
|
Notes
|
|
1696
2297
|
-----
|
|
1697
|
-
The definition for eigencompliances and the eigenstresses are introduced in [
|
|
2298
|
+
The definition for eigencompliances and the eigenstresses are introduced in [Helbig]_.
|
|
1698
2299
|
"""
|
|
1699
2300
|
return np.linalg.eigh(self.to_Kelvin())
|
|
1700
2301
|
|
|
@@ -1745,4 +2346,232 @@ class ComplianceTensor(StiffnessTensor):
|
|
|
1745
2346
|
--------
|
|
1746
2347
|
eig_compliances : compute the eigencompliances from the Kelvin's matrix of compliance
|
|
1747
2348
|
"""
|
|
1748
|
-
return 1/self.eig_compliances
|
|
2349
|
+
return 1/self.eig_compliances
|
|
2350
|
+
|
|
2351
|
+
@property
|
|
2352
|
+
def lame1(self):
|
|
2353
|
+
return self.inv().lame1
|
|
2354
|
+
|
|
2355
|
+
@property
|
|
2356
|
+
def lame2(self):
|
|
2357
|
+
return self.inv().lame2
|
|
2358
|
+
|
|
2359
|
+
|
|
2360
|
+
@classmethod
|
|
2361
|
+
def hexagonal(cls, *, S11=0., S12=0., S13=0., S33=0., S44=0., phase_name=None):
|
|
2362
|
+
"""
|
|
2363
|
+
Create a fourth-order tensor from hexagonal symmetry.
|
|
2364
|
+
|
|
2365
|
+
Parameters
|
|
2366
|
+
----------
|
|
2367
|
+
S11, S12 , S13, S33, S44 : float
|
|
2368
|
+
Components of the tensor, using the Voigt notation
|
|
2369
|
+
phase_name : str, optional
|
|
2370
|
+
Phase name to display
|
|
2371
|
+
Returns
|
|
2372
|
+
-------
|
|
2373
|
+
FourthOrderTensor
|
|
2374
|
+
|
|
2375
|
+
See Also
|
|
2376
|
+
--------
|
|
2377
|
+
transverse_isotropic : creates a transverse-isotropic tensor from engineering parameters
|
|
2378
|
+
cubic : create a tensor from cubic symmetry
|
|
2379
|
+
tetragonal : create a tensor from tetragonal symmetry
|
|
2380
|
+
"""
|
|
2381
|
+
return cls._fromCrystalSymmetry(symmetry='hexagonal', S11=S11, S12=S12, S13=S13, S33=S33, S44=S44,
|
|
2382
|
+
phase_name=phase_name, prefix='S')
|
|
2383
|
+
|
|
2384
|
+
@classmethod
|
|
2385
|
+
def trigonal(cls, *, S11=0., S12=0., S13=0., S14=0., S33=0., S44=0., S15=0., phase_name=None):
|
|
2386
|
+
"""
|
|
2387
|
+
Create a fourth-order tensor from trigonal symmetry.
|
|
2388
|
+
|
|
2389
|
+
Parameters
|
|
2390
|
+
----------
|
|
2391
|
+
S11, S12, S13, S14, S33, S44 : float
|
|
2392
|
+
Components of the tensor, using the Voigt notation
|
|
2393
|
+
S15 : float, optional
|
|
2394
|
+
S15 component of the tensor, only used for point groups 3 and -3.
|
|
2395
|
+
phase_name : str, optional
|
|
2396
|
+
Phase name to display
|
|
2397
|
+
Returns
|
|
2398
|
+
-------
|
|
2399
|
+
FourthOrderTensor
|
|
2400
|
+
|
|
2401
|
+
See Also
|
|
2402
|
+
--------
|
|
2403
|
+
tetragonal : create a tensor from tetragonal symmetry
|
|
2404
|
+
orthorhombic : create a tensor from orthorhombic symmetry
|
|
2405
|
+
"""
|
|
2406
|
+
return cls._fromCrystalSymmetry(symmetry='trigonal', point_group='3',
|
|
2407
|
+
S11=S11, S12=S12, S13=S13, S14=S14, S15=S15,
|
|
2408
|
+
S33=S33, S44=S44, phase_name=phase_name, prefix='S')
|
|
2409
|
+
|
|
2410
|
+
@classmethod
|
|
2411
|
+
def tetragonal(cls, *, S11=0., S12=0., S13=0., S33=0., S44=0., S16=0., S66=0., phase_name=None):
|
|
2412
|
+
"""
|
|
2413
|
+
Create a fourth-order tensor from tetragonal symmetry.
|
|
2414
|
+
|
|
2415
|
+
Parameters
|
|
2416
|
+
----------
|
|
2417
|
+
S11, S12, S13, S33, S44, S66 : float
|
|
2418
|
+
Components of the tensor, using the Voigt notation
|
|
2419
|
+
S16 : float, optional
|
|
2420
|
+
S16 component in Voigt notation (for point groups 4, -4 and 4/m only)
|
|
2421
|
+
phase_name : str, optional
|
|
2422
|
+
Phase name to display
|
|
2423
|
+
|
|
2424
|
+
Returns
|
|
2425
|
+
-------
|
|
2426
|
+
FourthOrderTensor
|
|
2427
|
+
|
|
2428
|
+
See Also
|
|
2429
|
+
--------
|
|
2430
|
+
trigonal : create a tensor from trigonal symmetry
|
|
2431
|
+
orthorhombic : create a tensor from orthorhombic symmetry
|
|
2432
|
+
"""
|
|
2433
|
+
return cls._fromCrystalSymmetry(symmetry='tetragonal', point_group='4',
|
|
2434
|
+
S11=S11, S12=S12, S13=S13, S16=S16,
|
|
2435
|
+
S33=S33, S44=S44, S66=S66, phase_name=phase_name, prefix='S')
|
|
2436
|
+
|
|
2437
|
+
@classmethod
|
|
2438
|
+
def cubic(cls, *, S11=0., S12=0., S44=0., phase_name=None):
|
|
2439
|
+
"""
|
|
2440
|
+
Create a fourth-order tensor from cubic symmetry.
|
|
2441
|
+
|
|
2442
|
+
Parameters
|
|
2443
|
+
----------
|
|
2444
|
+
S11 , S12, S44 : float
|
|
2445
|
+
phase_name : str, optional
|
|
2446
|
+
Phase name to display
|
|
2447
|
+
|
|
2448
|
+
Returns
|
|
2449
|
+
-------
|
|
2450
|
+
StiffnessTensor
|
|
2451
|
+
|
|
2452
|
+
See Also
|
|
2453
|
+
--------
|
|
2454
|
+
hexagonal : create a tensor from hexagonal symmetry
|
|
2455
|
+
orthorhombic : create a tensor from orthorhombic symmetry
|
|
2456
|
+
"""
|
|
2457
|
+
return cls._fromCrystalSymmetry(symmetry='cubic', S11=S11, S12=S12, S44=S44, phase_name=phase_name, prefix='S')
|
|
2458
|
+
|
|
2459
|
+
@classmethod
|
|
2460
|
+
def orthorhombic(cls, *, S11=0., S12=0., S13=0., S22=0., S23=0., S33=0., S44=0., S55=0., S66=0., phase_name=None):
|
|
2461
|
+
"""
|
|
2462
|
+
Create a fourth-order tensor from orthorhombic symmetry.
|
|
2463
|
+
|
|
2464
|
+
Parameters
|
|
2465
|
+
----------
|
|
2466
|
+
S11, S12, S13, S22, S23, S33, S44, S55, S66 : float
|
|
2467
|
+
Components of the tensor, using the Voigt notation
|
|
2468
|
+
phase_name : str, optional
|
|
2469
|
+
Phase name to display
|
|
2470
|
+
|
|
2471
|
+
Returns
|
|
2472
|
+
-------
|
|
2473
|
+
FourthOrderTensor
|
|
2474
|
+
|
|
2475
|
+
See Also
|
|
2476
|
+
--------
|
|
2477
|
+
monoclinic : create a tensor from monoclinic symmetry
|
|
2478
|
+
orthorhombic : create a tensor from orthorhombic symmetry
|
|
2479
|
+
"""
|
|
2480
|
+
return cls._fromCrystalSymmetry(symmetry='orthorhombic',
|
|
2481
|
+
S11=S11, S12=S12, S13=S13, S22=S22, S23=S23, S33=S33, S44=S44, S55=S55, S66=S66,
|
|
2482
|
+
phase_name=phase_name, prefix='S')
|
|
2483
|
+
|
|
2484
|
+
@classmethod
|
|
2485
|
+
def monoclinic(cls, *, S11=0., S12=0., S13=0., S22=0., S23=0., S33=0., S44=0., S55=0., S66=0.,
|
|
2486
|
+
S15=None, S25=None, S35=None, S46=None,
|
|
2487
|
+
S16=None, S26=None, S36=None, S45=None,
|
|
2488
|
+
phase_name=None):
|
|
2489
|
+
"""
|
|
2490
|
+
Create a fourth-order tensor from monoclinic symmetry. It automatically detects whether the components are given
|
|
2491
|
+
according to the Y or Z diad, depending on the input arguments.
|
|
2492
|
+
|
|
2493
|
+
For Diad || y, S15, S25, S35 and S46 must be provided.
|
|
2494
|
+
For Diad || z, S16, S26, S36 and S45 must be provided.
|
|
2495
|
+
|
|
2496
|
+
Parameters
|
|
2497
|
+
----------
|
|
2498
|
+
S11, S12 , S13, S22, S23, S33, S44, S55, S66 : float
|
|
2499
|
+
Components of the tensor, using the Voigt notation
|
|
2500
|
+
S15 : float, optional
|
|
2501
|
+
S15 component of the tensor (if Diad || y)
|
|
2502
|
+
S25 : float, optional
|
|
2503
|
+
S25 component of the tensor (if Diad || y)
|
|
2504
|
+
S35 : float, optional
|
|
2505
|
+
S35 component of the tensor (if Diad || y)
|
|
2506
|
+
S46 : float, optional
|
|
2507
|
+
S46 component of the tensor (if Diad || y)
|
|
2508
|
+
S16 : float, optional
|
|
2509
|
+
S16 component of the tensor (if Diad || z)
|
|
2510
|
+
S26 : float, optional
|
|
2511
|
+
S26 component of the tensor (if Diad || z)
|
|
2512
|
+
S36 : float, optional
|
|
2513
|
+
S36 component of the tensor (if Diad || z)
|
|
2514
|
+
S45 : float, optional
|
|
2515
|
+
S45 component of the tensor (if Diad || z)
|
|
2516
|
+
phase_name : str, optional
|
|
2517
|
+
Name to display
|
|
2518
|
+
|
|
2519
|
+
Returns
|
|
2520
|
+
-------
|
|
2521
|
+
FourthOrderTensor
|
|
2522
|
+
|
|
2523
|
+
See Also
|
|
2524
|
+
--------
|
|
2525
|
+
triclinic : create a tensor from triclinic symmetry
|
|
2526
|
+
orthorhombic : create a tensor from orthorhombic symmetry
|
|
2527
|
+
"""
|
|
2528
|
+
diad_y = not (None in (S15, S25, S35, S46))
|
|
2529
|
+
diad_z = not (None in (S16, S26, S36, S45))
|
|
2530
|
+
if diad_y and diad_z:
|
|
2531
|
+
raise KeyError('Ambiguous diad. Provide either S15, S25, S35 and S46; or S16, S26, S36 and S45')
|
|
2532
|
+
elif diad_y:
|
|
2533
|
+
return cls._fromCrystalSymmetry(symmetry='monoclinic', diad='y',
|
|
2534
|
+
S11=S11, S12=S12, S13=S13, S22=S22, S23=S23, S33=S33, S44=S44, S55=S55,
|
|
2535
|
+
S66=S66,
|
|
2536
|
+
S15=S15, S25=S25, S35=S35, S46=S46, phase_name=phase_name, prefix='S')
|
|
2537
|
+
elif diad_z:
|
|
2538
|
+
return cls._fromCrystalSymmetry(symmetry='monoclinic', diad='z',
|
|
2539
|
+
S11=S11, S12=S12, S13=S13, S22=S22, S23=S23, S33=S33, S44=S44, S55=S55,
|
|
2540
|
+
S66=S66,
|
|
2541
|
+
S16=S16, S26=S26, S36=S36, S45=S45, phase_name=phase_name, prefix='S')
|
|
2542
|
+
else:
|
|
2543
|
+
raise KeyError('For monoclinic symmetry, one should provide either S15, S25, S35 and S46, '
|
|
2544
|
+
'or S16, S26, S36 and S45.')
|
|
2545
|
+
|
|
2546
|
+
@classmethod
|
|
2547
|
+
def triclinic(cls, S11=0., S12=0., S13=0., S14=0., S15=0., S16=0.,
|
|
2548
|
+
S22=0., S23=0., C24=0., S25=0., S26=0.,
|
|
2549
|
+
S33=0., C34=0., S35=0., S36=0.,
|
|
2550
|
+
S44=0., S45=0., S46=0.,
|
|
2551
|
+
S55=0., C56=0.,
|
|
2552
|
+
S66=0., phase_name=None):
|
|
2553
|
+
"""
|
|
2554
|
+
|
|
2555
|
+
Parameters
|
|
2556
|
+
----------
|
|
2557
|
+
S11 , S12 , S13 , S14 , S15 , S16 , S22 , S23 , C24 , S25 , S26 , S33 , C34 , S35 , S36 , S44 , S45 , S46 , S55 , C56 , S66 : float
|
|
2558
|
+
Components of the tensor
|
|
2559
|
+
phase_name : str, optional
|
|
2560
|
+
Name to display
|
|
2561
|
+
|
|
2562
|
+
Returns
|
|
2563
|
+
-------
|
|
2564
|
+
FourthOrderTensor
|
|
2565
|
+
|
|
2566
|
+
See Also
|
|
2567
|
+
--------
|
|
2568
|
+
monoclinic : create a tensor from monoclinic symmetry
|
|
2569
|
+
orthorhombic : create a tensor from orthorhombic symmetry
|
|
2570
|
+
"""
|
|
2571
|
+
matrix = np.array([[S11, S12, S13, S14, S15, S16],
|
|
2572
|
+
[S12, S22, S23, C24, S25, S26],
|
|
2573
|
+
[S13, S23, S33, C34, S35, S36],
|
|
2574
|
+
[S14, C24, C34, S44, S45, S46],
|
|
2575
|
+
[S15, S25, S35, S45, S55, C56],
|
|
2576
|
+
[S16, S26, S36, S46, C56, S66]])
|
|
2577
|
+
return cls(matrix, phase_name=phase_name)
|