elasticipy 4.2.0__py3-none-any.whl → 5.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- Elasticipy/gui.py +101 -2
- Elasticipy/interfaces/FEPX.py +3 -3
- Elasticipy/plasticity.py +179 -34
- Elasticipy/spherical_function.py +43 -4
- Elasticipy/tensors/elasticity.py +667 -141
- Elasticipy/tensors/fourth_order.py +895 -172
- Elasticipy/tensors/mapping.py +48 -0
- Elasticipy/tensors/second_order.py +49 -7
- Elasticipy/tensors/stress_strain.py +209 -3
- Elasticipy/tensors/thermal_expansion.py +6 -1
- {elasticipy-4.2.0.dist-info → elasticipy-5.0.0.dist-info}/METADATA +40 -21
- elasticipy-5.0.0.dist-info/RECORD +24 -0
- elasticipy-5.0.0.dist-info/entry_points.txt +2 -0
- elasticipy-4.2.0.dist-info/RECORD +0 -23
- {elasticipy-4.2.0.dist-info → elasticipy-5.0.0.dist-info}/WHEEL +0 -0
- {elasticipy-4.2.0.dist-info → elasticipy-5.0.0.dist-info}/licenses/LICENSE +0 -0
- {elasticipy-4.2.0.dist-info → elasticipy-5.0.0.dist-info}/top_level.txt +0 -0
Elasticipy/gui.py
CHANGED
|
@@ -128,9 +128,84 @@ class ElasticityGUI(QMainWindow):
|
|
|
128
128
|
self.calculate_button.clicked.connect(self.calculate_and_plot)
|
|
129
129
|
left_panel_layout.addWidget(self.calculate_button)
|
|
130
130
|
|
|
131
|
+
# Add horizontal separator
|
|
132
|
+
separator = QFrame()
|
|
133
|
+
separator.setFrameShape(QFrame.HLine)
|
|
134
|
+
separator.setFrameShadow(QFrame.Sunken)
|
|
135
|
+
left_panel_layout.addWidget(separator)
|
|
136
|
+
|
|
137
|
+
############################################
|
|
138
|
+
# Numeric results
|
|
139
|
+
############################################
|
|
140
|
+
self.result_labels = {}
|
|
141
|
+
RESULT_GROUPS = {
|
|
142
|
+
"Young modulus": [
|
|
143
|
+
("E_mean", "Mean"),
|
|
144
|
+
("E_voigt", "Voigt"),
|
|
145
|
+
("E_reuss", "Reuss"),
|
|
146
|
+
("E_hill", "Hill"),
|
|
147
|
+
],
|
|
148
|
+
"Shear modulus": [
|
|
149
|
+
("G_mean", "Mean"),
|
|
150
|
+
("G_voigt", "Voigt"),
|
|
151
|
+
("G_reuss", "Reuss"),
|
|
152
|
+
("G_hill", "Hill"),
|
|
153
|
+
],
|
|
154
|
+
"Poisson ratio": [
|
|
155
|
+
("nu_mean", "Mean"),
|
|
156
|
+
("nu_voigt", "Voigt"),
|
|
157
|
+
("nu_reuss", "Reuss"),
|
|
158
|
+
("nu_hill", "Hill"),
|
|
159
|
+
],
|
|
160
|
+
"Linear compressibility": [
|
|
161
|
+
("Beta_mean", "Mean"),
|
|
162
|
+
("Beta_voigt", "Voigt"),
|
|
163
|
+
("Beta_reuss", "Reuss"),
|
|
164
|
+
("Beta_hill", "Hill"),
|
|
165
|
+
],
|
|
166
|
+
"Other": [
|
|
167
|
+
("K", "Bulk modulus"),
|
|
168
|
+
("Z", "Zener ratio"),
|
|
169
|
+
("A", "Univ. anisotropy factor"),
|
|
170
|
+
]
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
############################################
|
|
174
|
+
# Numeric results (grouped)
|
|
175
|
+
############################################
|
|
176
|
+
self.result_labels = {}
|
|
177
|
+
for group_name, items in RESULT_GROUPS.items():
|
|
178
|
+
|
|
179
|
+
# Group title
|
|
180
|
+
group_label = QLabel(group_name + ":")
|
|
181
|
+
group_label.setStyleSheet("font-weight: bold; margin-top: 6px;")
|
|
182
|
+
left_panel_layout.addWidget(group_label)
|
|
183
|
+
|
|
184
|
+
# Indented layout
|
|
185
|
+
indent_layout = QVBoxLayout()
|
|
186
|
+
indent_layout.setContentsMargins(15, 0, 0, 0)
|
|
187
|
+
|
|
188
|
+
for key, label_text in items:
|
|
189
|
+
row = QHBoxLayout()
|
|
190
|
+
|
|
191
|
+
label_name = QLabel(f"{label_text}:")
|
|
192
|
+
label_value = QLabel("—")
|
|
193
|
+
label_value.setMinimumWidth(100)
|
|
194
|
+
label_value.setStyleSheet("font-family: Consolas, Courier;")
|
|
195
|
+
|
|
196
|
+
self.result_labels[key] = label_value
|
|
197
|
+
|
|
198
|
+
row.addWidget(label_name)
|
|
199
|
+
row.addStretch()
|
|
200
|
+
row.addWidget(label_value)
|
|
201
|
+
|
|
202
|
+
indent_layout.addLayout(row)
|
|
203
|
+
|
|
204
|
+
left_panel_layout.addLayout(indent_layout)
|
|
205
|
+
|
|
131
206
|
# Fill space
|
|
132
207
|
left_panel_layout.addStretch()
|
|
133
|
-
bottom_layout.addLayout(left_panel_layout)
|
|
208
|
+
bottom_layout.addLayout(left_panel_layout,1)
|
|
134
209
|
|
|
135
210
|
############################################
|
|
136
211
|
# Plotting area
|
|
@@ -143,7 +218,7 @@ class ElasticityGUI(QMainWindow):
|
|
|
143
218
|
|
|
144
219
|
self.figure = Figure()
|
|
145
220
|
self.canvas = FigureCanvas(self.figure)
|
|
146
|
-
bottom_layout.addWidget(self.canvas)
|
|
221
|
+
bottom_layout.addWidget(self.canvas,4)
|
|
147
222
|
|
|
148
223
|
#######################################################################################
|
|
149
224
|
# Main widget
|
|
@@ -160,6 +235,8 @@ class ElasticityGUI(QMainWindow):
|
|
|
160
235
|
self.plotting_selector.setCurrentText('Young modulus')
|
|
161
236
|
self.which_selector.setEnabled(False)
|
|
162
237
|
|
|
238
|
+
self.C_matrix = np.zeros((6, 6))
|
|
239
|
+
|
|
163
240
|
def update_fields(self):
|
|
164
241
|
# Deactivate unused fields
|
|
165
242
|
active_fields = self.selected_symmetry().required
|
|
@@ -226,6 +303,25 @@ class ElasticityGUI(QMainWindow):
|
|
|
226
303
|
else:
|
|
227
304
|
value.plot_as_pole_figure(fig=self.figure, **plot_kwargs)
|
|
228
305
|
self.canvas.draw()
|
|
306
|
+
if not np.all(self.C_matrix == Csym):
|
|
307
|
+
self.result_labels["E_mean"].setText(f"{stiff.Young_modulus.mean():.3f}")
|
|
308
|
+
self.result_labels["G_mean"].setText(f"{stiff.shear_modulus.mean():.3f}")
|
|
309
|
+
self.result_labels["nu_mean"].setText(f"{stiff.Poisson_ratio.mean():.3f}")
|
|
310
|
+
self.result_labels["Beta_mean"].setText(f"{stiff.linear_compressibility.mean():.3f}")
|
|
311
|
+
for method in ['voigt', 'reuss', 'hill']:
|
|
312
|
+
C = stiff.average(method=method)
|
|
313
|
+
self.result_labels[f"E_{method}"].setText(f"{C.Young_modulus.eval([1,0,0]):.3f}")
|
|
314
|
+
self.result_labels[f"G_{method}"].setText(f"{C.shear_modulus.eval([1, 0, 0],[0,1,0]):.3f}")
|
|
315
|
+
self.result_labels[f"nu_{method}"].setText(f"{C.Poisson_ratio.eval([1, 0, 0], [0, 1, 0]):.3f}")
|
|
316
|
+
self.result_labels[f"Beta_{method}"].setText(f"{C.linear_compressibility.eval([1, 0, 0]):.3f}")
|
|
317
|
+
self.result_labels["K"].setText(f"{stiff.bulk_modulus:.3f}")
|
|
318
|
+
try:
|
|
319
|
+
Z = stiff.Zener_ratio()
|
|
320
|
+
self.result_labels["Z"].setText(f"{stiff.Zener_ratio():.3f}")
|
|
321
|
+
except ValueError:
|
|
322
|
+
self.result_labels["Z"].setText("—")
|
|
323
|
+
self.result_labels["A"].setText(f"{stiff.universal_anisotropy:.3f}")
|
|
324
|
+
self.C_matrix = Csym
|
|
229
325
|
|
|
230
326
|
except ValueError as inst:
|
|
231
327
|
QMessageBox.critical(self, "Singular stiffness", inst.__str__(), QMessageBox.Ok)
|
|
@@ -261,3 +357,6 @@ def crystal_elastic_plotter():
|
|
|
261
357
|
window = ElasticityGUI()
|
|
262
358
|
window.show()
|
|
263
359
|
sys.exit(app.exec_())
|
|
360
|
+
|
|
361
|
+
if __name__ == "__main__":
|
|
362
|
+
crystal_elastic_plotter()
|
Elasticipy/interfaces/FEPX.py
CHANGED
|
@@ -90,14 +90,14 @@ def from_results_folder(folder, dtype=None):
|
|
|
90
90
|
- SecondOrderTensor
|
|
91
91
|
- SymmetricSecondOrderTensor
|
|
92
92
|
- SkewSymmetricSecondOrderTensor
|
|
93
|
-
-
|
|
94
|
-
-
|
|
93
|
+
- StressTensor
|
|
94
|
+
- StrainTensor
|
|
95
95
|
|
|
96
96
|
Returns
|
|
97
97
|
-------
|
|
98
98
|
SecondOrderTensor or numpy.ndarray
|
|
99
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
|
-
|
|
100
|
+
of time increment n is the number of elements in the mesh.
|
|
101
101
|
"""
|
|
102
102
|
dir_path = Path(folder)
|
|
103
103
|
folder_name = dir_path.name
|
Elasticipy/plasticity.py
CHANGED
|
@@ -37,11 +37,47 @@ class IsotropicHardening:
|
|
|
37
37
|
' current strain: {}'.format(self.plastic_strain))
|
|
38
38
|
|
|
39
39
|
def flow_stress(self, strain, **kwargs):
|
|
40
|
+
"""
|
|
41
|
+
Compute the stress from the cumulative plastic strain
|
|
42
|
+
|
|
43
|
+
Parameters
|
|
44
|
+
----------
|
|
45
|
+
strain : float
|
|
46
|
+
Equivalent Plastic strain
|
|
47
|
+
kwargs
|
|
48
|
+
Additional arguments passed to the function
|
|
49
|
+
|
|
50
|
+
Returns
|
|
51
|
+
-------
|
|
52
|
+
float or np.ndarray
|
|
53
|
+
|
|
54
|
+
Examples
|
|
55
|
+
--------
|
|
56
|
+
As an example, we consider a Jonhson-Cook model:
|
|
57
|
+
|
|
58
|
+
>>> from Elasticipy.plasticity import JohnsonCook
|
|
59
|
+
>>> JC = JohnsonCook(A=792, B=510, n=0.26)
|
|
60
|
+
>>> print(JC)
|
|
61
|
+
Johnson-Cook plasticity model
|
|
62
|
+
type: Isotropic
|
|
63
|
+
criterion: von Mises
|
|
64
|
+
current strain: 0.0
|
|
65
|
+
|
|
66
|
+
>>> JC.flow_stress(0.0) # Check that the yield stress = A
|
|
67
|
+
792.0
|
|
68
|
+
|
|
69
|
+
In order to get the full tensile curve in 0 to 10% strain range:
|
|
70
|
+
|
|
71
|
+
>>> import numpy as np
|
|
72
|
+
>>> JC.flow_stress(np.linspace(0,0.1,5)) # Check that the yield stress = B
|
|
73
|
+
array([ 792. , 987.44950657, 1026.04662195, 1052.067513 ,
|
|
74
|
+
1072.26584567])
|
|
75
|
+
"""
|
|
40
76
|
pass
|
|
41
77
|
|
|
42
78
|
def apply_strain(self, strain, **kwargs):
|
|
43
79
|
"""
|
|
44
|
-
Apply strain to the current
|
|
80
|
+
Apply strain to the current plasticity model.
|
|
45
81
|
|
|
46
82
|
This function updates the internal variable to store hardening state.
|
|
47
83
|
|
|
@@ -59,6 +95,38 @@ class IsotropicHardening:
|
|
|
59
95
|
See Also
|
|
60
96
|
--------
|
|
61
97
|
flow_stress : compute the flow stress, given a cumulative equivalent strain
|
|
98
|
+
|
|
99
|
+
Examples
|
|
100
|
+
--------
|
|
101
|
+
As an example, we consider the Johnson-Cook plasticity model:
|
|
102
|
+
|
|
103
|
+
>>> from Elasticipy.plasticity import JohnsonCook
|
|
104
|
+
>>> JC = JohnsonCook(A=792, B=510, n=0.26)
|
|
105
|
+
>>> print(JC)
|
|
106
|
+
Johnson-Cook plasticity model
|
|
107
|
+
type: Isotropic
|
|
108
|
+
criterion: von Mises
|
|
109
|
+
current strain: 0.0
|
|
110
|
+
|
|
111
|
+
>>> stress = JC.apply_strain(0.1)
|
|
112
|
+
>>> print(stress)
|
|
113
|
+
1072.2658456673885
|
|
114
|
+
>>> print(JC)
|
|
115
|
+
Johnson-Cook plasticity model
|
|
116
|
+
type: Isotropic
|
|
117
|
+
criterion: von Mises
|
|
118
|
+
current strain: 0.1
|
|
119
|
+
|
|
120
|
+
Obvisously, the applied strain is cumulative:
|
|
121
|
+
|
|
122
|
+
>>> stress = JC.apply_strain(0.1)
|
|
123
|
+
>>> print(stress)
|
|
124
|
+
1127.612381818713
|
|
125
|
+
>>> print(JC)
|
|
126
|
+
Johnson-Cook plasticity model
|
|
127
|
+
type: Isotropic
|
|
128
|
+
criterion: von Mises
|
|
129
|
+
current strain: 0.2
|
|
62
130
|
"""
|
|
63
131
|
if isinstance(strain, float):
|
|
64
132
|
self.plastic_strain += np.abs(strain)
|
|
@@ -68,10 +136,112 @@ class IsotropicHardening:
|
|
|
68
136
|
raise ValueError('The applied strain must be float of StrainTensor')
|
|
69
137
|
return self.flow_stress(self.plastic_strain, **kwargs)
|
|
70
138
|
|
|
71
|
-
def compute_strain_increment(self, stress, **kwargs):
|
|
72
|
-
|
|
139
|
+
def compute_strain_increment(self, stress, criterion='von Mises', apply_strain=True, **kwargs):
|
|
140
|
+
"""
|
|
141
|
+
Given the equivalent stress, compute the strain increment with respect to the normality rule.
|
|
142
|
+
|
|
143
|
+
Parameters
|
|
144
|
+
----------
|
|
145
|
+
stress : float or StressTensor
|
|
146
|
+
Equivalent stress to compute the stress from, or full stress tensor.
|
|
147
|
+
apply_strain : bool, optional
|
|
148
|
+
If true, the plasticity model will be updated to account for the applied strain (hardening)
|
|
149
|
+
criterion : str, optional
|
|
150
|
+
Plasticity criterion to consider to compute the equivalent stress and apply the normality rule.
|
|
151
|
+
It can be 'von Mises', 'Tresca' or 'J2'. 'J2' is equivalent to 'von Mises'.
|
|
152
|
+
kwargs
|
|
153
|
+
Keyword arguments passed to the model
|
|
154
|
+
|
|
155
|
+
Returns
|
|
156
|
+
-------
|
|
157
|
+
StrainTensor or float
|
|
158
|
+
Increment of plastic strain. If the input stress is float, only the magnitude of the increment will be
|
|
159
|
+
returned (float value). If the stress is of type StressTensor, the returned value will be a full
|
|
160
|
+
StrainTensor.
|
|
161
|
+
|
|
162
|
+
See Also
|
|
163
|
+
--------
|
|
164
|
+
apply_strain : apply strain to the JC model and updates its hardening value
|
|
165
|
+
|
|
166
|
+
Examples
|
|
167
|
+
--------
|
|
168
|
+
As an example, we consider the Johnson-Cook plasticity model:
|
|
169
|
+
|
|
170
|
+
>>> from Elasticipy.plasticity import JohnsonCook
|
|
171
|
+
>>> JC = JohnsonCook(A=792, B=510, n=0.26)
|
|
172
|
+
|
|
173
|
+
The yield stress is equal to A here. So consider a tensile stress whose magnitude below A:
|
|
174
|
+
|
|
175
|
+
>>> from Elasticipy.tensors.stress_strain import StressTensor
|
|
176
|
+
>>> sigma = StressTensor.tensile([1,0,0], 700)
|
|
177
|
+
>>> strain_inc = JC.compute_strain_increment(sigma)
|
|
178
|
+
>>> print(strain_inc)
|
|
179
|
+
Strain tensor
|
|
180
|
+
[[ 0. 0. 0.]
|
|
181
|
+
[ 0. -0. 0.]
|
|
182
|
+
[ 0. 0. -0.]]
|
|
183
|
+
|
|
184
|
+
whereas if the stress is larger than A:
|
|
185
|
+
|
|
186
|
+
>>> sigma = StressTensor.tensile([1,0,0], 800)
|
|
187
|
+
>>> strain_inc = JC.compute_strain_increment(sigma)
|
|
188
|
+
>>> print(strain_inc)
|
|
189
|
+
Strain tensor
|
|
190
|
+
[[ 1.14733854e-07 0.00000000e+00 0.00000000e+00]
|
|
191
|
+
[ 0.00000000e+00 -5.73669268e-08 0.00000000e+00]
|
|
192
|
+
[ 0.00000000e+00 0.00000000e+00 -5.73669268e-08]]
|
|
193
|
+
|
|
194
|
+
Check out that the JC model has been updated:
|
|
195
|
+
|
|
196
|
+
>>> print(JC)
|
|
197
|
+
Johnson-Cook plasticity model
|
|
198
|
+
type: Isotropic
|
|
199
|
+
criterion: von Mises
|
|
200
|
+
current strain: 1.1473385353505149e-07
|
|
201
|
+
|
|
202
|
+
Therefore, the yield stress has increased because of hardening. For instance, if we apply the same stress has
|
|
203
|
+
before, we get:
|
|
204
|
+
|
|
205
|
+
>>> JC.compute_strain_increment(sigma)
|
|
206
|
+
Strain tensor
|
|
207
|
+
[[ 0. 0. 0.]
|
|
208
|
+
[ 0. -0. 0.]
|
|
209
|
+
[ 0. 0. -0.]]
|
|
210
|
+
"""
|
|
73
211
|
|
|
74
212
|
def reset_strain(self):
|
|
213
|
+
"""
|
|
214
|
+
Update the internal variable so that the plastic strain is reset to zero.
|
|
215
|
+
|
|
216
|
+
Returns
|
|
217
|
+
-------
|
|
218
|
+
None
|
|
219
|
+
|
|
220
|
+
Examples
|
|
221
|
+
--------
|
|
222
|
+
As an example, we consider the Johnson-Cook plasticity model:
|
|
223
|
+
|
|
224
|
+
>>> from Elasticipy.plasticity import JohnsonCook
|
|
225
|
+
>>> JC = JohnsonCook(A=792, B=510, n=0.26)
|
|
226
|
+
|
|
227
|
+
First apply a strain increment:
|
|
228
|
+
|
|
229
|
+
>>> stress = JC.apply_strain(0.1)
|
|
230
|
+
>>> print(JC)
|
|
231
|
+
Johnson-Cook plasticity model
|
|
232
|
+
type: Isotropic
|
|
233
|
+
criterion: von Mises
|
|
234
|
+
current strain: 0.1
|
|
235
|
+
|
|
236
|
+
If one wants to reset the JC, without recreating it:
|
|
237
|
+
|
|
238
|
+
>>> stress = JC.reset_strain()
|
|
239
|
+
>>> print(JC)
|
|
240
|
+
Johnson-Cook plasticity model
|
|
241
|
+
type: Isotropic
|
|
242
|
+
criterion: von Mises
|
|
243
|
+
current strain: 0.0
|
|
244
|
+
"""
|
|
75
245
|
self.plastic_strain = 0.0
|
|
76
246
|
|
|
77
247
|
|
|
@@ -153,6 +323,7 @@ class JohnsonCook(IsotropicHardening):
|
|
|
153
323
|
eps_p.
|
|
154
324
|
T : float or list or tuple or np.ndarray
|
|
155
325
|
Temperature. If float, the temperature is supposed to be homogeneous for every value of eps_p.
|
|
326
|
+
|
|
156
327
|
Returns
|
|
157
328
|
-------
|
|
158
329
|
float or numpy.ndarray
|
|
@@ -177,35 +348,7 @@ class JohnsonCook(IsotropicHardening):
|
|
|
177
348
|
|
|
178
349
|
return stress
|
|
179
350
|
|
|
180
|
-
|
|
181
|
-
|
|
182
351
|
def compute_strain_increment(self, stress, T=None, apply_strain=True, criterion='von Mises'):
|
|
183
|
-
"""
|
|
184
|
-
Given the equivalent stress, compute the strain increment with respect to the normality rule.
|
|
185
|
-
|
|
186
|
-
Parameters
|
|
187
|
-
----------
|
|
188
|
-
stress : float or StressTensor
|
|
189
|
-
Equivalent stress to compute the stress from, or full stress tensor.
|
|
190
|
-
T : float
|
|
191
|
-
Temperature
|
|
192
|
-
apply_strain : bool, optional
|
|
193
|
-
If true, the JC model will be updated to account for the applied strain (hardening)
|
|
194
|
-
criterion : str, optional
|
|
195
|
-
Plasticity criterion to consider to compute the equivalent stress and apply the normality rule.
|
|
196
|
-
It can be 'von Mises', 'Tresca' or 'J2'. 'J2' is equivalent to 'von Mises'.
|
|
197
|
-
|
|
198
|
-
Returns
|
|
199
|
-
-------
|
|
200
|
-
StrainTensor or float
|
|
201
|
-
Increment of plastic strain. If the input stress is float, only the magnitude of the increment will be
|
|
202
|
-
returned (float value). If the stress is of type StressTensor, the returned value will be a full
|
|
203
|
-
StrainTensor.
|
|
204
|
-
|
|
205
|
-
See Also
|
|
206
|
-
--------
|
|
207
|
-
apply_strain : apply strain to the JC model and updates its hardening value
|
|
208
|
-
"""
|
|
209
352
|
if isinstance(stress, StressTensor):
|
|
210
353
|
eq_stress = self.criterion.eq_stress(stress)
|
|
211
354
|
else:
|
|
@@ -242,9 +385,6 @@ class JohnsonCook(IsotropicHardening):
|
|
|
242
385
|
return strain_increment
|
|
243
386
|
|
|
244
387
|
def reset_strain(self):
|
|
245
|
-
"""
|
|
246
|
-
Reinitialize the plastic strain to 0
|
|
247
|
-
"""
|
|
248
388
|
self.plastic_strain = 0.0
|
|
249
389
|
|
|
250
390
|
|
|
@@ -351,7 +491,12 @@ class DruckerPrager(PlasticityCriterion):
|
|
|
351
491
|
-----
|
|
352
492
|
The pressure-dependent DG plasticity criterion assumes that the equivalent stress is defined as:
|
|
353
493
|
|
|
494
|
+
.. math::
|
|
495
|
+
|
|
496
|
+
\\alpha I_1 + \\sqrt{J_2}
|
|
354
497
|
|
|
498
|
+
where :math:`I_1` is the first invariant of the stress tensor, and :math:`J_2` is the second invariant of the
|
|
499
|
+
deviatoric stress tensor.
|
|
355
500
|
"""
|
|
356
501
|
self.alpha = alpha
|
|
357
502
|
|
Elasticipy/spherical_function.py
CHANGED
|
@@ -237,6 +237,23 @@ class SphericalFunction:
|
|
|
237
237
|
See Also
|
|
238
238
|
--------
|
|
239
239
|
eval_spherical : evaluate the function along a given direction given using the spherical coordinates
|
|
240
|
+
|
|
241
|
+
Examples
|
|
242
|
+
--------
|
|
243
|
+
As an example of spherical function, we consider the Young modulus estimated from a stiffness tensor:
|
|
244
|
+
|
|
245
|
+
>>> from Elasticipy.tensors.elasticity import StiffnessTensor
|
|
246
|
+
>>> E = StiffnessTensor.cubic(C11=110, C12=54, C44=60).Young_modulus
|
|
247
|
+
|
|
248
|
+
The Young modulus along x direction is:
|
|
249
|
+
|
|
250
|
+
>>> E.eval([1,0,0])
|
|
251
|
+
74.4390243902439
|
|
252
|
+
|
|
253
|
+
The Young moduli along a set a directions can be evaluated at once. E.g. along x, y and z:
|
|
254
|
+
|
|
255
|
+
>>> E.eval([[1,0,0], [0,1,0], [0,0,1]])
|
|
256
|
+
array([74.43902439, 74.43902439, 74.43902439])
|
|
240
257
|
"""
|
|
241
258
|
u_vec = np.atleast_2d(u)
|
|
242
259
|
norm = np.linalg.norm(u_vec, axis=1)
|
|
@@ -244,11 +261,20 @@ class SphericalFunction:
|
|
|
244
261
|
raise ValueError('The input vector cannot be zeros')
|
|
245
262
|
u_vec = (u_vec.T / norm).T
|
|
246
263
|
values = self.fun(u_vec)
|
|
247
|
-
if isinstance(u, list) and np.array(u).shape == (3,):
|
|
264
|
+
if isinstance(u, list) and np.array(u).shape == (3,) and isinstance(values, np.ndarray):
|
|
248
265
|
return values[0]
|
|
249
266
|
else:
|
|
250
267
|
return values
|
|
251
268
|
|
|
269
|
+
def __eq__(self, other):
|
|
270
|
+
if type(other) is self.__class__:
|
|
271
|
+
n_evals = 10000
|
|
272
|
+
_, a_evals = self.evaluate_on_spherical_grid(n_evals)
|
|
273
|
+
_, b_evals = other.evaluate_on_spherical_grid(n_evals)
|
|
274
|
+
return np.allclose(a_evals, b_evals)
|
|
275
|
+
else:
|
|
276
|
+
return False
|
|
277
|
+
|
|
252
278
|
def eval_spherical(self, *args, degrees=False):
|
|
253
279
|
"""
|
|
254
280
|
Evaluate value along a given (set of) direction(s) defined by its (their) spherical coordinates.
|
|
@@ -269,7 +295,18 @@ class SphericalFunction:
|
|
|
269
295
|
|
|
270
296
|
See Also
|
|
271
297
|
--------
|
|
272
|
-
eval : evaluate the function along a direction given by its cartesian
|
|
298
|
+
eval : evaluate the function along a direction given by its cartesian
|
|
299
|
+
|
|
300
|
+
Examples
|
|
301
|
+
--------
|
|
302
|
+
>>> from Elasticipy.tensors.elasticity import StiffnessTensor
|
|
303
|
+
>>> E = StiffnessTensor.cubic(C11=110, C12=54, C44=60).Young_modulus
|
|
304
|
+
|
|
305
|
+
In spherical coordinates, the x direction is defined by theta=90° and phi=0. Therefore, the Young modulus along
|
|
306
|
+
x direction is:
|
|
307
|
+
|
|
308
|
+
>>> E.eval_spherical([0, 90], degrees=True)
|
|
309
|
+
74.4390243902439
|
|
273
310
|
"""
|
|
274
311
|
angles = np.atleast_2d(args)
|
|
275
312
|
if degrees:
|
|
@@ -277,7 +314,9 @@ class SphericalFunction:
|
|
|
277
314
|
phi, theta = angles.T
|
|
278
315
|
u = sph2cart(phi, theta)
|
|
279
316
|
values = self.eval(u)
|
|
280
|
-
if (np.array(args).shape == (2,) or np.array(args).shape == (1, 2))
|
|
317
|
+
if ((np.array(args).shape == (2,) or np.array(args).shape == (1, 2))
|
|
318
|
+
and not isinstance(args, np.ndarray)
|
|
319
|
+
and isinstance(values, np.ndarray)):
|
|
281
320
|
return values[0]
|
|
282
321
|
else:
|
|
283
322
|
return values
|
|
@@ -735,7 +774,7 @@ class HyperSphericalFunction(SphericalFunction):
|
|
|
735
774
|
the latitude angle from Z axis (theta==0 -> u = Z axis), and psi is the angle defining the orientation of
|
|
736
775
|
the second direction (v) in the plane orthogonal to u, as illustrated below:
|
|
737
776
|
|
|
738
|
-
.. image::
|
|
777
|
+
.. image:: ../../../docs/_static/images/HyperSphericalCoordinates.png
|
|
739
778
|
|
|
740
779
|
|
|
741
780
|
degrees : bool, default False
|