apsg 1.3.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.
- AUTHORS.md +9 -0
- CHANGELOG.md +304 -0
- CONTRIBUTING.md +91 -0
- apsg/__init__.py +104 -0
- apsg/config.py +214 -0
- apsg/database/__init__.py +23 -0
- apsg/database/_alchemy.py +609 -0
- apsg/database/_sdbread.py +284 -0
- apsg/decorator/__init__.py +5 -0
- apsg/decorator/_decorator.py +43 -0
- apsg/feature/__init__.py +79 -0
- apsg/feature/_container.py +1808 -0
- apsg/feature/_geodata.py +702 -0
- apsg/feature/_paleomag.py +425 -0
- apsg/feature/_statistics.py +430 -0
- apsg/feature/_tensor2.py +550 -0
- apsg/feature/_tensor3.py +1108 -0
- apsg/helpers/__init__.py +28 -0
- apsg/helpers/_helper.py +7 -0
- apsg/helpers/_math.py +46 -0
- apsg/helpers/_notation.py +119 -0
- apsg/math/__init__.py +6 -0
- apsg/math/_matrix.py +406 -0
- apsg/math/_vector.py +590 -0
- apsg/pandas/__init__.py +27 -0
- apsg/pandas/_pandas_api.py +507 -0
- apsg/plotting/__init__.py +25 -0
- apsg/plotting/_fabricplot.py +563 -0
- apsg/plotting/_paleomagplots.py +71 -0
- apsg/plotting/_plot_artists.py +551 -0
- apsg/plotting/_projection.py +326 -0
- apsg/plotting/_roseplot.py +360 -0
- apsg/plotting/_stereogrid.py +332 -0
- apsg/plotting/_stereonet.py +992 -0
- apsg/shell.py +35 -0
- apsg-1.3.0.dist-info/AUTHORS.md +9 -0
- apsg-1.3.0.dist-info/METADATA +141 -0
- apsg-1.3.0.dist-info/RECORD +40 -0
- apsg-1.3.0.dist-info/WHEEL +4 -0
- apsg-1.3.0.dist-info/entry_points.txt +3 -0
apsg/feature/_tensor2.py
ADDED
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import math
|
|
2
|
+
import numpy as np
|
|
3
|
+
from scipy import linalg as spla
|
|
4
|
+
|
|
5
|
+
from apsg.helpers._math import sind, cosd, atan2d
|
|
6
|
+
from apsg.math._vector import Vector2
|
|
7
|
+
from apsg.math._matrix import Matrix2
|
|
8
|
+
from apsg.decorator._decorator import ensure_arguments
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class DeformationGradient2(Matrix2):
|
|
12
|
+
"""
|
|
13
|
+
The class to represent 2D deformation gradient tensor.
|
|
14
|
+
|
|
15
|
+
Args:
|
|
16
|
+
a (2x2 array_like): Input data, that can be converted to
|
|
17
|
+
2x2 2D array. This includes lists, tuples and ndarrays.
|
|
18
|
+
|
|
19
|
+
Returns:
|
|
20
|
+
``DeformationGradient2`` object
|
|
21
|
+
|
|
22
|
+
Example:
|
|
23
|
+
>>> F = defgrad2(np.diag([2, 0.5]))
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
@classmethod
|
|
27
|
+
def from_comp(cls, xx=1, xy=0, yx=0, yy=1):
|
|
28
|
+
"""Return ``DeformationGradient2`` defined by individual components.
|
|
29
|
+
Default is zero tensor.
|
|
30
|
+
|
|
31
|
+
Keyword Args:
|
|
32
|
+
xx (float): tensor component F_xx
|
|
33
|
+
xy (float): tensor component F_xy
|
|
34
|
+
yx (float): tensor component F_yx
|
|
35
|
+
yy (float): tensor component F_yy
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
>>> F = matrix2.from_comp(xy=2)
|
|
39
|
+
>>> F
|
|
40
|
+
[[0. 2.]
|
|
41
|
+
[0. 0.]]
|
|
42
|
+
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
return cls([[xx, xy], [yx, yy]])
|
|
46
|
+
|
|
47
|
+
@classmethod
|
|
48
|
+
def from_ratio(cls, R=1):
|
|
49
|
+
"""Return isochoric ``DeformationGradient2`` tensor with axial stretches defined by strain ratio.
|
|
50
|
+
Default is identity tensor.
|
|
51
|
+
|
|
52
|
+
Keyword Args:
|
|
53
|
+
R (float): strain ratio
|
|
54
|
+
|
|
55
|
+
Example:
|
|
56
|
+
>>> F = defgrad2.from_ratio(R=4)
|
|
57
|
+
>> F
|
|
58
|
+
DeformationGradient2
|
|
59
|
+
[[2. 0. ]
|
|
60
|
+
[0. 0.5]]
|
|
61
|
+
|
|
62
|
+
"""
|
|
63
|
+
|
|
64
|
+
return cls.from_comp(xx=R ** (1 / 2), yy=R ** (-1 / 2))
|
|
65
|
+
|
|
66
|
+
@classmethod
|
|
67
|
+
def from_angle(cls, theta):
|
|
68
|
+
"""Return ``DeformationGradient2`` representing rotation by angle theta.
|
|
69
|
+
|
|
70
|
+
Args:
|
|
71
|
+
theta: Angle of rotation in degrees
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
>>> F = defgrad2.from_angle(45)
|
|
75
|
+
>>> F
|
|
76
|
+
DeformationGradient2
|
|
77
|
+
[[ 0.707 -0.707]
|
|
78
|
+
[ 0.707 0.707]]
|
|
79
|
+
|
|
80
|
+
"""
|
|
81
|
+
|
|
82
|
+
c, s = cosd(theta), sind(theta)
|
|
83
|
+
return cls([[c, -s], [s, c]])
|
|
84
|
+
|
|
85
|
+
@classmethod
|
|
86
|
+
@ensure_arguments(Vector2, Vector2)
|
|
87
|
+
def from_two_vectors(cls, v1, v2):
|
|
88
|
+
"""Return ``DeformationGradient2`` representing rotation around axis perpendicular
|
|
89
|
+
to both vectors and rotate v1 to v2.
|
|
90
|
+
|
|
91
|
+
Args:
|
|
92
|
+
v1: ``Vector2`` like object
|
|
93
|
+
v2: ``Vector2`` like object
|
|
94
|
+
|
|
95
|
+
Example:
|
|
96
|
+
>>> F = defgrad2.from_two_vectors(vec2(1, 1), vec2(0, 1))
|
|
97
|
+
>>> F
|
|
98
|
+
DeformationGradient2
|
|
99
|
+
[[ 0.707 -0.707]
|
|
100
|
+
[ 0.707 0.707]]
|
|
101
|
+
|
|
102
|
+
"""
|
|
103
|
+
return cls.from_angle(v1.angle(v2))
|
|
104
|
+
|
|
105
|
+
@property
|
|
106
|
+
def R(self):
|
|
107
|
+
"""Return rotation part of ``DeformationGradient2`` from polar decomposition."""
|
|
108
|
+
R, _ = spla.polar(self)
|
|
109
|
+
return type(self)(R)
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def U(self):
|
|
113
|
+
"""Return stretching part of ``DeformationGradient2`` from right polar decomposition."""
|
|
114
|
+
_, U = spla.polar(self, "right")
|
|
115
|
+
return type(self)(U)
|
|
116
|
+
|
|
117
|
+
@property
|
|
118
|
+
def V(self):
|
|
119
|
+
"""Return stretching part of ``DeformationGradient2`` from left polar decomposition."""
|
|
120
|
+
_, V = spla.polar(self, "left")
|
|
121
|
+
return type(self)(V)
|
|
122
|
+
|
|
123
|
+
def angle(self):
|
|
124
|
+
"""Return rotation part of ``DeformationGradient2`` as angle."""
|
|
125
|
+
R, _ = spla.polar(self)
|
|
126
|
+
return atan2d(R[1, 0], R[0, 0])
|
|
127
|
+
|
|
128
|
+
def velgrad(self, time=1):
|
|
129
|
+
"""Return ``VelocityGradient2`` for given time"""
|
|
130
|
+
return VelocityGradient2(spla.logm(np.asarray(self)) / time)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class VelocityGradient2(Matrix2):
|
|
134
|
+
"""
|
|
135
|
+
The class to represent 2D velocity gradient tensor.
|
|
136
|
+
|
|
137
|
+
Args:
|
|
138
|
+
a (2x2 array_like): Input data, that can be converted to
|
|
139
|
+
2x2 2D array. This includes lists, tuples and ndarrays.
|
|
140
|
+
|
|
141
|
+
Returns:
|
|
142
|
+
``VelocityGradient2`` object
|
|
143
|
+
|
|
144
|
+
Example:
|
|
145
|
+
>>> L = velgrad2(np.diag([0.1, -0.1]))
|
|
146
|
+
|
|
147
|
+
"""
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
def from_comp(cls, xx=0, xy=0, yx=0, yy=0):
|
|
151
|
+
"""Return ``VelocityGradient2`` defined by individual components.
|
|
152
|
+
Default is zero tensor.
|
|
153
|
+
|
|
154
|
+
Keyword Args:
|
|
155
|
+
xx (float): tensor component L_xx
|
|
156
|
+
xy (float): tensor component L_xy
|
|
157
|
+
yx (float): tensor component L_yx
|
|
158
|
+
yy (float): tensor component L_yy
|
|
159
|
+
|
|
160
|
+
Example:
|
|
161
|
+
>>> L = velgrad2.from_comp(xy=2)
|
|
162
|
+
>>> L
|
|
163
|
+
[[0. 2.]
|
|
164
|
+
[0. 0.]]
|
|
165
|
+
|
|
166
|
+
"""
|
|
167
|
+
|
|
168
|
+
return cls([[xx, xy], [yx, yy]])
|
|
169
|
+
|
|
170
|
+
def defgrad(self, time=1, steps=1):
|
|
171
|
+
"""
|
|
172
|
+
Return ``DeformationGradient2`` tensor accumulated after given time.
|
|
173
|
+
|
|
174
|
+
Keyword Args:
|
|
175
|
+
time (float): time of deformation. Default 1
|
|
176
|
+
steps (int): when bigger than 1, will return a list
|
|
177
|
+
of ``DeformationGradient2`` tensors for each timestep.
|
|
178
|
+
"""
|
|
179
|
+
if steps > 1: # FIX once container for matrix will be implemented
|
|
180
|
+
return [
|
|
181
|
+
DeformationGradient2(spla.expm(np.asarray(self) * t))
|
|
182
|
+
for t in np.linspace(0, time, steps)
|
|
183
|
+
]
|
|
184
|
+
else:
|
|
185
|
+
return DeformationGradient2(spla.expm(np.asarray(self) * time))
|
|
186
|
+
|
|
187
|
+
def rate(self):
|
|
188
|
+
"""
|
|
189
|
+
Return rate of deformation tensor
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
return type(self)((self + self.T) / 2)
|
|
193
|
+
|
|
194
|
+
def spin(self):
|
|
195
|
+
"""
|
|
196
|
+
Return spin tensor
|
|
197
|
+
"""
|
|
198
|
+
|
|
199
|
+
return type(self)((self - self.T) / 2)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class Tensor2(Matrix2):
|
|
203
|
+
pass
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
class Stress2(Tensor2):
|
|
207
|
+
"""
|
|
208
|
+
The class to represent 2D stress tensor.
|
|
209
|
+
|
|
210
|
+
Args:
|
|
211
|
+
a (2x2 array_like): Input data, that can be converted to
|
|
212
|
+
2x2 2D array. This includes lists, tuples and ndarrays.
|
|
213
|
+
|
|
214
|
+
Returns:
|
|
215
|
+
``Stress2`` object
|
|
216
|
+
|
|
217
|
+
Example:
|
|
218
|
+
>>> S = Stress2([[-8, 0, 0],[0, -5, 0],[0, 0, -1]])
|
|
219
|
+
|
|
220
|
+
"""
|
|
221
|
+
|
|
222
|
+
@classmethod
|
|
223
|
+
def from_comp(cls, xx=0, xy=0, yy=0):
|
|
224
|
+
"""
|
|
225
|
+
Return ``Stress2`` tensor. Default is zero tensor.
|
|
226
|
+
|
|
227
|
+
Note that stress tensor must be symmetrical.
|
|
228
|
+
|
|
229
|
+
Keyword Args:
|
|
230
|
+
xx, xy, yy (float): tensor components
|
|
231
|
+
|
|
232
|
+
Example:
|
|
233
|
+
>>> S = stress2.from_comp(xx=-5, yy=-2, xy=1)
|
|
234
|
+
>>> S
|
|
235
|
+
Stress2
|
|
236
|
+
[[-5. 1.]
|
|
237
|
+
[ 1. -2.]]
|
|
238
|
+
"""
|
|
239
|
+
|
|
240
|
+
return cls([[xx, xy], [xy, yy]])
|
|
241
|
+
|
|
242
|
+
@property
|
|
243
|
+
def mean_stress(self):
|
|
244
|
+
"""
|
|
245
|
+
Mean stress
|
|
246
|
+
"""
|
|
247
|
+
|
|
248
|
+
return self.I1 / 2
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def hydrostatic(self):
|
|
252
|
+
"""
|
|
253
|
+
Mean hydrostatic stress tensor component
|
|
254
|
+
"""
|
|
255
|
+
|
|
256
|
+
return type(self)(np.diag(self.mean_stress * np.ones(2)))
|
|
257
|
+
|
|
258
|
+
@property
|
|
259
|
+
def deviatoric(self):
|
|
260
|
+
"""
|
|
261
|
+
A stress deviator tensor component
|
|
262
|
+
"""
|
|
263
|
+
|
|
264
|
+
return type(self)(self - self.hydrostatic)
|
|
265
|
+
|
|
266
|
+
@property
|
|
267
|
+
def sigma1(self):
|
|
268
|
+
"""
|
|
269
|
+
A maximum principal stress (max compressive)
|
|
270
|
+
"""
|
|
271
|
+
|
|
272
|
+
return self.E2
|
|
273
|
+
|
|
274
|
+
@property
|
|
275
|
+
def sigma2(self):
|
|
276
|
+
"""
|
|
277
|
+
A minimum principal stress
|
|
278
|
+
"""
|
|
279
|
+
|
|
280
|
+
return self.E1
|
|
281
|
+
|
|
282
|
+
@property
|
|
283
|
+
def sigma1dir(self):
|
|
284
|
+
"""
|
|
285
|
+
Return unit length vector in direction of maximum
|
|
286
|
+
principal stress (max compressive)
|
|
287
|
+
"""
|
|
288
|
+
|
|
289
|
+
return self.V2
|
|
290
|
+
|
|
291
|
+
@property
|
|
292
|
+
def sigma2dir(self):
|
|
293
|
+
"""
|
|
294
|
+
Return unit length vector in direction of minimum
|
|
295
|
+
principal stress
|
|
296
|
+
"""
|
|
297
|
+
|
|
298
|
+
return self.V1
|
|
299
|
+
|
|
300
|
+
@property
|
|
301
|
+
def sigma1vec(self):
|
|
302
|
+
"""
|
|
303
|
+
Return maximum principal stress vector (max compressive)
|
|
304
|
+
"""
|
|
305
|
+
|
|
306
|
+
return self.E2 * self.V2
|
|
307
|
+
|
|
308
|
+
@property
|
|
309
|
+
def sigma2vec(self):
|
|
310
|
+
"""
|
|
311
|
+
Return minimum principal stress vector
|
|
312
|
+
"""
|
|
313
|
+
|
|
314
|
+
return self.E1 * self.V1
|
|
315
|
+
|
|
316
|
+
@property
|
|
317
|
+
def I1(self):
|
|
318
|
+
"""
|
|
319
|
+
First invariant
|
|
320
|
+
"""
|
|
321
|
+
|
|
322
|
+
return float(np.trace(self))
|
|
323
|
+
|
|
324
|
+
@property
|
|
325
|
+
def I2(self):
|
|
326
|
+
"""
|
|
327
|
+
Second invariant
|
|
328
|
+
"""
|
|
329
|
+
|
|
330
|
+
return float((self.I1**2 - np.trace(self**2)) / 2)
|
|
331
|
+
|
|
332
|
+
@property
|
|
333
|
+
def I3(self):
|
|
334
|
+
"""
|
|
335
|
+
Third invariant
|
|
336
|
+
"""
|
|
337
|
+
|
|
338
|
+
return self.det
|
|
339
|
+
|
|
340
|
+
@property
|
|
341
|
+
def diagonalized(self):
|
|
342
|
+
"""
|
|
343
|
+
Returns diagonalized Stress tensor and orthogonal matrix R, which transforms actual
|
|
344
|
+
coordinate system to the principal one.
|
|
345
|
+
"""
|
|
346
|
+
return (
|
|
347
|
+
type(self)(np.diag(self.eigenvalues())),
|
|
348
|
+
DeformationGradient2(self.eigenvectors()),
|
|
349
|
+
)
|
|
350
|
+
|
|
351
|
+
def cauchy(self, n):
|
|
352
|
+
"""
|
|
353
|
+
Return stress vector associated with plane given by normal vector.
|
|
354
|
+
|
|
355
|
+
Args:
|
|
356
|
+
n: normal given as ``Vector2`` object
|
|
357
|
+
|
|
358
|
+
Example:
|
|
359
|
+
>>> S = Stress.from_comp(xx=-5, yy=-2, xy=1)
|
|
360
|
+
>>> S.cauchy(vec2(1,1))
|
|
361
|
+
V(-2.520, 0.812, 8.660)
|
|
362
|
+
|
|
363
|
+
"""
|
|
364
|
+
|
|
365
|
+
return Vector2(np.dot(self, n.normalized()))
|
|
366
|
+
|
|
367
|
+
def stress_comp(self, n):
|
|
368
|
+
"""
|
|
369
|
+
Return normal and shear stress ``Vector2`` components on plane given
|
|
370
|
+
by normal vector.
|
|
371
|
+
"""
|
|
372
|
+
|
|
373
|
+
t = self.cauchy(n)
|
|
374
|
+
sn = t.proj(n)
|
|
375
|
+
|
|
376
|
+
return sn, t - sn
|
|
377
|
+
|
|
378
|
+
def normal_stress(self, n):
|
|
379
|
+
"""
|
|
380
|
+
Return normal stress magnitude on plane given by normal vector.
|
|
381
|
+
"""
|
|
382
|
+
|
|
383
|
+
return float(np.dot(n, self.cauchy(n)))
|
|
384
|
+
|
|
385
|
+
def shear_stress(self, n):
|
|
386
|
+
"""
|
|
387
|
+
Return shear stress magnitude on plane given by normal vector.
|
|
388
|
+
"""
|
|
389
|
+
|
|
390
|
+
sn, tau = self.stress_comp(n)
|
|
391
|
+
return abs(tau)
|
|
392
|
+
|
|
393
|
+
def signed_shear_stress(self, n):
|
|
394
|
+
"""
|
|
395
|
+
Return signed shear stress magnitude on plane given by normal vector.
|
|
396
|
+
"""
|
|
397
|
+
R = DeformationGradient2.from_angle(n.direction)
|
|
398
|
+
return self.transform(R)[1, 0]
|
|
399
|
+
|
|
400
|
+
|
|
401
|
+
class Ellipse(Tensor2):
|
|
402
|
+
"""
|
|
403
|
+
The class to represent 2D ellipse
|
|
404
|
+
|
|
405
|
+
See following methods and properties for additional operations.
|
|
406
|
+
|
|
407
|
+
Args:
|
|
408
|
+
matrix (2x2 array_like): Input data, that can be converted to
|
|
409
|
+
2x2 2D matrix. This includes lists, tuples and ndarrays.
|
|
410
|
+
|
|
411
|
+
Returns:
|
|
412
|
+
``Ellipse`` object
|
|
413
|
+
|
|
414
|
+
Example:
|
|
415
|
+
>>> E = ellipse([[8, 0], [0, 2]])
|
|
416
|
+
>>> E
|
|
417
|
+
Ellipse
|
|
418
|
+
[[8. 0.]
|
|
419
|
+
[0. 2.]]
|
|
420
|
+
(ar:2, ori:0)
|
|
421
|
+
|
|
422
|
+
"""
|
|
423
|
+
|
|
424
|
+
def __repr__(self) -> str:
|
|
425
|
+
return (
|
|
426
|
+
f"{Matrix2.__repr__(self)}\n(ar:{self.ar:.3g}, ori:{self.orientation:.3g})"
|
|
427
|
+
)
|
|
428
|
+
|
|
429
|
+
@classmethod
|
|
430
|
+
def from_defgrad(cls, F, form="left", **kwargs) -> "Ellipse":
|
|
431
|
+
"""
|
|
432
|
+
Return deformation tensor from ``Defgrad2``.
|
|
433
|
+
|
|
434
|
+
Kwargs:
|
|
435
|
+
form: 'left' or 'B' for left Cauchy–Green deformation tensor or
|
|
436
|
+
Finger deformation tensor
|
|
437
|
+
'right' or 'C' for right Cauchy–Green deformation tensor or
|
|
438
|
+
Green's deformation tensor.
|
|
439
|
+
Default is 'left'.
|
|
440
|
+
"""
|
|
441
|
+
if form in ("left", "B"):
|
|
442
|
+
return cls(np.dot(F, np.transpose(F)), **kwargs)
|
|
443
|
+
elif form in ("right", "C"):
|
|
444
|
+
return cls(np.dot(np.transpose(F), F), **kwargs)
|
|
445
|
+
else:
|
|
446
|
+
raise TypeError("Wrong form argument")
|
|
447
|
+
|
|
448
|
+
@classmethod
|
|
449
|
+
def from_stretch(cls, x=1, y=1, **kwargs) -> "Ellipse":
|
|
450
|
+
"""
|
|
451
|
+
Return diagonal tensor defined by magnitudes of principal stretches.
|
|
452
|
+
"""
|
|
453
|
+
return cls([[x * x, 0], [0, y * y]], **kwargs)
|
|
454
|
+
|
|
455
|
+
@property
|
|
456
|
+
def S1(self) -> float:
|
|
457
|
+
"""
|
|
458
|
+
Return the maximum principal stretch.
|
|
459
|
+
"""
|
|
460
|
+
return math.sqrt(self.E1)
|
|
461
|
+
|
|
462
|
+
@property
|
|
463
|
+
def S2(self) -> float:
|
|
464
|
+
"""
|
|
465
|
+
Return the minimum principal stretch.
|
|
466
|
+
"""
|
|
467
|
+
return math.sqrt(self.E2)
|
|
468
|
+
|
|
469
|
+
@property
|
|
470
|
+
def e1(self) -> float:
|
|
471
|
+
"""
|
|
472
|
+
Return the maximum natural principal strain.
|
|
473
|
+
"""
|
|
474
|
+
return math.log(self.S1)
|
|
475
|
+
|
|
476
|
+
@property
|
|
477
|
+
def e2(self) -> float:
|
|
478
|
+
"""
|
|
479
|
+
Return the minimum natural principal strain.
|
|
480
|
+
"""
|
|
481
|
+
return math.log(self.S2)
|
|
482
|
+
|
|
483
|
+
@property
|
|
484
|
+
def ar(self) -> float:
|
|
485
|
+
"""
|
|
486
|
+
Return the ellipse axial ratio.
|
|
487
|
+
"""
|
|
488
|
+
return self.S1 / self.S2
|
|
489
|
+
|
|
490
|
+
@property
|
|
491
|
+
def orientation(self):
|
|
492
|
+
"""
|
|
493
|
+
Return the orientation of the maximum eigenvector.
|
|
494
|
+
"""
|
|
495
|
+
return self.V1.direction % 180
|
|
496
|
+
|
|
497
|
+
@property
|
|
498
|
+
def e12(self) -> float:
|
|
499
|
+
"""
|
|
500
|
+
Return the difference between natural principal strains.
|
|
501
|
+
"""
|
|
502
|
+
return self.e1 - self.e2
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
class OrientationTensor2(Ellipse):
|
|
506
|
+
"""
|
|
507
|
+
Represents an 2D orientation tensor, which characterize data distribution
|
|
508
|
+
using eigenvalue method. See (Watson 1966, Scheidegger 1965).
|
|
509
|
+
|
|
510
|
+
See following methods and properties for additional operations.
|
|
511
|
+
|
|
512
|
+
Args:
|
|
513
|
+
matrix (2x2 array_like): Input data, that can be converted to
|
|
514
|
+
2x2 2D matrix. This includes lists, tuples and ndarrays.
|
|
515
|
+
Array could be also ``Group`` (for backward compatibility)
|
|
516
|
+
|
|
517
|
+
Returns:
|
|
518
|
+
``OrientationTensor2`` object
|
|
519
|
+
|
|
520
|
+
Example:
|
|
521
|
+
>>> v = vec2set.random(n=1000)
|
|
522
|
+
>>> ot = v.ortensor()
|
|
523
|
+
>>> ot
|
|
524
|
+
OrientationTensor2
|
|
525
|
+
[[ 0.502 -0.011]
|
|
526
|
+
[-0.011 0.498]]
|
|
527
|
+
(ar:1.02, ori:140)
|
|
528
|
+
|
|
529
|
+
"""
|
|
530
|
+
|
|
531
|
+
@classmethod
|
|
532
|
+
def from_features(cls, g) -> "OrientationTensor2":
|
|
533
|
+
"""
|
|
534
|
+
Return ``Ortensor`` of data in Vector2Set features
|
|
535
|
+
|
|
536
|
+
Args:
|
|
537
|
+
g (Vector2Set): Set of features
|
|
538
|
+
|
|
539
|
+
Example:
|
|
540
|
+
>>> v = vec2set.random_vonmises(position=120)
|
|
541
|
+
>>> ot = v.ortensor()
|
|
542
|
+
>>> ot
|
|
543
|
+
OrientationTensor2
|
|
544
|
+
[[ 0.377 -0.282]
|
|
545
|
+
[-0.282 0.623]]
|
|
546
|
+
(ar:2.05, ori:123)
|
|
547
|
+
|
|
548
|
+
"""
|
|
549
|
+
|
|
550
|
+
return cls(np.dot(np.array(g).T, np.array(g)) / len(g))
|