elasticipy 2.8.10__tar.gz → 3.0.0__tar.gz

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.
Files changed (90) hide show
  1. {elasticipy-2.8.10 → elasticipy-3.0.0}/Examples/Example_Stiffness_tensor.py +1 -1
  2. elasticipy-3.0.0/Examples/essai_plasticity.py +73 -0
  3. {elasticipy-2.8.10 → elasticipy-3.0.0}/JOSS/paper.bib +4 -2
  4. {elasticipy-2.8.10/src/elasticipy.egg-info → elasticipy-3.0.0}/PKG-INFO +2 -1
  5. {elasticipy-2.8.10 → elasticipy-3.0.0}/README.md +1 -0
  6. elasticipy-3.0.0/docs/source/Tutorials/Plasticity.rst +187 -0
  7. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/Tutorial_StiffnessTensor.rst +1 -1
  8. elasticipy-3.0.0/docs/source/Tutorials/Tutorial_StressStrain.rst +231 -0
  9. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/Tutorial_ThermalExpansion.rst +9 -5
  10. elasticipy-3.0.0/docs/source/Tutorials/images/Cyclic.png +0 -0
  11. elasticipy-3.0.0/docs/source/Tutorials/images/Incremental.png +0 -0
  12. elasticipy-3.0.0/docs/source/Tutorials/images/Shear.png +0 -0
  13. elasticipy-3.0.0/docs/source/Tutorials/images/Stress-controlled.png +0 -0
  14. elasticipy-3.0.0/docs/source/Tutorials/images/StressStrain-controlled.png +0 -0
  15. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials.rst +2 -1
  16. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/index.rst +4 -0
  17. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/Elasticipy/FourthOrderTensor.py +360 -44
  18. elasticipy-3.0.0/src/Elasticipy/Plasticity.py +332 -0
  19. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/Elasticipy/SecondOrderTensor.py +338 -66
  20. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/Elasticipy/StressStrainTensors.py +13 -8
  21. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/Elasticipy/ThermalExpansion.py +88 -17
  22. {elasticipy-2.8.10 → elasticipy-3.0.0/src/elasticipy.egg-info}/PKG-INFO +2 -1
  23. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/elasticipy.egg-info/SOURCES.txt +7 -0
  24. {elasticipy-2.8.10 → elasticipy-3.0.0}/tests/test_StiffnessTensor.py +147 -9
  25. {elasticipy-2.8.10 → elasticipy-3.0.0}/tests/test_StressStrainTensors.py +182 -16
  26. {elasticipy-2.8.10 → elasticipy-3.0.0}/tests/test_ThermalExpansion.py +15 -0
  27. elasticipy-3.0.0/tests/test_plasticity.py +148 -0
  28. elasticipy-2.8.10/docs/source/Tutorials/Tutorial_StressStrain.rst +0 -445
  29. elasticipy-2.8.10/src/Elasticipy/Plasticity.py +0 -125
  30. elasticipy-2.8.10/tests/test_plasticity.py +0 -57
  31. {elasticipy-2.8.10 → elasticipy-3.0.0}/.github/workflows/Codecov.yml +0 -0
  32. {elasticipy-2.8.10 → elasticipy-3.0.0}/.github/workflows/JOSS build.yml +0 -0
  33. {elasticipy-2.8.10 → elasticipy-3.0.0}/.github/workflows/cloc.yml +0 -0
  34. {elasticipy-2.8.10 → elasticipy-3.0.0}/.github/workflows/python-publish.yml +0 -0
  35. {elasticipy-2.8.10 → elasticipy-3.0.0}/.readthedocs.yaml +0 -0
  36. {elasticipy-2.8.10 → elasticipy-3.0.0}/CODE_OF_CONDUCT.md +0 -0
  37. {elasticipy-2.8.10 → elasticipy-3.0.0}/CONTRIBUTING.md +0 -0
  38. {elasticipy-2.8.10 → elasticipy-3.0.0}/Examples/Elasticipy_vs_pymatgen.py +0 -0
  39. {elasticipy-2.8.10 → elasticipy-3.0.0}/Examples/Elate_vs_Elasticipy.py +0 -0
  40. {elasticipy-2.8.10 → elasticipy-3.0.0}/Examples/Example_StressStrain_arrays.py +0 -0
  41. {elasticipy-2.8.10 → elasticipy-3.0.0}/Examples/Example_WaveVelocity.py +0 -0
  42. {elasticipy-2.8.10 → elasticipy-3.0.0}/Examples/MaterialsProject.json +0 -0
  43. {elasticipy-2.8.10 → elasticipy-3.0.0}/Examples/Multiple_phases.py +0 -0
  44. {elasticipy-2.8.10 → elasticipy-3.0.0}/Examples/example_readwrite.py +0 -0
  45. {elasticipy-2.8.10 → elasticipy-3.0.0}/JOSS/ElasticipyVSpymatgen.png +0 -0
  46. {elasticipy-2.8.10 → elasticipy-3.0.0}/JOSS/Nye.png +0 -0
  47. {elasticipy-2.8.10 → elasticipy-3.0.0}/JOSS/Plot_E.png +0 -0
  48. {elasticipy-2.8.10 → elasticipy-3.0.0}/JOSS/YoungModulus.png +0 -0
  49. {elasticipy-2.8.10 → elasticipy-3.0.0}/JOSS/paper.md +0 -0
  50. {elasticipy-2.8.10 → elasticipy-3.0.0}/LICENSE +0 -0
  51. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/_static/images/HyperSphericalCoordinates.png +0 -0
  52. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/index.rst +0 -0
  53. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Elasticipy.FourthOrderTensor.rst +0 -0
  54. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Elasticipy.Plasticity.rst +0 -0
  55. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Elasticipy.SecondOrderTensor.rst +0 -0
  56. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Elasticipy.SphericalFunction.rst +0 -0
  57. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Elasticipy.StressStrainTensors.rst +0 -0
  58. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Elasticipy.rst +0 -0
  59. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/GUI.rst +0 -0
  60. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/Tutorial_AveragingMethods.rst +0 -0
  61. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/Tutorial_MultiplePhases.rst +0 -0
  62. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/Tutorial_ReadWriteFiles.rst +0 -0
  63. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/Tutorial_wave-velocities.rst +0 -0
  64. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/images/E_PF.png +0 -0
  65. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/images/E_VRH_sections.png +0 -0
  66. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/images/E_hill_fiber.png +0 -0
  67. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/images/E_plot3D.png +0 -0
  68. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/images/E_xyz_sections.png +0 -0
  69. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/images/GUI.png +0 -0
  70. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/images/G_plot3D.png +0 -0
  71. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/images/G_plot3D_min.png +0 -0
  72. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/images/G_xyz_sections.png +0 -0
  73. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/images/WaveVelocities.png +0 -0
  74. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/Tutorials/images/plot_volumeFraction.png +0 -0
  75. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/conf.py +0 -0
  76. {elasticipy-2.8.10 → elasticipy-3.0.0}/docs/source/modules.rst +0 -0
  77. {elasticipy-2.8.10 → elasticipy-3.0.0}/pyproject.toml +0 -0
  78. {elasticipy-2.8.10 → elasticipy-3.0.0}/requirements.txt +0 -0
  79. {elasticipy-2.8.10 → elasticipy-3.0.0}/setup.cfg +0 -0
  80. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/Elasticipy/CrystalSymmetries.py +0 -0
  81. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/Elasticipy/PoleFigure.py +0 -0
  82. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/Elasticipy/SphericalFunction.py +0 -0
  83. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/Elasticipy/__init__.py +0 -0
  84. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/Elasticipy/gui.py +0 -0
  85. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/elasticipy.egg-info/dependency_links.txt +0 -0
  86. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/elasticipy.egg-info/requires.txt +0 -0
  87. {elasticipy-2.8.10 → elasticipy-3.0.0}/src/elasticipy.egg-info/top_level.txt +0 -0
  88. {elasticipy-2.8.10 → elasticipy-3.0.0}/tests/MaterialsProject.json +0 -0
  89. {elasticipy-2.8.10 → elasticipy-3.0.0}/tests/__init__.py +0 -0
  90. {elasticipy-2.8.10 → elasticipy-3.0.0}/tests/test_SphericalFunction.py +0 -0
@@ -22,7 +22,7 @@ E.plot_as_pole_figure() # or even with PF
22
22
  print(E.max())
23
23
 
24
24
  # Apply a random rotation on stiffness tensor
25
- rotation = Rotation.from_euler('zxz', [0, 45, 0], degrees=True)
25
+ rotation = Rotation.from_euler('ZXZ', [90, 45, 0], degrees=True)
26
26
  Crot = C*rotation
27
27
  # Check that the Young modulus has changed as well
28
28
  Crot.Young_modulus.plot3D()
@@ -0,0 +1,73 @@
1
+ from Elasticipy.Plasticity import JohnsonCook
2
+ from Elasticipy.StressStrainTensors import StressTensor, StrainTensor
3
+ from Elasticipy.FourthOrderTensor import StiffnessTensor
4
+ import numpy as np
5
+ from matplotlib import pyplot as plt
6
+ import matplotlib as mpl
7
+ mpl.use('Qt5Agg') # Ensure interactive plot
8
+
9
+
10
+ JC = JohnsonCook(A=363, B=792.7122, n=0.5756) # https://doi.org/10.1016/j.matpr.2020.05.213
11
+ C = StiffnessTensor.isotropic(E=210000, nu=0.27)
12
+
13
+ n_step = 100
14
+ sigma_max = 725
15
+ stress_mag = np.linspace(0, sigma_max, n_step)
16
+ stress = StressTensor.shear([1,0,0], [0,1,0],stress_mag)
17
+
18
+ elastic_strain = C.inv() * stress
19
+ plastic_strain = StrainTensor.zeros(n_step)
20
+ for i in range(2, n_step):
21
+ strain_increment = JC.compute_strain_increment(stress[i])
22
+ plastic_strain[i] = plastic_strain[i-1] + strain_increment
23
+
24
+
25
+ eps_xx = elastic_strain.C[0,1]+plastic_strain.C[0,1]
26
+ fig, ax = plt.subplots()
27
+ ax.plot(eps_xx, stress_mag, label='Stress-controlled')
28
+ ax.set_xlabel(r'$\varepsilon_{xy}$')
29
+ ax.set_ylabel('Shear stress (MPa)')
30
+
31
+ ##
32
+ from scipy.optimize import minimize_scalar
33
+ stress = StressTensor.zeros(n_step)
34
+ plastic_strain = StrainTensor.zeros(n_step)
35
+ JC.reset_strain()
36
+ for i in range(2, n_step):
37
+ def fun(tensile_stress):
38
+ trial_stress = StressTensor.shear([1,0,0], [0,1,0], tensile_stress)
39
+ trial_elastic_strain = C.inv() * trial_stress
40
+ trial_strain_increment = JC.compute_strain_increment(trial_stress, apply_strain=False)
41
+ trial_plastic_strain = plastic_strain[i - 1] + trial_strain_increment
42
+ trial_elongation = trial_plastic_strain.C[0,1] + trial_elastic_strain.C[0,1]
43
+ return (trial_elongation - eps_xx[i])**2
44
+ s = minimize_scalar(fun)
45
+ s0 = s.x
46
+ stress.C[0,1][i] = s0
47
+ strain_increment = JC.compute_strain_increment(stress[i])
48
+ plastic_strain[i] = plastic_strain[i-1] + strain_increment
49
+
50
+ ax.plot(eps_xx, stress.C[0,1], label='Strain-controlled', linestyle='dotted')
51
+ ax.legend()
52
+
53
+
54
+ #
55
+ JC.reset_strain()
56
+ JC_tresca = JohnsonCook(A=363, B=792.7122, n=0.5756, criterion='Tresca')
57
+ stress_mag = np.linspace(0, 500, n_step)
58
+ stress = StressTensor.shear([1,0,0], [0,1,0],stress_mag)
59
+ models = (JC, JC_tresca)
60
+ labels = ('von Mises', 'Tresca')
61
+
62
+ elastic_strain = C.inv() * stress
63
+ fig, ax = plt.subplots()
64
+ for j, model in enumerate(models):
65
+ plastic_strain = StrainTensor.zeros(n_step)
66
+ for i in range(2, n_step):
67
+ strain_increment = model.compute_strain_increment(stress[i])
68
+ plastic_strain[i] = plastic_strain[i-1] + strain_increment
69
+ eps_xy = elastic_strain.C[0,1]+plastic_strain.C[0,1]
70
+ ax.plot(eps_xy, stress_mag, label=labels[j])
71
+ ax.set_xlabel(r'$\varepsilon_{xy}$')
72
+ ax.set_ylabel('Shear stress (MPa)')
73
+ ax.legend()
@@ -6,6 +6,7 @@ pages = {314-319},
6
6
  year = {2013},
7
7
  issn = {0927-0256},
8
8
  url = {https://doi.org/10.1016/j.commatsci.2012.10.028},
9
+ doi = {10.1016/j.commatsci.2012.10.028},
9
10
  author = {Shyue Ping Ong and William Davidson Richards and Anubhav Jain and Geoffroy Hautier and Michael Kocher and Shreyas Cholia and Dan Gunter and Vincent L. Chevrier and Kristin A. Persson and Gerbrand Ceder},
10
11
  keywords = {Materials, Project, Design, Thermodynamics, High-throughput},
11
12
  }
@@ -33,7 +34,8 @@ journal = {Journal of Physics: Condensed Matter},
33
34
  pages={175--192},
34
35
  year={2011},
35
36
  publisher={The Geological Society of London London},
36
- url={http://dx.doi.org/10.1144/SP360.10}
37
+ url={http://dx.doi.org/10.1144/SP360.10},
38
+ doi={10.1144/SP360.10}
37
39
  }
38
40
 
39
41
  @book{nye,
@@ -124,4 +126,4 @@ DOI = {10.5194/se-11-259-2020}
124
126
  doi = {10.1038/s41586-020-2649-2},
125
127
  publisher = {Springer Science and Business Media {LLC}},
126
128
  url = {https://doi.org/10.1038/s41586-020-2649-2}
127
- }
129
+ }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: elasticipy
3
- Version: 2.8.10
3
+ Version: 3.0.0
4
4
  Summary: A Python library for elasticity tensor computations
5
5
  Author-email: Dorian Depriester <dorian.dep@gmail.com>
6
6
  License: MIT
@@ -36,6 +36,7 @@ Requires-Dist: mp_api; extra == "dev"
36
36
  [![DOI](https://zenodo.org/badge/876162900.svg)](https://doi.org/10.5281/zenodo.14501849)
37
37
  [![codecov](https://codecov.io/gh/DorianDepriester/Elasticipy/graph/badge.svg?token=VUZPEUPBH1)](https://codecov.io/gh/DorianDepriester/Elasticipy)
38
38
  ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/Elasticipy)
39
+ [![status](https://joss.theoj.org/papers/8cce91b782f17f52e9ee30916cd86ad5/status.svg)](https://joss.theoj.org/papers/8cce91b782f17f52e9ee30916cd86ad5)
39
40
 
40
41
 
41
42
 
@@ -6,6 +6,7 @@
6
6
  [![DOI](https://zenodo.org/badge/876162900.svg)](https://doi.org/10.5281/zenodo.14501849)
7
7
  [![codecov](https://codecov.io/gh/DorianDepriester/Elasticipy/graph/badge.svg?token=VUZPEUPBH1)](https://codecov.io/gh/DorianDepriester/Elasticipy)
8
8
  ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/Elasticipy)
9
+ [![status](https://joss.theoj.org/papers/8cce91b782f17f52e9ee30916cd86ad5/status.svg)](https://joss.theoj.org/papers/8cce91b782f17f52e9ee30916cd86ad5)
9
10
 
10
11
 
11
12
 
@@ -0,0 +1,187 @@
1
+ Plasticity
2
+ ----------
3
+ Although Elasticipy was not initially meant to work on plasticity (hence the name...), it provides some clues about
4
+ simulations of elasto-plastic behaviour of materials. This tutorial shows how one can simulate a tensile test in the
5
+ elasto-plastic domain of a material.
6
+
7
+ The Johnson-Cook model
8
+ ======================
9
+ The Johnson-Cook (JC) model is widely used in the literature for modeling isotropic hardening of metalic materials.
10
+ Therefore, it is available out-of-the-box in Elasticipy. It assumes that the flow stress (:math:`\sigma`) depends on the
11
+ equivalent strain (:math:`\varepsilon`), the strain rate (:math:`\dot{\varepsilon}`) and the temperature (:math:`T`),
12
+ according to the following equation:
13
+
14
+ .. math::
15
+
16
+ \sigma = \left(A + B\varepsilon^n\right)
17
+ \left[1 + C\log\left(\frac{\varepsilon}{\dot{\varepsilon}_0}\right)\right]
18
+ \left(1-\theta^m\right)
19
+
20
+ with
21
+
22
+ .. math::
23
+
24
+ \theta = \begin{cases}
25
+ \frac{T-T_0}{T_m-T_0} & \text{if } T<T_m\\\\
26
+ 1 & \text{otherwise}
27
+ \end{cases}
28
+
29
+
30
+ :math:`A`, :math:`B`, :math:`C`, :math:`\dot{\varepsilon}_0`, :math:`T_0`, :math:`T_m` and :math:`m` are parameters
31
+ whose values depend on the material.
32
+
33
+ Simulation of a stress-controlled tensile test
34
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
35
+ At first, we will try to simulate a stress-controlled tensile test. Although this approach is quite uncommon, we will do
36
+ this anyway, just because it is easier to program.
37
+
38
+ First, let us create the model:
39
+
40
+ .. doctest::
41
+
42
+ >>> from Elasticipy.Plasticity import JohnsonCook
43
+ >>> JC = JohnsonCook(A=363, B=792.7122, n=0.5756)
44
+
45
+ The parameters are taken from [1]_. As we will also take elastic behaviour into account, we also need:
46
+
47
+ >>> from Elasticipy.FourthOrderTensor import StiffnessTensor
48
+ >>> C = StiffnessTensor.isotropic(E=210000, nu=0.27)
49
+
50
+ Now, let say that we want to investigate the material's response for the tensile stress ranging from 0 to 725 MPa:
51
+
52
+ >>> from Elasticipy.StressStrainTensors import StressTensor, StrainTensor
53
+ >>> import numpy as np
54
+ >>> n_step = 100
55
+ >>> stress_mag = np.linspace(0, 725, n_step)
56
+ >>> stress = StressTensor.tensile([1,0,0], stress_mag)
57
+
58
+ At least, we can directly compute the elastic strain for each step:
59
+
60
+ >>> elastic_strain = C.inv() * stress
61
+
62
+ So now, the plastic strain can be computed using an iterative approach:
63
+
64
+ >>> plastic_strain = StrainTensor.zeros(n_step)
65
+ >>> for i in range(1, n_step):
66
+ ... strain_increment = JC.compute_strain_increment(stress[i])
67
+ ... plastic_strain[i] = plastic_strain[i-1] + strain_increment
68
+
69
+ That's all. Finally, let us plot the applied stress as a function of the overall elongation:
70
+
71
+ >>> from matplotlib import pyplot as plt
72
+ >>> elong = elastic_strain.C[0,0]+plastic_strain.C[0,0]
73
+ >>> fig, ax = plt.subplots()
74
+ >>> ax.plot(elong, stress_mag, label='Stress-controlled') # doctest: +SKIP
75
+ >>> ax.set_xlabel(r'$\varepsilon_{xx}$') # doctest: +SKIP
76
+ >>> ax.set_ylabel('Tensile stress (MPa)') # doctest: +SKIP
77
+
78
+ .. image:: images/Stress-controlled.png
79
+
80
+
81
+ Simulation of a strain-controlled tensile test
82
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
83
+ The difficulty of simulating a strain-controlled tensile test is that, at a given step, one must identify both the
84
+ elastic and the plastic strain (if any) at once, while ensuring that the stress keeps uniaxial. Therefore, the hack is
85
+ to add a subroutine (optimization loop) to find the tensile stress so that the associated strain complies with the applied strain:
86
+
87
+ >>> from scipy.optimize import minimize_scalar
88
+ >>> JC.reset_strain() # Ensure that the previous hardening does not count
89
+ >>> stress = StressTensor.zeros(n_step)
90
+ >>> plastic_strain = StrainTensor.zeros(n_step)
91
+ >>> JC.reset_strain()
92
+ >>> for i in range(1, n_step):
93
+ ... def fun(tensile_stress):
94
+ ... trial_stress = StressTensor.tensile([1,0,0], tensile_stress)
95
+ ... trial_elastic_strain = C.inv() * trial_stress
96
+ ... trial_strain_increment = JC.compute_strain_increment(trial_stress, apply_strain=False)
97
+ ... trial_plastic_strain = plastic_strain[i - 1] + trial_strain_increment
98
+ ... trial_elongation = trial_plastic_strain.C[0,0] + trial_elastic_strain.C[0,0]
99
+ ... return (trial_elongation - elong[i])**2
100
+ ... s = minimize_scalar(fun)
101
+ ... stress.C[0,0][i] = s.x
102
+ ... strain_increment = JC.compute_strain_increment(stress[i])
103
+ ... plastic_strain[i] = plastic_strain[i-1] + strain_increment
104
+
105
+ Finally, let's plot the corresponding tensile curve ontop of that of the stress-controlled tensile test:
106
+
107
+ >>> ax.plot(elong, stress.C[0,0], label='Strain-controlled', linestyle='dotted') # doctest: +SKIP
108
+ >>> ax.legend() # doctest: +SKIP
109
+
110
+ .. image:: images/StressStrain-controlled.png
111
+
112
+
113
+ Incremental loading
114
+ ===================
115
+ Here, we have only considered monotonic loading, but we can also consider different loading path, such as incremental:
116
+
117
+ >>> load_path = [np.linspace(0,0.1),
118
+ ... np.linspace(0.1,0.099),
119
+ ... np.linspace(0.099,0.2),
120
+ ... np.linspace(0.2,0.199),
121
+ ... np.linspace(0.199,0.3)]
122
+ >>> elong = np.concatenate(load_path)
123
+ >>> n_step = len(elong)
124
+
125
+ .. image:: images/Incremental.png
126
+
127
+ or cyclic:
128
+
129
+
130
+ >>> load_path = [np.linspace(0,0.1),
131
+ ... np.linspace(0.1,-0.2),
132
+ ... np.linspace(-0.2,0.3),
133
+ ... np.linspace(0.3,-0.4)]
134
+ >>> elong = np.concatenate(load_path)
135
+ >>> n_step = len(elong)
136
+
137
+ .. image:: images/Cyclic.png
138
+
139
+ .. note::
140
+
141
+ The figure above clearly evidences the isotropic hardening inherent to the JC model.
142
+
143
+
144
+ Complex loading path
145
+ ====================
146
+ In the example above, we have only studied longitudinal stress/strain. Still, it is worth mentioning that other stress
147
+ states can be investigated (e.g. shear, multiaxial etc.) thanks to the
148
+ `normality rule <https://www.doitpoms.ac.uk/tlplib/granular_materials/normal.php>`_.
149
+
150
+ Tresca's plasticity criterion
151
+ =============================
152
+ Above, we have used the von Mises plasticity criterion (a.k.a J2 criterion). This can be switched to Tresca by passing
153
+ the plasticity criterion to the model constructor:
154
+
155
+ >>> JC = JohnsonCook(A=363, B=792.7122, n=0.5756, criterion='Tresca')
156
+
157
+ For instance, one can highlight the difference between the J2 and Tresca plasticity in shear:
158
+
159
+ >>> JC.reset_strain()
160
+ >>> JC_tresca = JohnsonCook(A=363, B=792.7122, n=0.5756, criterion='Tresca')
161
+ >>> stress_mag = np.linspace(0, 500, n_step)
162
+ >>> stress = StressTensor.shear([1,0,0], [0,1,0],stress_mag)
163
+ >>> models = (JC, JC_tresca)
164
+ >>> labels = ('von Mises', 'Tresca')
165
+ >>>
166
+ >>> elastic_strain = C.inv() * stress
167
+ >>> fig, ax = plt.subplots()
168
+ >>> for j, model in enumerate(models):
169
+ ... plastic_strain = StrainTensor.zeros(n_step)
170
+ ... for i in range(1, n_step):
171
+ ... strain_increment = model.compute_strain_increment(stress[i])
172
+ ... plastic_strain[i] = plastic_strain[i-1] + strain_increment
173
+ ... eps_xy = elastic_strain.C[0,1]+plastic_strain.C[0,1]
174
+ ... ax.plot(eps_xy, stress_mag, label=labels[j])
175
+ >>> ax.set_xlabel(r'$\varepsilon_{xy}$') # doctest: +SKIP
176
+ >>> ax.set_ylabel('Shear stress (MPa)') # doctest: +SKIP
177
+ >>> ax.legend() # doctest: +SKIP
178
+
179
+
180
+ .. image:: images/Shear.png
181
+
182
+
183
+
184
+ .. [1] Sandeep Yadav, Sorabh Singhal, Yogeshwar Jasra, Ravindra K. Saxena,
185
+ Determination of Johnson-Cook material model for weldment of mild steel,
186
+ Materials Today: Proceedings, Volume 28, Part 3, 2020, Pages 1801-1808, ISSN 2214-7853,
187
+ https://doi.org/10.1016/j.matpr.2020.05.213.
@@ -55,7 +55,7 @@ deviation of the Young modulus:
55
55
  Another way to evidence anisotropy is to use the universal anisotropy factor [1]_:
56
56
 
57
57
  >>> C.universal_anisotropy
58
- 5.14100955164141
58
+ 5.1410095516414085
59
59
 
60
60
  Shear moduli and Poisson ratios
61
61
  -------------------------------
@@ -0,0 +1,231 @@
1
+ Stress and strain tensors
2
+ =========================
3
+
4
+ This tutorial illustrates how we work on strain and stress tensors, and how Elasticipy handles arrays of tensors.
5
+
6
+ Single tensors
7
+ --------------
8
+ Let's start with basic operations with the stress tensor. For instance, we can compute the von Mises and Tresca
9
+ equivalent stresses:
10
+
11
+
12
+ .. doctest::
13
+
14
+ >>> from Elasticipy.StressStrainTensors import StressTensor, StrainTensor
15
+ >>> stress = StressTensor.shear([1, 0, 0], [0, 1, 0], 1.0) # Unit XY shear stress
16
+ >>> print(stress.vonMises(), stress.Tresca())
17
+ 1.7320508075688772 2.0
18
+
19
+ So now, let's have a look on the strain tensor, and compute the principal strains and the volumetric change:
20
+
21
+ >>> strain = StrainTensor.shear([1,0,0], [0,1,0], 1e-3) # XY Shear strain with 1e-3 mag.
22
+ >>> print(strain.principal_strains())
23
+ [ 0.001 0. -0.001]
24
+ >>> print(strain.volumetric_strain())
25
+ 0.0
26
+ >>> stress = StressTensor.shear([1, 0, 0], [0, 1, 0], 1.0) # Unit XY shear stress
27
+ >>> print(stress.vonMises(), stress.Tresca())
28
+ 1.7320508075688772 2.0
29
+
30
+ Linear elasticity
31
+ --------------------------------
32
+ This section is dedicated to linear elasticity, hence introducing the fourth-order stiffness tensor.
33
+ As an example, create a stiffness tensor corresponding to steel:
34
+
35
+ >>> from Elasticipy.FourthOrderTensor import StiffnessTensor
36
+ >>> C = StiffnessTensor.isotropic(E=210e3, nu=0.28)
37
+ >>> print(C)
38
+ Stiffness tensor (in Voigt notation):
39
+ [[268465.90909091 104403.40909091 104403.40909091 0.
40
+ 0. 0. ]
41
+ [104403.40909091 268465.90909091 104403.40909091 0.
42
+ 0. 0. ]
43
+ [104403.40909091 104403.40909091 268465.90909091 0.
44
+ 0. 0. ]
45
+ [ 0. 0. 0. 82031.25
46
+ 0. 0. ]
47
+ [ 0. 0. 0. 0.
48
+ 82031.25 0. ]
49
+ [ 0. 0. 0. 0.
50
+ 0. 82031.25 ]]
51
+ Symmetry: isotropic
52
+
53
+
54
+ Considering the previous strain, evaluate the corresponding stress:
55
+
56
+ >>> sigma = C * strain
57
+ >>> print(sigma)
58
+ Stress tensor
59
+ [[ 0. 164.0625 0. ]
60
+ [164.0625 0. 0. ]
61
+ [ 0. 0. 0. ]]
62
+
63
+ Conversely, one can compute the compliance tensor:
64
+
65
+ >>> S = C.inv()
66
+ >>> print(S)
67
+ Compliance tensor (in Voigt notation):
68
+ [[ 4.76190476e-06 -1.33333333e-06 -1.33333333e-06 0.00000000e+00
69
+ 0.00000000e+00 0.00000000e+00]
70
+ [-1.33333333e-06 4.76190476e-06 -1.33333333e-06 0.00000000e+00
71
+ 0.00000000e+00 0.00000000e+00]
72
+ [-1.33333333e-06 -1.33333333e-06 4.76190476e-06 0.00000000e+00
73
+ 0.00000000e+00 0.00000000e+00]
74
+ [ 0.00000000e+00 0.00000000e+00 0.00000000e+00 1.21904762e-05
75
+ 0.00000000e+00 0.00000000e+00]
76
+ [ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
77
+ 1.21904762e-05 0.00000000e+00]
78
+ [ 0.00000000e+00 0.00000000e+00 0.00000000e+00 0.00000000e+00
79
+ 0.00000000e+00 1.21904762e-05]]
80
+ Symmetry: isotropic
81
+
82
+ and check that we retrieve the correct (initial) strain:
83
+
84
+ >>> print(S * sigma)
85
+ Strain tensor
86
+ [[0. 0.001 0. ]
87
+ [0.001 0. 0. ]
88
+ [0. 0. 0. ]]
89
+
90
+ .. _multidimensional-arrays:
91
+
92
+ Multidimensional tensor arrays
93
+ ------------------------------
94
+ Elasticipy allows to process thousands of tensors at one, with the aid of tensor arrays.
95
+ As an illustration, we consider the anisotropic behaviour of ferrite:
96
+
97
+ >>> C = StiffnessTensor.fromCrystalSymmetry(symmetry='cubic', phase_name='ferrite',
98
+ ... C11=274, C12=175, C44=89)
99
+ >>> print(C)
100
+ Stiffness tensor (in Voigt notation) for ferrite:
101
+ [[274. 175. 175. 0. 0. 0.]
102
+ [175. 274. 175. 0. 0. 0.]
103
+ [175. 175. 274. 0. 0. 0.]
104
+ [ 0. 0. 0. 89. 0. 0.]
105
+ [ 0. 0. 0. 0. 89. 0.]
106
+ [ 0. 0. 0. 0. 0. 89.]]
107
+ Symmetry: cubic
108
+
109
+ Let's start by creating an array of 10 stresses:
110
+
111
+ >>> import numpy as np
112
+ >>> n_array = 10
113
+ >>> shear_stress = np.linspace(0, 100, n_array)
114
+ >>> sigma = StressTensor.shear([1,0,0],[0,1,0], shear_stress) # Array of stresses corresponding to X-Y shear
115
+ >>> print(sigma[0]) # Check the initial value of the stress...
116
+ Stress tensor
117
+ [[0. 0. 0.]
118
+ [0. 0. 0.]
119
+ [0. 0. 0.]]
120
+ >>> print(sigma[-1]) # ...and the final value.
121
+ Stress tensor
122
+ [[ 0. 100. 0.]
123
+ [100. 0. 0.]
124
+ [ 0. 0. 0.]]
125
+
126
+ The corresponding strain array is evaluated with the same syntax as before:
127
+
128
+ >>> eps = C.inv() * sigma
129
+ >>> print(eps[0]) # Now check the initial value of strain...
130
+ Strain tensor
131
+ [[0. 0. 0.]
132
+ [0. 0. 0.]
133
+ [0. 0. 0.]]
134
+ >>> print(eps[-1]) # ...and the final value.
135
+ Strain tensor
136
+ [[0. 0.56179775 0. ]
137
+ [0.56179775 0. 0. ]
138
+ [0. 0. 0. ]]
139
+
140
+ We can for instance compute the corresponding elastic energies:
141
+
142
+ >>> print(eps.elastic_energy(sigma))
143
+ [ 0. 0.69357747 2.77430989 6.24219725 11.09723956 17.33943682
144
+ 24.96878901 33.98529616 44.38895825 56.17977528]
145
+
146
+ Another application of working with an array of stress tensors is to check whether a tensor field complies with the
147
+ balance of linear momentum (see `here <https://en.wikiversity.org/wiki/Continuum_mechanics/Balance_of_linear_momentum>`_
148
+ for details) or not. For instance, if we want to compute the divergence of ``sigma``:
149
+
150
+ >>> sigma.div()
151
+ array([[ 0. , 11.11111111, 0. ],
152
+ [ 0. , 11.11111111, 0. ],
153
+ [ 0. , 11.11111111, 0. ],
154
+ [ 0. , 11.11111111, 0. ],
155
+ [ 0. , 11.11111111, 0. ],
156
+ [ 0. , 11.11111111, 0. ],
157
+ [ 0. , 11.11111111, 0. ],
158
+ [ 0. , 11.11111111, 0. ],
159
+ [ 0. , 11.11111111, 0. ],
160
+ [ 0. , 11.11111111, 0. ]])
161
+
162
+ Here, the *i*-th row provides the divergence vector for the *i*-th stress tensor.
163
+ See `the full documentation <../Elasticipy.SecondOrderTensor.html#Elasticipy.SecondOrderTensor.SecondOrderTensor.div>`_ for
164
+ details about this function.
165
+
166
+ .. _strain_rotations:
167
+
168
+ Apply rotations
169
+ ---------------
170
+ Rotations can be applied on the tensors. If multiple rotations are applied at once, this results in tensor arrays.
171
+ Rotations are defined by ``scipy.transform.Rotation``
172
+ (see `here <https://docs.scipy.org/doc/scipy/reference/generated/scipy.spatial.transform.Rotation.html>`__ for details).
173
+
174
+ >>> from scipy.spatial.transform import Rotation
175
+
176
+ For example, let's consider a random set of 1000 rotations:
177
+
178
+ >>> n_ori = 1000
179
+ >>> random_state = 1234 # This is just to ensure reproducibility
180
+ >>> rotations = Rotation.random(n_ori, random_state=random_state)
181
+
182
+ These rotations can be applied on the strain tensor
183
+
184
+ >>> eps_rotated = eps.rotate(rotations, mode='cross')
185
+
186
+
187
+ Option ``mode='cross'`` allows to compute all combinations of strains and rotation, resulting in a kind of 2D matrix of
188
+ strain tensors:
189
+
190
+ >>> print(eps_rotated.shape)
191
+ (10, 1000)
192
+
193
+ Therefore, we can compute the corresponding rotated stress array:
194
+
195
+ >>> sigma_rotated = C * eps_rotated
196
+ >>> print(sigma_rotated.shape) # Check out the shape of the stresses
197
+ (10, 1000)
198
+
199
+ And get the stress back to the initial coordinate system:
200
+
201
+ >>> sigma = sigma_rotated * rotations.inv() # Go back to initial frame
202
+
203
+ As opposed to the ``rotate(..., mode='cross')`` (see above), we use ``*`` here to keep the same
204
+ dimensionality (perform element-wise multiplication). It is equivalent to:
205
+
206
+ >>> sigma = sigma_rotated.rotate(rotations.inv())
207
+
208
+ Finally, we can estimate the mean stresses among all the orientations:
209
+
210
+ >>> sigma_mean = sigma.mean(axis=1) # Compute the mean over all orientations
211
+ >>> print(sigma_mean[-1]) # random
212
+ Stress tensor
213
+ [[ 5.35134832e-01 8.22419895e+01 2.02619662e-01]
214
+ [ 8.22419895e+01 -4.88440590e-01 -1.52733598e-01]
215
+ [ 2.02619662e-01 -1.52733598e-01 -4.66942413e-02]]
216
+
217
+ Actually, a more straightforward method is to define a set of rotated stiffness tensors, and compute their Reuss average:
218
+
219
+ >>> C_rotated = C * rotations
220
+ >>> C_Voigt = C_rotated.Voigt_average()
221
+
222
+ Which yields the same results in terms of stress:
223
+
224
+ >>> sigma_Voigt = C_Voigt * eps
225
+ >>> print(sigma_Voigt[-1])
226
+ Stress tensor
227
+ [[ 5.35134832e-01 8.22419895e+01 2.02619662e-01]
228
+ [ 8.22419895e+01 -4.88440590e-01 -1.52733598e-01]
229
+ [ 2.02619662e-01 -1.52733598e-01 -4.66942413e-02]]
230
+
231
+ See :ref:`here<Averaging methods>` for further details about the averaging methods.
@@ -77,20 +77,24 @@ If we want to consider multiple orientations at once, we can create an array of
77
77
  Shape=(10000,)
78
78
 
79
79
  If we want to compute the strain for each combination of orientations and temperatures in ``[0,1,2]`` (as done above),
80
- we can use the ``matmul`` operator:
80
+ we can use the ``apply_temperature()`` operator with ``mode='cross'``:
81
81
 
82
- >>> eps = alpha_rotated.matmul([0,1,2])
82
+ >>> eps = alpha_rotated.apply_temperature([0,1,2], mode='cross')
83
83
  >>> print(eps)
84
84
  Strain tensor
85
85
  Shape=(10000, 3)
86
86
 
87
+ .. note::
88
+
89
+ Above, we have used ``*``, which is just a shortcut for ``apply_temperature(...,mode='pair')``.
90
+
87
91
  For instance we can check out the maximum value for initial (0°) and final (2°) temperatures:
88
92
 
89
93
  >>> eps[:,0].max() # 0 because it corresponds to 0°
90
94
  Strain tensor
91
- [[ 0. -0. -0.]
92
- [-0. 0. -0.]
93
- [-0. -0. 0.]]
95
+ [[0. 0. 0.]
96
+ [0. 0. 0.]
97
+ [0. 0. 0.]]
94
98
  >>> eps[:,-1].max()
95
99
  Strain tensor
96
100
  [[1.12000000e-05 5.99947076e-06 5.99905095e-06]
@@ -11,4 +11,5 @@ Tutorials
11
11
  ./Tutorials/Tutorial_ThermalExpansion.rst
12
12
  ./Tutorials/Tutorial_wave-velocities.rst
13
13
  ./Tutorials/Tutorial_MultiplePhases.rst
14
- ./Tutorials/Tutorial_ReadWriteFiles.rst
14
+ ./Tutorials/Tutorial_ReadWriteFiles.rst
15
+ ./Tutorials/Plasticity.rst
@@ -32,6 +32,10 @@ Welcome to Elasticipy's Documentation!
32
32
  .. image:: https://img.shields.io/pypi/pyversions/Elasticipy
33
33
  :alt: PyPI - Python Version
34
34
 
35
+ .. image:: https://joss.theoj.org/papers/8cce91b782f17f52e9ee30916cd86ad5/status.svg
36
+ :alt: JOSS status
37
+ :target: https://joss.theoj.org/papers/8cce91b782f17f52e9ee30916cd86ad5
38
+
35
39
 
36
40
 
37
41
  Purpose of this package