elasticipy 3.0.0__py3-none-any.whl → 4.1.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Elasticipy/FourthOrderTensor.py +16 -1779
- Elasticipy/StressStrainTensors.py +16 -138
- Elasticipy/ThermalExpansion.py +9 -246
- Elasticipy/gui.py +3 -3
- Elasticipy/interfaces/FEPX.py +119 -0
- Elasticipy/interfaces/PRISMS.py +103 -0
- Elasticipy/{Plasticity.py → plasticity.py} +35 -2
- Elasticipy/{PoleFigure.py → polefigure.py} +21 -0
- Elasticipy/{SphericalFunction.py → spherical_function.py} +3 -15
- Elasticipy/tensors/__init__.py +0 -0
- Elasticipy/tensors/elasticity.py +1748 -0
- Elasticipy/tensors/fourth_order.py +662 -0
- Elasticipy/tensors/mapping.py +44 -0
- Elasticipy/{SecondOrderTensor.py → tensors/second_order.py} +113 -9
- Elasticipy/tensors/stress_strain.py +154 -0
- Elasticipy/tensors/thermal_expansion.py +249 -0
- {elasticipy-3.0.0.dist-info → elasticipy-4.1.0.dist-info}/METADATA +26 -8
- elasticipy-4.1.0.dist-info/RECORD +23 -0
- {elasticipy-3.0.0.dist-info → elasticipy-4.1.0.dist-info}/WHEEL +1 -1
- elasticipy-3.0.0.dist-info/RECORD +0 -15
- /Elasticipy/{CrystalSymmetries.py → crystal_symmetries.py} +0 -0
- {elasticipy-3.0.0.dist-info → elasticipy-4.1.0.dist-info/licenses}/LICENSE +0 -0
- {elasticipy-3.0.0.dist-info → elasticipy-4.1.0.dist-info}/top_level.txt +0 -0
|
@@ -1,138 +1,16 @@
|
|
|
1
|
-
import
|
|
2
|
-
from Elasticipy.
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
""
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
If the tensor array is of shape [m,n,...], the results will be of shape [m,n,...,3].
|
|
18
|
-
|
|
19
|
-
Returns
|
|
20
|
-
-------
|
|
21
|
-
np.ndarray
|
|
22
|
-
Principal strain values
|
|
23
|
-
"""
|
|
24
|
-
return self.eigvals()
|
|
25
|
-
|
|
26
|
-
def volumetric_strain(self):
|
|
27
|
-
"""
|
|
28
|
-
Volumetric change (1st invariant of the strain tensor)
|
|
29
|
-
|
|
30
|
-
Returns
|
|
31
|
-
-------
|
|
32
|
-
numpy.ndarray or float
|
|
33
|
-
Volumetric change
|
|
34
|
-
"""
|
|
35
|
-
return self.I1
|
|
36
|
-
|
|
37
|
-
def eq_strain(self):
|
|
38
|
-
"""von Mises equivalent strain"""
|
|
39
|
-
return np.sqrt(2/3 * self.ddot(self))
|
|
40
|
-
|
|
41
|
-
def elastic_energy(self, stress):
|
|
42
|
-
"""
|
|
43
|
-
Compute the elastic energy.
|
|
44
|
-
|
|
45
|
-
Parameters
|
|
46
|
-
----------
|
|
47
|
-
stress : StressTensor
|
|
48
|
-
Corresponding stress tensor
|
|
49
|
-
|
|
50
|
-
Returns
|
|
51
|
-
-------
|
|
52
|
-
Volumetric elastic energy
|
|
53
|
-
"""
|
|
54
|
-
return 0.5 * self.ddot(stress)
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
class StressTensor(SymmetricSecondOrderTensor):
|
|
58
|
-
"""
|
|
59
|
-
Class for manipulating stress tensors or arrays of stress tensors.
|
|
60
|
-
"""
|
|
61
|
-
name = 'Stress tensor'
|
|
62
|
-
|
|
63
|
-
def principal_stresses(self):
|
|
64
|
-
"""
|
|
65
|
-
Values of the principals stresses.
|
|
66
|
-
|
|
67
|
-
If the tensor array is of shape [m,n,...], the results will be of shape [m,n,...,3].
|
|
68
|
-
|
|
69
|
-
Returns
|
|
70
|
-
-------
|
|
71
|
-
np.ndarray
|
|
72
|
-
Principal stresses
|
|
73
|
-
"""
|
|
74
|
-
return self.eigvals()
|
|
75
|
-
|
|
76
|
-
def vonMises(self):
|
|
77
|
-
"""
|
|
78
|
-
von Mises equivalent stress.
|
|
79
|
-
|
|
80
|
-
Returns
|
|
81
|
-
-------
|
|
82
|
-
np.ndarray or float
|
|
83
|
-
von Mises equivalent stress
|
|
84
|
-
|
|
85
|
-
See Also
|
|
86
|
-
--------
|
|
87
|
-
Tresca : Tresca equivalent stress
|
|
88
|
-
"""
|
|
89
|
-
return np.sqrt(3 * self.J2)
|
|
90
|
-
|
|
91
|
-
def Tresca(self):
|
|
92
|
-
"""
|
|
93
|
-
Tresca(-Guest) equivalent stress.
|
|
94
|
-
|
|
95
|
-
Returns
|
|
96
|
-
-------
|
|
97
|
-
np.ndarray or float
|
|
98
|
-
Tresca equivalent stress
|
|
99
|
-
|
|
100
|
-
See Also
|
|
101
|
-
--------
|
|
102
|
-
vonMises : von Mises equivalent stress
|
|
103
|
-
"""
|
|
104
|
-
ps = self.principal_stresses()
|
|
105
|
-
return ps[...,0] - ps[...,-1]
|
|
106
|
-
|
|
107
|
-
def hydrostatic_pressure(self):
|
|
108
|
-
"""
|
|
109
|
-
Hydrostatic pressure
|
|
110
|
-
|
|
111
|
-
Returns
|
|
112
|
-
-------
|
|
113
|
-
np.ndarray or float
|
|
114
|
-
|
|
115
|
-
See Also
|
|
116
|
-
--------
|
|
117
|
-
sphericalPart : spherical part of the stress
|
|
118
|
-
"""
|
|
119
|
-
return -self.I1/3
|
|
120
|
-
|
|
121
|
-
def elastic_energy(self, strain, mode='pair'):
|
|
122
|
-
"""
|
|
123
|
-
Compute the elastic energy.
|
|
124
|
-
|
|
125
|
-
Parameters
|
|
126
|
-
----------
|
|
127
|
-
strain : StrainTensor
|
|
128
|
-
Corresponding elastic strain tensor
|
|
129
|
-
mode : str, optional
|
|
130
|
-
If 'pair' (default), the elastic energies are computed element-wise. Broadcasting rule applies.
|
|
131
|
-
If 'cross', each cross-combination of stress and strain are considered.
|
|
132
|
-
|
|
133
|
-
Returns
|
|
134
|
-
-------
|
|
135
|
-
numpy.ndarray
|
|
136
|
-
Volumetric elastic energy
|
|
137
|
-
"""
|
|
138
|
-
return 0.5 * self.ddot(strain, mode=mode)
|
|
1
|
+
import warnings
|
|
2
|
+
from Elasticipy.tensors.stress_strain import StressTensor as NewStressTensor
|
|
3
|
+
from Elasticipy.tensors.stress_strain import StrainTensor as NewStrainTensor
|
|
4
|
+
|
|
5
|
+
warnings.warn(
|
|
6
|
+
"The module 'Elasticipy.StressStrainTensors' is deprecated and will be removed in a future release. "
|
|
7
|
+
"Please use 'Elasticipy.tensors.stress_strain' instead.",
|
|
8
|
+
DeprecationWarning,
|
|
9
|
+
stacklevel=2
|
|
10
|
+
)
|
|
11
|
+
|
|
12
|
+
class StressTensor(NewStressTensor):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
class StrainTensor(NewStrainTensor):
|
|
16
|
+
pass
|
Elasticipy/ThermalExpansion.py
CHANGED
|
@@ -1,249 +1,12 @@
|
|
|
1
1
|
import warnings
|
|
2
|
+
from Elasticipy.tensors.thermal_expansion import ThermalExpansionTensor as NewThermalExpansionTensor
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
warnings.warn(
|
|
5
|
+
"The module 'Elasticipy.ThermalExpansion' is deprecated and will be removed in a future release. "
|
|
6
|
+
"Please use 'Elasticipy.tensors.thermal_expansion' instead.",
|
|
7
|
+
DeprecationWarning,
|
|
8
|
+
stacklevel=2
|
|
9
|
+
)
|
|
7
10
|
|
|
8
|
-
class ThermalExpansionTensor(
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def __mul__(self, other):
|
|
12
|
-
if isinstance(other, Rotation) or is_orix_rotation(other):
|
|
13
|
-
return super().__mul__(other)
|
|
14
|
-
else:
|
|
15
|
-
other = np.asarray(other)
|
|
16
|
-
other_expanded = other[..., None, None]
|
|
17
|
-
other_with_eye = other_expanded * np.ones(3)
|
|
18
|
-
new_mat = self.matrix * other_with_eye
|
|
19
|
-
return StrainTensor(new_mat)
|
|
20
|
-
|
|
21
|
-
def apply_temperature(self, temperature, mode='pair'):
|
|
22
|
-
"""
|
|
23
|
-
Apply temperature increase to the thermal expansion tensor, or to the array.
|
|
24
|
-
|
|
25
|
-
Application can be made pair-wise, or considering all cross-combinations (see below).
|
|
26
|
-
|
|
27
|
-
Parameters
|
|
28
|
-
----------
|
|
29
|
-
temperature : float or numpy.ndarray
|
|
30
|
-
mode : str, optional
|
|
31
|
-
If "pair" (default), the temperatures are applied pair-wise on the tensor array. Broadcasting rule applies
|
|
32
|
-
If "cross", all cross combinations are considered. Therefore, if ``C=A.apply_temperature(T, mode="cross")``,
|
|
33
|
-
then ``C.shape=A.shape + T.shape``.
|
|
34
|
-
|
|
35
|
-
Returns
|
|
36
|
-
-------
|
|
37
|
-
StrainTensor
|
|
38
|
-
Strain corresponding to the applied temperature increase(s).
|
|
39
|
-
|
|
40
|
-
Examples
|
|
41
|
-
--------
|
|
42
|
-
Let consider a transverse isotropic case (e.g. carbon fibers):
|
|
43
|
-
>>> from Elasticipy.ThermalExpansion import ThermalExpansionTensor as ThEx
|
|
44
|
-
>>> alpha = ThEx.transverse_isotropic(alpha_11=5.6e-6, alpha_33=-0.4e-6)
|
|
45
|
-
|
|
46
|
-
Now apply temperature increases:
|
|
47
|
-
>>> T = [0, 1, 2]
|
|
48
|
-
>>> eps = alpha.apply_temperature(T)
|
|
49
|
-
|
|
50
|
-
We get a strain tensor of the same shape as the applied temperatures:
|
|
51
|
-
>>> eps
|
|
52
|
-
Strain tensor
|
|
53
|
-
Shape=(3,)
|
|
54
|
-
>>> eps[-1]
|
|
55
|
-
Strain tensor
|
|
56
|
-
[[ 1.12e-05 0.00e+00 0.00e+00]
|
|
57
|
-
[ 0.00e+00 1.12e-05 0.00e+00]
|
|
58
|
-
[ 0.00e+00 0.00e+00 -8.00e-07]]
|
|
59
|
-
|
|
60
|
-
Now let's rotate the thermal expansions:
|
|
61
|
-
>>> from scipy.spatial.transform import Rotation
|
|
62
|
-
>>> rot = Rotation.random(3) # Set of 3 random 3D rotations
|
|
63
|
-
>>> alpha_rotated = alpha * rot
|
|
64
|
-
|
|
65
|
-
If we want to combine each rotated thermal expansion with the corresponding temperature increase:
|
|
66
|
-
>>> eps_rotated_pair = alpha_rotated * T # Equivalent to alpha_rotated.apply_temperature(T)
|
|
67
|
-
>>> eps_rotated_pair
|
|
68
|
-
Strain tensor
|
|
69
|
-
Shape=(3,)
|
|
70
|
-
|
|
71
|
-
Conversely, if we want to evaluate all cross-combinations of thermal expansions and temperature increases:
|
|
72
|
-
|
|
73
|
-
>>> eps_rotated_cross = alpha_rotated.apply_temperature(T, mode='cross')
|
|
74
|
-
>>> eps_rotated_cross
|
|
75
|
-
Strain tensor
|
|
76
|
-
Shape=(3, 3)
|
|
77
|
-
|
|
78
|
-
"""
|
|
79
|
-
temperature = np.asarray(temperature)
|
|
80
|
-
if mode == 'pair':
|
|
81
|
-
matrix = self.matrix*temperature[...,np.newaxis,np.newaxis]
|
|
82
|
-
elif mode == 'cross':
|
|
83
|
-
indices_self = ALPHABET[:self.ndim]
|
|
84
|
-
indices_temp = ALPHABET[:len(temperature.shape)].upper()
|
|
85
|
-
ein_str = indices_self + 'ij,' + indices_temp + '->' + indices_self + indices_temp + 'ij'
|
|
86
|
-
matrix = np.einsum(ein_str, self.matrix, temperature)
|
|
87
|
-
else:
|
|
88
|
-
raise ValueError('Invalid mode. It could be either "pair" or "cross".')
|
|
89
|
-
return StrainTensor(matrix)
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
def matmul(self, other):
|
|
93
|
-
"""
|
|
94
|
-
Matrix like product with array of float, resulting either in StrainTensor.
|
|
95
|
-
|
|
96
|
-
Compute the product between the tensor and a numpy array in a "matrix-product" way,*
|
|
97
|
-
that is by computing each of the products. If T.shape=(m,n,o,...) and other.shape=(p,q,r,...), then::
|
|
98
|
-
|
|
99
|
-
T.matmul(other).shape = (m,n,o,...,p,q,r,...)
|
|
100
|
-
|
|
101
|
-
Parameters
|
|
102
|
-
----------
|
|
103
|
-
other : np.ndarray
|
|
104
|
-
Value to multiply by.
|
|
105
|
-
Returns
|
|
106
|
-
-------
|
|
107
|
-
StrainTensor
|
|
108
|
-
Array fo strain tensors corresponding to all cross-combinations between Thermal expansions and temperature
|
|
109
|
-
increases.
|
|
110
|
-
"""
|
|
111
|
-
warnings.warn(
|
|
112
|
-
'matmul() is deprecated and will be removed in a future version. Use dot(tensor,mode="cross") or '
|
|
113
|
-
'rotate(rotation,mode="cross") instead.',
|
|
114
|
-
DeprecationWarning,
|
|
115
|
-
stacklevel=2)
|
|
116
|
-
if isinstance(other, Rotation) or is_orix_rotation(other):
|
|
117
|
-
return super().matmul(other)
|
|
118
|
-
else:
|
|
119
|
-
return self.apply_temperature(other, mode='cross')
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
@classmethod
|
|
123
|
-
def isotropic(cls, alpha):
|
|
124
|
-
"""
|
|
125
|
-
Create an isotropic thermal expansion tensor.
|
|
126
|
-
|
|
127
|
-
Parameters
|
|
128
|
-
----------
|
|
129
|
-
alpha : float
|
|
130
|
-
Thermal expansion coefficient.
|
|
131
|
-
|
|
132
|
-
Returns
|
|
133
|
-
-------
|
|
134
|
-
ThermalExpansionTensor
|
|
135
|
-
"""
|
|
136
|
-
return cls(np.eye(3)*alpha)
|
|
137
|
-
|
|
138
|
-
@classmethod
|
|
139
|
-
def orthotropic(cls, alpha_11, alpha_22, alpha_33):
|
|
140
|
-
"""
|
|
141
|
-
Create an orthotropic thermal expansion tensor.
|
|
142
|
-
|
|
143
|
-
Parameters
|
|
144
|
-
----------
|
|
145
|
-
alpha_11, alpha_22, alpha_33 : float
|
|
146
|
-
Thermal expansion coefficients along the first, second and third axes, respectively.
|
|
147
|
-
|
|
148
|
-
Returns
|
|
149
|
-
-------
|
|
150
|
-
ThermalExpansionTensor
|
|
151
|
-
"""
|
|
152
|
-
return cls(np.diag([alpha_11, alpha_22, alpha_33]))
|
|
153
|
-
|
|
154
|
-
@classmethod
|
|
155
|
-
def orthorhombic(cls, *args):
|
|
156
|
-
"""
|
|
157
|
-
Create a thermal expansion tensor corresponding to an orthotropic thermal expansion coefficient.
|
|
158
|
-
|
|
159
|
-
This function is an alias for orthotropic().
|
|
160
|
-
|
|
161
|
-
Parameters
|
|
162
|
-
----------
|
|
163
|
-
args : list
|
|
164
|
-
Orthotropic thermal expansion coefficient.
|
|
165
|
-
|
|
166
|
-
Returns
|
|
167
|
-
-------
|
|
168
|
-
ThermalExpansionTensor
|
|
169
|
-
|
|
170
|
-
See Also
|
|
171
|
-
--------
|
|
172
|
-
orthotropic
|
|
173
|
-
"""
|
|
174
|
-
return cls.orthotropic(*args)
|
|
175
|
-
|
|
176
|
-
@classmethod
|
|
177
|
-
def monoclinic(cls, alpha_11, alpha_22, alpha_33, alpha_13=None, alpha_12=None):
|
|
178
|
-
"""
|
|
179
|
-
Create a thermal expansion tensor for monoclinic symmetry.
|
|
180
|
-
|
|
181
|
-
If alpha_13, the Diad || z is assumed. If alpha_12, the Diad || z is assumed. Therefore, these two parameters
|
|
182
|
-
are exclusive.
|
|
183
|
-
|
|
184
|
-
Parameters
|
|
185
|
-
----------
|
|
186
|
-
alpha_11, alpha_22, alpha_33 : float
|
|
187
|
-
Thermal expansion coefficient along the first, second and third axes, respectively.
|
|
188
|
-
alpha_13 : float, optional
|
|
189
|
-
Thermal expansion coefficient corresponding to XZ shear (for Diad || y)
|
|
190
|
-
alpha_12: float, optional
|
|
191
|
-
Thermal expansion coefficient corresponding to XY shear (for Diad || z)
|
|
192
|
-
|
|
193
|
-
Returns
|
|
194
|
-
-------
|
|
195
|
-
ThermalExpansionTensor
|
|
196
|
-
"""
|
|
197
|
-
matrix = np.diag([alpha_11, alpha_22, alpha_33])
|
|
198
|
-
if (alpha_13 is not None) and (alpha_12 is None):
|
|
199
|
-
matrix[0, 2] = matrix[2, 0]= alpha_13
|
|
200
|
-
elif (alpha_12 is not None) and (alpha_13 is None):
|
|
201
|
-
matrix[0, 1] = matrix[1, 0]= alpha_12
|
|
202
|
-
elif (alpha_13 is not None) and (alpha_12 is not None):
|
|
203
|
-
raise ValueError('alpha_13 and alpha_12 cannot be used together.')
|
|
204
|
-
else:
|
|
205
|
-
raise ValueError('Either alpha_13 or alpha_12 must be provided.')
|
|
206
|
-
return cls(matrix)
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
@classmethod
|
|
210
|
-
def triclinic(cls, alpha_11=0., alpha_12=0., alpha_13=0., alpha_22=0., alpha_23=0., alpha_33=0.):
|
|
211
|
-
"""
|
|
212
|
-
Create a thermal expansion tensor for triclinic symmetry.
|
|
213
|
-
|
|
214
|
-
Parameters
|
|
215
|
-
----------
|
|
216
|
-
alpha_11, alpha_12, alpha_13, alpha_22, alpha_23, alpha_33 : float
|
|
217
|
-
Values of the thermal expansion coefficients
|
|
218
|
-
|
|
219
|
-
Returns
|
|
220
|
-
-------
|
|
221
|
-
ThermalExpansionTensor
|
|
222
|
-
"""
|
|
223
|
-
mat = [[alpha_11, alpha_12, alpha_13],
|
|
224
|
-
[alpha_12, alpha_22, alpha_23],
|
|
225
|
-
[alpha_13, alpha_23, alpha_33]]
|
|
226
|
-
return cls(mat)
|
|
227
|
-
|
|
228
|
-
@classmethod
|
|
229
|
-
def transverse_isotropic(cls, alpha_11, alpha_33):
|
|
230
|
-
"""
|
|
231
|
-
Create a thermal expansion tensor for transverse isotropic symmetry.
|
|
232
|
-
|
|
233
|
-
Parameters
|
|
234
|
-
----------
|
|
235
|
-
alpha_11 : float
|
|
236
|
-
Thermal expansion coefficient along the first and second axes
|
|
237
|
-
alpha_33 : float
|
|
238
|
-
Thermal expansion coefficient along the third axis
|
|
239
|
-
|
|
240
|
-
Returns
|
|
241
|
-
-------
|
|
242
|
-
ThermalExpansionTensor
|
|
243
|
-
"""
|
|
244
|
-
return cls(np.diag([alpha_11, alpha_11, alpha_33]))
|
|
245
|
-
|
|
246
|
-
@property
|
|
247
|
-
def volumetric_coefficient(self):
|
|
248
|
-
""" Returns the volumetric thermal expansion coefficient."""
|
|
249
|
-
return self.I1
|
|
11
|
+
class ThermalExpansionTensor(NewThermalExpansionTensor):
|
|
12
|
+
pass
|
Elasticipy/gui.py
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import sys
|
|
2
2
|
|
|
3
3
|
import numpy as np
|
|
4
|
-
from
|
|
4
|
+
from qtpy.QtWidgets import (
|
|
5
5
|
QApplication, QMainWindow, QComboBox, QGridLayout, QLabel,
|
|
6
6
|
QLineEdit, QPushButton, QVBoxLayout, QHBoxLayout, QWidget, QFrame, QMessageBox
|
|
7
7
|
)
|
|
8
8
|
from matplotlib.backends.backend_qt5agg import FigureCanvasQTAgg as FigureCanvas
|
|
9
9
|
from matplotlib.figure import Figure
|
|
10
10
|
|
|
11
|
-
from Elasticipy.
|
|
12
|
-
from Elasticipy.
|
|
11
|
+
from Elasticipy.crystal_symmetries import SYMMETRIES
|
|
12
|
+
from Elasticipy.tensors.elasticity import StiffnessTensor
|
|
13
13
|
|
|
14
14
|
WHICH_OPTIONS = {'Mean': 'mean', 'Max': 'max', 'Min': 'min', 'Std. dev.': 'std'}
|
|
15
15
|
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
from Elasticipy.tensors.second_order import SymmetricSecondOrderTensor, SecondOrderTensor, \
|
|
2
|
+
SkewSymmetricSecondOrderTensor
|
|
3
|
+
from Elasticipy.tensors.stress_strain import StressTensor, StrainTensor
|
|
4
|
+
import pandas as pd
|
|
5
|
+
import numpy as np
|
|
6
|
+
import os
|
|
7
|
+
import re
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
DTYPES={'stress':StressTensor,
|
|
11
|
+
'strain':StrainTensor,
|
|
12
|
+
'strain_el':StrainTensor,
|
|
13
|
+
'strain_pl':StrainTensor,
|
|
14
|
+
'velgrad':SecondOrderTensor,
|
|
15
|
+
'defrate':SymmetricSecondOrderTensor,
|
|
16
|
+
'defrate_pl':SymmetricSecondOrderTensor,
|
|
17
|
+
'spinrate':SkewSymmetricSecondOrderTensor,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _list_valid_filenames(folder, startswith='strain'):
|
|
22
|
+
file_list = os.listdir(folder)
|
|
23
|
+
pattern = r'{}\.step\d+'.format(startswith)
|
|
24
|
+
return [f for f in file_list if re.fullmatch(pattern, f)]
|
|
25
|
+
|
|
26
|
+
def from_step_file(file, dtype=None):
|
|
27
|
+
"""
|
|
28
|
+
Import data from a single step file given by FEPX.
|
|
29
|
+
|
|
30
|
+
The type of returns is inferred from the data one wants to parse
|
|
31
|
+
(according the `FEPX documentation <https://fepx.info/doc/output.html>`_ ).
|
|
32
|
+
|
|
33
|
+
Parameters
|
|
34
|
+
----------
|
|
35
|
+
file : str
|
|
36
|
+
Path to the file to read
|
|
37
|
+
dtype : type, optional
|
|
38
|
+
If provided, sets the type of returned array. It can be:
|
|
39
|
+
- float
|
|
40
|
+
- SecondOrderTensor
|
|
41
|
+
- SymmetricSecondOrderTensor
|
|
42
|
+
- SkewSymmetricSecondOrderTensor
|
|
43
|
+
- stressTensor
|
|
44
|
+
- strainTensor
|
|
45
|
+
|
|
46
|
+
Returns
|
|
47
|
+
-------
|
|
48
|
+
SecondOrderTensor or numpy.ndarray
|
|
49
|
+
Array of second-order tensors built from the read data. The array will be of shape (n,), where n is the number
|
|
50
|
+
of elements in the mesh.
|
|
51
|
+
"""
|
|
52
|
+
data = pd.read_csv(file, header=None, sep=' ')
|
|
53
|
+
array = data.to_numpy()
|
|
54
|
+
base_name = os.path.splitext(os.path.basename(file))[0]
|
|
55
|
+
if dtype is None:
|
|
56
|
+
if base_name in DTYPES:
|
|
57
|
+
dtype = DTYPES[base_name]
|
|
58
|
+
else:
|
|
59
|
+
dtype = float
|
|
60
|
+
if issubclass(dtype,SymmetricSecondOrderTensor):
|
|
61
|
+
return dtype.from_Voigt(array, voigt_map=[1,1,1,1,1,1])
|
|
62
|
+
elif dtype == SkewSymmetricSecondOrderTensor:
|
|
63
|
+
zeros = np.zeros(array.shape[0])
|
|
64
|
+
mat = np.array([[ zeros, array[:, 0], array[:, 1]],
|
|
65
|
+
[-array[:, 0], zeros, array[:, 2]],
|
|
66
|
+
[-array[:, 1], -array[:, 2], zeros ]]).transpose((2, 0, 1))
|
|
67
|
+
return SkewSymmetricSecondOrderTensor(mat)
|
|
68
|
+
elif dtype == SecondOrderTensor:
|
|
69
|
+
length = array.shape[0]
|
|
70
|
+
return SecondOrderTensor(array.reshape((length,3,3)))
|
|
71
|
+
elif dtype == float:
|
|
72
|
+
return array
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def from_results_folder(folder, dtype=None):
|
|
77
|
+
"""
|
|
78
|
+
Import all data of a given field from FEPX results folder.
|
|
79
|
+
|
|
80
|
+
The type of returns is inferred from the data one wants to parse
|
|
81
|
+
(according the `FEPX documentation <https://fepx.info/doc/output.html>`_ ).
|
|
82
|
+
|
|
83
|
+
Parameters
|
|
84
|
+
----------
|
|
85
|
+
folder : str
|
|
86
|
+
Path to the folder to read the results from
|
|
87
|
+
dtype : type, optional
|
|
88
|
+
If provided, sets the type of returned array. It can be:
|
|
89
|
+
- float
|
|
90
|
+
- SecondOrderTensor
|
|
91
|
+
- SymmetricSecondOrderTensor
|
|
92
|
+
- SkewSymmetricSecondOrderTensor
|
|
93
|
+
- stressTensor
|
|
94
|
+
- strainTensor
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
SecondOrderTensor or numpy.ndarray
|
|
99
|
+
Array of second-order tensors built from the read data. The array will be of shape (m, n), where m is the number
|
|
100
|
+
of time increment n is the number of elements in the mesh.
|
|
101
|
+
"""
|
|
102
|
+
dir_path = Path(folder)
|
|
103
|
+
folder_name = dir_path.name
|
|
104
|
+
if not dir_path.is_dir():
|
|
105
|
+
raise ValueError(f"{folder} is not a valid directory.")
|
|
106
|
+
constructor = None
|
|
107
|
+
array = []
|
|
108
|
+
for file in dir_path.iterdir():
|
|
109
|
+
if file.is_file() and file.name.startswith(folder_name):
|
|
110
|
+
data_file = from_step_file(str(file), dtype=dtype)
|
|
111
|
+
if constructor is None:
|
|
112
|
+
constructor = type(data_file)
|
|
113
|
+
elif constructor != type(data_file):
|
|
114
|
+
raise ValueError('The types of data contained in {} seem to be inconsistent.'.format(folder))
|
|
115
|
+
array.append(data_file)
|
|
116
|
+
if constructor == np.ndarray:
|
|
117
|
+
return np.stack(array, axis=0)
|
|
118
|
+
else:
|
|
119
|
+
return constructor.stack(array)
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from Elasticipy.tensors.second_order import SecondOrderTensor, SymmetricSecondOrderTensor
|
|
2
|
+
from Elasticipy.tensors.stress_strain import StressTensor
|
|
3
|
+
import pandas as pd
|
|
4
|
+
import numpy as np
|
|
5
|
+
|
|
6
|
+
def _rebuild_tensor(F11, F22, F33, F12, F13, F21, F23, F31, F32):
|
|
7
|
+
return np.array([[F11, F12, F13], [F21, F22, F23], [F31, F32, F33]]).transpose((2,0,1))
|
|
8
|
+
|
|
9
|
+
def from_quadrature_file(file, returns='stress'):
|
|
10
|
+
"""
|
|
11
|
+
Read data from quadrature output file generated by PRISMS plasticity.
|
|
12
|
+
|
|
13
|
+
These files, usually named QuadratureOutputsXXX.csv (where XXX denotes the time increment), contain large amount of
|
|
14
|
+
data, such as gradient components, stress values, grain ID etc.
|
|
15
|
+
|
|
16
|
+
Parameters
|
|
17
|
+
----------
|
|
18
|
+
file : str
|
|
19
|
+
Path to quadrature file
|
|
20
|
+
returns : str or list of str or tuple of str
|
|
21
|
+
name(s) of requested field(s). They can be:
|
|
22
|
+
- grain ID
|
|
23
|
+
- phase ID
|
|
24
|
+
- det(J)
|
|
25
|
+
- twin
|
|
26
|
+
- coordinates
|
|
27
|
+
- orientation
|
|
28
|
+
- elastic gradient
|
|
29
|
+
- plastic gradient
|
|
30
|
+
- stress
|
|
31
|
+
Returns
|
|
32
|
+
-------
|
|
33
|
+
ndarray or SecondOrderTensor or StressTensor
|
|
34
|
+
The number of returned values depends on the requested fields.
|
|
35
|
+
"""
|
|
36
|
+
data=pd.read_csv(file, header=None, dtype=float, usecols=range(0,37))
|
|
37
|
+
grainID, phaseID, detJ, twin, x, y, z, rot1, rot2, rot3, *other = data.T.to_numpy()
|
|
38
|
+
Fe11, Fe22, Fe33, Fe12, Fe13, Fe21, Fe23, Fe31, Fe32, *FpStress = other
|
|
39
|
+
Fp11, Fp22, Fp33, Fp12, Fp13, Fp21, Fp23, Fp31, Fp32, *stress_compo = FpStress
|
|
40
|
+
if isinstance(returns, str):
|
|
41
|
+
returns_list = (returns,)
|
|
42
|
+
else:
|
|
43
|
+
returns_list = returns
|
|
44
|
+
returned_values = []
|
|
45
|
+
for r in returns_list:
|
|
46
|
+
rl = r.lower()
|
|
47
|
+
if (rl == 'grain id') or (rl == 'grainid'):
|
|
48
|
+
returned_values.append(grainID)
|
|
49
|
+
elif (rl == 'phase id') or (rl == 'phaseid'):
|
|
50
|
+
returned_values.append(phaseID)
|
|
51
|
+
elif rl == 'det(j)':
|
|
52
|
+
returned_values.append(detJ)
|
|
53
|
+
elif rl == 'twin':
|
|
54
|
+
returned_values.append(twin)
|
|
55
|
+
elif rl == 'coordinates':
|
|
56
|
+
returned_values.append(np.array([x, y, z]).T)
|
|
57
|
+
elif rl == 'orientation':
|
|
58
|
+
returned_values.append(np.array([rot1, rot2, rot3]).T)
|
|
59
|
+
elif rl == 'elastic gradient':
|
|
60
|
+
Fe = _rebuild_tensor(Fe11, Fe22, Fe33, Fe12, Fe13, Fe21, Fe23, Fe31, Fe32)
|
|
61
|
+
returned_values.append(SecondOrderTensor(Fe))
|
|
62
|
+
elif rl == 'plastic gradient':
|
|
63
|
+
Fp = _rebuild_tensor(Fp11, Fp22, Fp33, Fp12, Fp13, Fp21, Fp23, Fp31, Fp32)
|
|
64
|
+
returned_values.append(SecondOrderTensor(Fp))
|
|
65
|
+
elif rl == 'stress':
|
|
66
|
+
stress = _rebuild_tensor(*stress_compo)
|
|
67
|
+
returned_values.append(StressTensor(stress))
|
|
68
|
+
else:
|
|
69
|
+
raise ValueError('Unknown return type')
|
|
70
|
+
if isinstance(returns, str):
|
|
71
|
+
return returned_values[0]
|
|
72
|
+
else:
|
|
73
|
+
return tuple(returned_values)
|
|
74
|
+
|
|
75
|
+
def from_stressstrain_file(file):
|
|
76
|
+
"""
|
|
77
|
+
Read data from stress-strain file generated by PRISMS plasticity.
|
|
78
|
+
|
|
79
|
+
This file is usually named stressstrain.txt
|
|
80
|
+
|
|
81
|
+
Parameters
|
|
82
|
+
----------
|
|
83
|
+
file : str
|
|
84
|
+
Path to stress-strain file
|
|
85
|
+
|
|
86
|
+
Returns
|
|
87
|
+
-------
|
|
88
|
+
SymmetricSecondOrderTensor
|
|
89
|
+
Average Green-Lagrange strain tensor
|
|
90
|
+
StressTensor
|
|
91
|
+
Average stress tensor
|
|
92
|
+
"""
|
|
93
|
+
data = pd.read_csv(file, sep='\t')
|
|
94
|
+
Exx, Exy, Exz = data['Exx'], data['Exy'], data['Exz']
|
|
95
|
+
Eyy, Eyz = data['Eyy'], data['Eyz']
|
|
96
|
+
Ezz = data['Ezz']
|
|
97
|
+
E = np.array([[Exx, Exy, Exz], [Exy, Eyy, Eyz], [Exz, Eyz, Ezz]]).transpose((2,0,1))
|
|
98
|
+
Txx, Txy, Txz = data['Txx'], data['Txy'], data['Txz']
|
|
99
|
+
Tyy, Tyz = data['Tyy'], data['Tyz']
|
|
100
|
+
Tzz = data['Tzz']
|
|
101
|
+
T = np.array([[Txx, Txy, Txz], [Txy, Tyy, Tyz], [Txz, Tyz, Tzz]]).transpose((2, 0, 1))
|
|
102
|
+
return SymmetricSecondOrderTensor(E), StressTensor(T)
|
|
103
|
+
|