elasticipy 2.9.0__py3-none-any.whl → 3.0.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,13 +1,24 @@
1
1
  import numpy as np
2
2
  import re
3
3
 
4
- from Elasticipy.SecondOrderTensor import SymmetricSecondOrderTensor, rotation_to_matrix, is_orix_rotation
4
+ from Elasticipy.SecondOrderTensor import SymmetricSecondOrderTensor, rotation_to_matrix, is_orix_rotation, \
5
+ SecondOrderTensor, ALPHABET
6
+ from Elasticipy.SecondOrderTensor import _orientation_shape, _is_single_rotation
5
7
  from Elasticipy.StressStrainTensors import StrainTensor, StressTensor
6
8
  from Elasticipy.SphericalFunction import SphericalFunction, HyperSphericalFunction
7
9
  from scipy.spatial.transform import Rotation
8
10
  from Elasticipy.CrystalSymmetries import SYMMETRIES
9
11
  from copy import deepcopy
10
12
 
13
+ a = np.sqrt(2)
14
+ _voigt_to_kelvin_matrix = np.array([[1, 1, 1, a, a, a],
15
+ [1, 1, 1, a, a, a],
16
+ [1, 1, 1, a, a, a],
17
+ [a, a, a, 2, 2, 2],
18
+ [a, a, a, 2, 2, 2],
19
+ [a, a, a, 2, 2, 2],])
20
+
21
+
11
22
  def _parse_tensor_components(prefix, **kwargs):
12
23
  pattern = r'^{}(\d{{2}})$'.format(prefix)
13
24
  value = dict()
@@ -67,7 +78,7 @@ def _compute_unit_strain_along_direction(S, m, n, direction='longitudinal'):
67
78
  if direction == 'transverse':
68
79
  ein_str = 'ijkl,pi,pj,pk,pl->p'
69
80
  return np.einsum(ein_str, S.full_tensor(), m, m, n, n)
70
- elif direction =='longitudinal':
81
+ elif direction == 'longitudinal':
71
82
  ein_str = 'ijkl,pi,pk,pj,pl->p'
72
83
  return np.einsum(ein_str, S.full_tensor(), m, m, n, n)
73
84
  else:
@@ -83,6 +94,7 @@ def _isotropic_matrix(C11, C12, C44):
83
94
  [0, 0, 0, 0, C44, 0],
84
95
  [0, 0, 0, 0, 0, C44]])
85
96
 
97
+
86
98
  def _check_definite_positive(mat):
87
99
  try:
88
100
  np.linalg.cholesky(mat)
@@ -91,20 +103,12 @@ def _check_definite_positive(mat):
91
103
  raise ValueError('The input matrix is not definite positive (eigenvalues: {})'.format(eigen_val))
92
104
 
93
105
 
94
- def _is_single_rotation(rotation):
95
- if isinstance(rotation, Rotation):
96
- return rotation.single
97
- elif is_orix_rotation(rotation):
98
- return rotation.size == 1
99
- else:
100
- raise TypeError('The input argument must be of class scipy.transform.Rotation or '
101
- 'orix.quaternion.rotation.Rotation')
102
-
103
106
  def _rotate_tensor(full_tensor, rotation):
104
107
  rot_mat = rotation_to_matrix(rotation)
105
- str_ein = '...im,...jn,...ko,...lp,mnop->...ijkl'
108
+ str_ein = '...im,...jn,...ko,...lp,mnop->...ijkl'
106
109
  return np.einsum(str_ein, rot_mat, rot_mat, rot_mat, rot_mat, full_tensor)
107
110
 
111
+
108
112
  class SymmetricTensor:
109
113
  """
110
114
  Template class for manipulating symmetric fourth-order tensors.
@@ -145,9 +149,9 @@ class SymmetricTensor:
145
149
  Whether to check or not that the input matrix is definite positive
146
150
  """
147
151
  M = np.asarray(M)
148
- if M.shape == (6,6):
152
+ if M.shape == (6, 6):
149
153
  matrix = M
150
- elif M.shape == (3,3,3,3):
154
+ elif M.shape == (3, 3, 3, 3):
151
155
  matrix = self._full_to_matrix(M)
152
156
  else:
153
157
  raise ValueError('The input matrix must of shape (6,6)')
@@ -161,12 +165,13 @@ class SymmetricTensor:
161
165
  self.symmetry = symmetry
162
166
  self.orientations = orientations
163
167
 
164
- for i in range(0,6):
165
- for j in range(0,6):
168
+ for i in range(0, 6):
169
+ for j in range(0, 6):
166
170
  def getter(obj, I=i, J=j):
167
171
  return obj.matrix[I, J]
172
+
168
173
  getter.__doc__ = f"Returns the ({i + 1},{j + 1}) component of the {self.tensor_name} matrix."
169
- component_name = 'C{}{}'.format(i+1, j+1)
174
+ component_name = 'C{}{}'.format(i + 1, j + 1)
170
175
  setattr(self.__class__, component_name, property(getter)) # Dynamically create the property
171
176
 
172
177
  def __repr__(self):
@@ -177,14 +182,37 @@ class SymmetricTensor:
177
182
  print_symmetry = '\nSymmetry: {}'.format(self.symmetry)
178
183
  msg = heading + self.matrix.__str__() + print_symmetry
179
184
  if self.orientations is not None:
180
- msg = msg + '\n{} orientations'.format(len(self))
185
+ shape = self.shape
186
+ if len(shape) == 1:
187
+ msg = msg + '\n{} orientations'.format(shape[0])
188
+ else:
189
+ msg = msg + '\n{} orientations'.format(shape)
181
190
  return msg
182
191
 
183
192
  def __len__(self):
184
- if self.orientations is None:
193
+ o = self.orientations
194
+ if o is None:
185
195
  return 1
186
196
  else:
187
- return len(self.orientations)
197
+ if is_orix_rotation(o):
198
+ return o.shape[0]
199
+ else:
200
+ return len(o)
201
+
202
+ @property
203
+ def shape(self):
204
+ """
205
+ Return the shape of the tensor array
206
+ Returns
207
+ -------
208
+ tuple
209
+ Shape of the tensor array
210
+ """
211
+ o = self.orientations
212
+ if o is None:
213
+ return None
214
+ else:
215
+ return _orientation_shape(o)
188
216
 
189
217
  def full_tensor(self):
190
218
  """
@@ -204,6 +232,26 @@ class SymmetricTensor:
204
232
  else:
205
233
  return _rotate_tensor(m, self.orientations)
206
234
 
235
+ def flatten(self):
236
+ """
237
+ Flatten the tensor
238
+
239
+ If the tensor has (m,n,o...,r) orientations, the flattened tensor will have m*n*o*...*r orientations
240
+
241
+ Returns
242
+ -------
243
+ SymmetricTensor
244
+ Flattened tensor
245
+ """
246
+ tensor_flat = self._unrotate()
247
+ o = self.orientations
248
+ if is_orix_rotation(o):
249
+ o_flat = o.flatten()
250
+ else:
251
+ o_flat = o
252
+ tensor_flat.orientations = o_flat
253
+ return tensor_flat
254
+
207
255
  @classmethod
208
256
  def _full_to_matrix(cls, full_tensor):
209
257
  ij, kl = np.indices((6, 6))
@@ -232,6 +280,41 @@ class SymmetricTensor:
232
280
  else:
233
281
  raise ValueError('The rotation to apply must be single')
234
282
 
283
+ @property
284
+ def ndim(self):
285
+ """
286
+ Returns the dimensionality of the tensor (number of dimensions in the orientation array)
287
+
288
+ Returns
289
+ -------
290
+ int
291
+ Number of dimensions
292
+ """
293
+ shape = self.shape
294
+ if shape:
295
+ return len(shape)
296
+ else:
297
+ return 0
298
+
299
+ def mean(self, axis=None):
300
+ """
301
+ Compute the mean value of the tensor T, considering the orientations
302
+
303
+ Parameters
304
+ ----------
305
+ axis : int or list of int or tuple of int, optional
306
+ axis along which to compute the mean. If None, the mean is computed on the flattened tensor
307
+
308
+ Returns
309
+ -------
310
+ numpy.ndarray
311
+ If no axis is given, the result will be of shape (3,3,3,3).
312
+ Otherwise, if T.ndim=m, and len(axis)=n, the returned value will be of shape (...,3,3,3,3), with ndim=m-n+4
313
+ """
314
+ if axis is None:
315
+ axis = tuple([i for i in range(self.ndim)])
316
+ return np.mean(self.full_tensor(), axis=axis)
317
+
235
318
  def _unrotate(self):
236
319
  unrotated_tensor = deepcopy(self)
237
320
  unrotated_tensor.orientations = None
@@ -264,19 +347,54 @@ class SymmetricTensor:
264
347
  if isinstance(other, SymmetricSecondOrderTensor):
265
348
  return SymmetricSecondOrderTensor(self * other.matrix)
266
349
  elif isinstance(other, np.ndarray):
267
- if other.shape[-2:] == (3, 3):
268
- if self.orientations is None:
269
- return np.einsum('ijkl,...kl->...ij', self.full_tensor(), other)
270
- else:
271
- return np.einsum('qijkl,...kl->q...ij', self.full_tensor(), other)
350
+ if other.shape == (3, 3):
351
+ # other is a single tensor
352
+ matrix = np.einsum('...ijkl,kl->...ij', self.full_tensor(), other)
353
+ return SecondOrderTensor(matrix)
354
+ elif self.shape is None:
355
+ # other is an array, but self is single
356
+ matrix = np.einsum('ijkl,...kl->...ij', self.full_tensor(), other)
357
+ return SecondOrderTensor(matrix)
358
+ elif (self.ndim >= (other.ndim - 2)) and self.shape[-other.ndim+2:]==other.shape[:-2]:
359
+ # self.shape==(m,n,o,p) and other.shape==(o,p,3,3)
360
+ indices_0 = ALPHABET[:self.ndim]
361
+ indices_1 = indices_0[-other.ndim+2:]
362
+ ein_str = indices_0 + 'IJKL,' + indices_1 + 'KL->' + indices_0 + 'IJ'
363
+ matrix = np.einsum(ein_str, self.full_tensor(), other)
364
+ return SecondOrderTensor(matrix)
365
+ elif self.shape == other.shape[:-2]:
366
+ # other and self are arrays of the same shape
367
+ matrix = np.einsum('...ijkl,...kl->...ij', self.full_tensor(), other)
368
+ return SecondOrderTensor(matrix)
369
+ else:
370
+ raise ValueError('The arrays to multiply could not be broadcast with shapes {} and {}'.format(self.shape, other.shape[:-2]))
272
371
  elif isinstance(other, Rotation) or is_orix_rotation(other):
273
372
  if _is_single_rotation(other):
274
373
  return self.rotate(other)
275
374
  else:
276
- return self.__class__(self.matrix, symmetry=self.symmetry, orientations=other, phase_name=self.phase_name)
375
+ return self.__class__(self.matrix, symmetry=self.symmetry, orientations=other,
376
+ phase_name=self.phase_name)
277
377
  else:
278
378
  return self.__class__(self.matrix * other, symmetry=self.symmetry)
279
379
 
380
+ def transpose_array(self):
381
+ """
382
+ Transpose the orientations of the tensor array
383
+
384
+ Returns
385
+ -------
386
+ FourthOrderTensor
387
+ The same tensor, but with transposed orientations
388
+ """
389
+ ndim = self.ndim
390
+ if ndim==0 or ndim==1:
391
+ return self
392
+ else:
393
+ new_tensor = self._unrotate()
394
+ new_order = np.flip(range(ndim))
395
+ new_tensor.orientations = self.orientations.transpose(*new_order)
396
+ return new_tensor
397
+
280
398
  def __rmul__(self, other):
281
399
  if isinstance(other, (Rotation, float, int, np.number)) or is_orix_rotation(other):
282
400
  return self * other
@@ -292,17 +410,24 @@ class SymmetricTensor:
292
410
  def __eq__(self, other):
293
411
  if isinstance(other, SymmetricTensor):
294
412
  return np.all(self.matrix == other.matrix) and np.all(self.orientations == other.orientations)
295
- elif isinstance(other, np.ndarray) and other.shape == (6,6):
413
+ elif isinstance(other, np.ndarray) and other.shape == (6, 6):
296
414
  return np.all(self.matrix == other)
297
415
  else:
298
416
  raise NotImplementedError('The element to compare with must be a fourth-order tensor '
299
417
  'or an array of shape (6,6).')
300
418
 
301
-
302
- def _orientation_average(self):
303
- mean_full_tensor = np.mean(self.full_tensor(), axis=0)
304
- mean_matrix = self._full_to_matrix(mean_full_tensor)
305
- return self.__class__(mean_matrix)
419
+ def matmul(self, other):
420
+ if isinstance(other, SecondOrderTensor):
421
+ other_matrix = other.matrix
422
+ else:
423
+ other_matrix = other
424
+ indices_0= 'abcdefgh'
425
+ indices_1= indices_0.upper()
426
+ indices_0 = indices_0[:self.ndim]
427
+ indices_1 = indices_1[:other_matrix.ndim-2]
428
+ ein_str = indices_0 + 'ijkl,' + indices_1 +'kl->' + indices_0 + indices_1 + 'ij'
429
+ new_mat = np.einsum(ein_str, self.full_tensor(), other_matrix)
430
+ return StrainTensor(new_mat)
306
431
 
307
432
  @classmethod
308
433
  def _matrixFromCrystalSymmetry(cls, symmetry='Triclinic', point_group=None, diad='y', prefix=None, **kwargs):
@@ -724,7 +849,8 @@ class SymmetricTensor:
724
849
  if self.orientations is None:
725
850
  raise IndexError('The tensor has no orientation, therefore it cannot be indexed.')
726
851
  else:
727
- return self._unrotate()*self.orientations[item]
852
+ return self._unrotate() * self.orientations[item]
853
+
728
854
 
729
855
  class StiffnessTensor(SymmetricTensor):
730
856
  """
@@ -823,6 +949,7 @@ class StiffnessTensor(SymmetricTensor):
823
949
  --------
824
950
  bulk_modulus : bulk modulus of the material
825
951
  """
952
+
826
953
  def compute_linear_compressibility(n):
827
954
  return _compute_unit_strain_along_direction(self, n, n, direction='spherical')
828
955
 
@@ -844,7 +971,6 @@ class StiffnessTensor(SymmetricTensor):
844
971
  """
845
972
  return self.inv().bulk_modulus
846
973
 
847
-
848
974
  def Voigt_average(self):
849
975
  """
850
976
  Compute the Voigt average of the stiffness tensor. If the tensor contains no orientation, we assume isotropic
@@ -873,7 +999,7 @@ class StiffnessTensor(SymmetricTensor):
873
999
  mat = _isotropic_matrix(C11, C12, C44)
874
1000
  return StiffnessTensor(mat, symmetry='isotropic', phase_name=self.phase_name)
875
1001
  else:
876
- return self._orientation_average()
1002
+ return StiffnessTensor(self.mean())
877
1003
 
878
1004
  def Reuss_average(self):
879
1005
  """
@@ -941,7 +1067,6 @@ class StiffnessTensor(SymmetricTensor):
941
1067
  else:
942
1068
  raise NotImplementedError('Only Voigt, Reus, and Hill are implemented.')
943
1069
 
944
-
945
1070
  @classmethod
946
1071
  def isotropic(cls, E=None, nu=None, lame1=None, lame2=None, phase_name=None):
947
1072
  """
@@ -1044,10 +1169,10 @@ class StiffnessTensor(SymmetricTensor):
1044
1169
  """
1045
1170
  tri_sup = np.array([[1 / Ex, -nu_yx / Ey, -nu_zx / Ez, 0, 0, 0],
1046
1171
  [0, 1 / Ey, -nu_zy / Ez, 0, 0, 0],
1047
- [0, 0, 1 / Ez, 0, 0, 0],
1048
- [0, 0, 0, 1 / Gyz, 0, 0],
1049
- [0, 0, 0, 0, 1 / Gxz, 0],
1050
- [0, 0, 0, 0, 0, 1 / Gxy]])
1172
+ [0, 0, 1 / Ez, 0, 0, 0],
1173
+ [0, 0, 0, 1 / Gyz, 0, 0],
1174
+ [0, 0, 0, 0, 1 / Gxz, 0],
1175
+ [0, 0, 0, 0, 0, 1 / Gxy]])
1051
1176
  S = tri_sup + np.tril(tri_sup.T, -1)
1052
1177
  return StiffnessTensor(np.linalg.inv(S), symmetry='orthotropic', **kwargs)
1053
1178
 
@@ -1298,7 +1423,7 @@ class StiffnessTensor(SymmetricTensor):
1298
1423
  .. [3] S. I. Ranganathan and M. Ostoja-Starzewski, Universal Elastic Anisotropy Index,
1299
1424
  *Phys. Rev. Lett.*, 101(5), 055504, 2008. https://doi.org/10.1103/PhysRevLett.101.055504
1300
1425
  """
1301
- C = self._unrotate() # Ensure that the averages do not use the orientations
1426
+ C = self._unrotate() # Ensure that the averages do not use the orientations
1302
1427
  Cvoigt = C.Voigt_average()
1303
1428
  Creuss = C.Reuss_average()
1304
1429
  Gv = Cvoigt.matrix[3, 3]
@@ -1353,6 +1478,123 @@ class StiffnessTensor(SymmetricTensor):
1353
1478
  raise ModuleNotFoundError('pymatgen module is required for this function.')
1354
1479
  return matgenElast.ElasticTensor(self.full_tensor())
1355
1480
 
1481
+ def to_Kelvin(self):
1482
+ """
1483
+ Returns all the tensor components using the Kelvin(-Mandel) mapping convention.
1484
+
1485
+ Returns
1486
+ -------
1487
+ numpy.ndarray
1488
+ (6,6) matrix, according to the Kelvin mapping
1489
+
1490
+ See Also
1491
+ --------
1492
+ eig : returns the eigenvalues and the eigenvectors of the Kelvin's matrix
1493
+ from_Kelvin : Construct a fourth-order tensor from its (6,6) Kelvin matrix
1494
+
1495
+ Notes
1496
+ -----
1497
+ This mapping convention is discussed in [4]_.
1498
+
1499
+ References
1500
+ ----------
1501
+ .. [4] Helbig, K. (2013). What Kelvin might have written about Elasticity. Geophysical Prospecting, 61(1), 1-20.
1502
+ doi: 10.1111/j.1365-2478.2011.01049.x
1503
+ """
1504
+ return self.matrix /self.voigt_map * _voigt_to_kelvin_matrix
1505
+
1506
+ def eig(self):
1507
+ """
1508
+ Compute the eigenstiffnesses and the eigenstrains.
1509
+
1510
+ Solve the eigenvalue problem from the Kelvin matrix of the stiffness tensor (see Notes).
1511
+
1512
+ Returns
1513
+ -------
1514
+ numpy.ndarray
1515
+ Array of 6 eigenstiffnesses (eigenvalues of the stiffness matrix)
1516
+ numpy.ndarray
1517
+ (6,6) array of eigenstrains (eigenvectors of the stiffness matrix)
1518
+
1519
+ See Also
1520
+ --------
1521
+ to_Kelvin : returns the stiffness components as a (6,6) matrix, according to the Kelvin mapping convention.
1522
+ eig_stiffnesses : returns the eigenstiffnesses only
1523
+ eig_strains : returns the eigenstrains only
1524
+
1525
+ Notes
1526
+ -----
1527
+ The definition for eigenstiffnesses and the eigenstrains are introduced in [4]_.
1528
+ """
1529
+ return np.linalg.eigh(self.to_Kelvin())
1530
+
1531
+ @property
1532
+ def eig_stiffnesses(self):
1533
+ """
1534
+ Compute the eigenstiffnesses given by the Kelvin's matrix for stiffness.
1535
+
1536
+ Returns
1537
+ -------
1538
+ numpy.ndarray
1539
+ 6 eigenvalues of the Kelvin's stiffness matrix, in ascending order
1540
+
1541
+ See Also
1542
+ --------
1543
+ eig : returns the eigenstiffnesses and the eigenstrains
1544
+ eig_strains : returns the eigenstrains only
1545
+ """
1546
+ return np.linalg.eigvalsh(self.to_Kelvin())
1547
+
1548
+ @property
1549
+ def eig_strains(self):
1550
+ """
1551
+ Compute the eigenstrains from the Kelvin's matrix for stiffness
1552
+
1553
+ Returns
1554
+ -------
1555
+ numpy.ndarray
1556
+ (6,6) matrix of eigenstrains, sorted by ascending order of eigenstiffnesses.
1557
+
1558
+ See Also
1559
+ --------
1560
+ eig : returns both the eigenvalues and the eigenvectors of the Kelvin matrix
1561
+ """
1562
+ return self.eig()[1]
1563
+
1564
+ @property
1565
+ def eig_compliances(self):
1566
+ """
1567
+ Compute the eigencompliances from the Kelvin's matrix of stiffness
1568
+
1569
+ Returns
1570
+ -------
1571
+ numpy.ndarray
1572
+ Inverses of the 6 eigenvalues of the Kelvin's stiffness matrix, in descending order
1573
+
1574
+ See Also
1575
+ --------
1576
+ eig_stiffnesses : compute the eigenstiffnesses from the Kelvin's matrix of stiffness
1577
+ """
1578
+ return 1/self.eig_stiffnesses
1579
+
1580
+ @classmethod
1581
+ def from_Kelvin(cls, matrix, **kwargs):
1582
+ """
1583
+ Create a tensor from the (6,6) matrix following the Kelvin(-Mandel) mapping convention
1584
+
1585
+ Parameters
1586
+ ----------
1587
+ matrix : list or numpy.ndarray
1588
+ (6,6) matrix of components
1589
+ kwargs : dict
1590
+ keyword arguments passed to the constructor
1591
+ Returns
1592
+ -------
1593
+ StiffnessTensor
1594
+ """
1595
+ return cls(matrix * cls.voigt_map / _voigt_to_kelvin_matrix, **kwargs)
1596
+
1597
+
1356
1598
  class ComplianceTensor(StiffnessTensor):
1357
1599
  """
1358
1600
  Class for manipulating compliance tensors
@@ -1405,7 +1647,7 @@ class ComplianceTensor(StiffnessTensor):
1405
1647
  mat = _isotropic_matrix(S11, S12, S44)
1406
1648
  return ComplianceTensor(mat, symmetry='isotropic', phase_name=self.phase_name)
1407
1649
  else:
1408
- return self._orientation_average()
1650
+ return ComplianceTensor(self.mean())
1409
1651
 
1410
1652
  def Voigt_average(self):
1411
1653
  return self.inv().Voigt_average().inv()
@@ -1424,14 +1666,14 @@ class ComplianceTensor(StiffnessTensor):
1424
1666
  @classmethod
1425
1667
  def transverse_isotropic(cls, *args, **kwargs):
1426
1668
  return super().transverse_isotropic(*args, **kwargs).inv()
1427
-
1669
+
1428
1670
  @classmethod
1429
1671
  def weighted_average(cls, *args):
1430
1672
  return super().weighted_average(*args).inv()
1431
1673
 
1432
1674
  @property
1433
1675
  def bulk_modulus(self):
1434
- return 1/np.sum(self.matrix[0:3,0:3])
1676
+ return 1 / np.sum(self.matrix[0:3, 0:3])
1435
1677
 
1436
1678
  @property
1437
1679
  def universal_anisotropy(self):
@@ -1461,3 +1703,77 @@ class ComplianceTensor(StiffnessTensor):
1461
1703
  except ImportError:
1462
1704
  raise ModuleNotFoundError('pymatgen module is required for this function.')
1463
1705
  return matgenElast.ComplianceTensor(self.full_tensor())
1706
+
1707
+ def eig(self):
1708
+ """
1709
+ Compute the eigencompliances and the eigenstresses.
1710
+
1711
+ Solve the eigenvalue problem from the Kelvin matrix of the compliance tensor (see Notes).
1712
+
1713
+ Returns
1714
+ -------
1715
+ numpy.ndarray
1716
+ Array of 6 eigencompliances (eigenvalues of the stiffness matrix)
1717
+ numpy.ndarray
1718
+ (6,6) array of eigenstresses (eigenvectors of the stiffness matrix)
1719
+
1720
+ See Also
1721
+ --------
1722
+ Kelvin : returns the stiffness components as a (6,6) matrix, according to the Kelvin mapping convention.
1723
+ eig_compliances : returns the eigencompliances only
1724
+ eig_stresses : returns the eigenstresses only
1725
+
1726
+ Notes
1727
+ -----
1728
+ The definition for eigencompliances and the eigenstresses are introduced in [4]_.
1729
+ """
1730
+ return np.linalg.eigh(self.to_Kelvin())
1731
+
1732
+ @property
1733
+ def eig_compliances(self):
1734
+ """
1735
+ Compute the eigencompliances given by the Kelvin's matrix for stiffness.
1736
+
1737
+ Returns
1738
+ -------
1739
+ numpy.ndarray
1740
+ 6 eigenvalues of the Kelvin's compliance matrix, in ascending order
1741
+
1742
+ See Also
1743
+ --------
1744
+ eig : returns the eigencompliances and the eigenstresses
1745
+ eig_strains : returns the eigenstresses only
1746
+ """
1747
+ return np.linalg.eigvalsh(self.to_Kelvin())
1748
+
1749
+ @property
1750
+ def eig_stresses(self):
1751
+ """
1752
+ Compute the eigenstresses from the Kelvin's matrix for stiffness
1753
+
1754
+ Returns
1755
+ -------
1756
+ numpy.ndarray
1757
+ (6,6) matrix of eigenstresses, sorted by ascending order of eigencompliances.
1758
+
1759
+ See Also
1760
+ --------
1761
+ eig : returns both the eigencompliances and the eigenstresses
1762
+ """
1763
+ return self.eig()[1]
1764
+
1765
+ @property
1766
+ def eig_stiffnesses(self):
1767
+ """
1768
+ Compute the eigenstiffnesses from the Kelvin's matrix of compliance
1769
+
1770
+ Returns
1771
+ -------
1772
+ numpy.ndarray
1773
+ inverses of 6 eigenvalues of the Kelvin's compliance matrix, in descending order
1774
+
1775
+ See Also
1776
+ --------
1777
+ eig_compliances : compute the eigencompliances from the Kelvin's matrix of compliance
1778
+ """
1779
+ return 1/self.eig_compliances