-
Notifications
You must be signed in to change notification settings - Fork 84
/
tensor_field.py
108 lines (85 loc) · 3.17 KB
/
tensor_field.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# -*- coding: utf-8 -*-
# Copyright 2016-2024 The pyXem developers
#
# This file is part of pyXem.
#
# pyXem is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# pyXem is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with pyXem. If not, see <http://www.gnu.org/licenses/>.
import numpy as np
from scipy.linalg import polar
import math
from hyperspy.signals import Signal2D
from hyperspy.utils import stack
from pyxem.signals import StrainMap
def _polar_decomposition(image, side):
"""Perform a polar decomposition of a second rank tensor.
Parameters
----------
image : numpy.ndarray
Matrix on which to form polar decomposition.
side : str
'left' or 'right' the side on which to perform polar decomposition.
Returns
-------
R, U : numpy.ndarray
Stretch and rotation matrices obtained by polar decomposition.
"""
return np.array(polar(image, side=side))
def _get_rotation_angle(matrix):
"""Find the rotation angle associated with a given rotation matrix.
Parameters
----------
matrix : numpy.ndarray
A rotation matrix.
Returns
-------
angle : numpy.ndarray
Rotation angle associated with matrix.
"""
return np.array(-math.asin(matrix[1, 0]))
class DisplacementGradientMap(Signal2D):
"""Signal class for Tensor Fields."""
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
# Check that the signal dimensions are (3,3) for it to be a valid
# TensorField
def polar_decomposition(self):
"""Perform polar decomposition on the second rank tensors describing
the TensorField. The polar decomposition is right handed and given by
:math:`D = RU`
Returns
-------
R : TensorField
The orthogonal matrix describing the rotation field.
U : TensorField
The strain tensor field.
"""
RU = self.map(_polar_decomposition, side="right", inplace=False)
return RU.isig[:, :, 0], RU.isig[:, :, 1]
def get_strain_maps(self):
"""Obtain strain maps from the displacement gradient tensor at each
navigation position in the small strain approximation.
Returns
-------
strain_results : BaseSignal
Signal of shape < 4 | , > , navigation order is e11,e22,e12,theta
"""
R, U = self.polar_decomposition()
e11 = np.reciprocal(U.isig[0, 0].T) - 1
e12 = U.isig[0, 1].T
# e21 = U.isig[1, 0].T # Same as e12
e22 = np.reciprocal(U.isig[1, 1].T) - 1
theta = R.map(_get_rotation_angle, inplace=False)
theta = theta.transpose(2)
strain_results = stack([e11, e22, e12, theta])
return StrainMap(strain_results)