apsg 1.3.4__py3-none-any.whl → 1.3.5__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.
apsg/__init__.py CHANGED
@@ -1,56 +1,114 @@
1
1
  # -*- coding: utf-8 -*-
2
2
 
3
- from apsg.math import (
4
- Vector3 as vec,
5
- Vector2 as vec2,
6
- Matrix2 as matrix2,
7
- Matrix3 as matrix,
8
- )
9
3
  from apsg.config import apsg_conf
10
4
  from apsg.feature import (
11
- Lineation as lin,
12
- Foliation as fol,
13
- Pair as pair,
14
- Fault as fault,
5
+ ClusterSet as cluster,
6
+ )
7
+ from apsg.feature import (
15
8
  Cone as cone,
16
9
  )
17
10
  from apsg.feature import (
18
- Vector3Set as vecset,
19
- Vector2Set as vec2set,
20
- LineationSet as linset,
21
- FoliationSet as folset,
22
- PairSet as pairset,
23
- FaultSet as faultset,
24
11
  ConeSet as coneset,
12
+ )
13
+ from apsg.feature import (
14
+ DeformationGradient2 as defgrad2,
15
+ )
16
+ from apsg.feature import (
17
+ DeformationGradient3 as defgrad,
18
+ )
19
+ from apsg.feature import (
20
+ Direction as dir2,
21
+ )
22
+ from apsg.feature import (
23
+ Direction2Set as dir2set,
24
+ )
25
+ from apsg.feature import (
26
+ Ellipse as ellipse,
27
+ )
28
+ from apsg.feature import (
25
29
  EllipseSet as ellipseset,
30
+ )
31
+ from apsg.feature import (
32
+ Ellipsoid as ellipsoid,
33
+ )
34
+ from apsg.feature import (
26
35
  EllipsoidSet as ellipsoidset,
36
+ )
37
+ from apsg.feature import (
38
+ Fault as fault,
39
+ )
40
+ from apsg.feature import (
41
+ FaultSet as faultset,
42
+ )
43
+ from apsg.feature import (
44
+ Foliation as fol,
45
+ )
46
+ from apsg.feature import (
47
+ FoliationSet as folset,
48
+ )
49
+ from apsg.feature import G
50
+ from apsg.feature import (
51
+ Lineation as lin,
52
+ )
53
+ from apsg.feature import (
54
+ LineationSet as linset,
55
+ )
56
+ from apsg.feature import (
57
+ OrientationTensor2 as ortensor2,
58
+ )
59
+ from apsg.feature import (
27
60
  OrientationTensor2Set as ortensor2set,
61
+ )
62
+ from apsg.feature import (
63
+ OrientationTensor3 as ortensor,
64
+ )
65
+ from apsg.feature import (
28
66
  OrientationTensor3Set as ortensorset,
29
- ClusterSet as cluster,
30
67
  )
31
68
  from apsg.feature import (
32
- DeformationGradient3 as defgrad,
33
- VelocityGradient3 as velgrad,
69
+ Pair as pair,
70
+ )
71
+ from apsg.feature import (
72
+ PairSet as pairset,
73
+ )
74
+ from apsg.feature import (
75
+ Stress2 as stress2,
76
+ )
77
+ from apsg.feature import (
34
78
  Stress3 as stress,
35
- Ellipsoid as ellipsoid,
36
- OrientationTensor3 as ortensor,
37
79
  )
38
80
  from apsg.feature import (
39
- DeformationGradient2 as defgrad2,
81
+ Vector2Set as vec2set,
82
+ )
83
+ from apsg.feature import (
84
+ Vector3Set as vecset,
85
+ )
86
+ from apsg.feature import (
40
87
  VelocityGradient2 as velgrad2,
41
- Stress2 as stress2,
42
- Ellipse as ellipse,
43
- OrientationTensor2 as ortensor2,
44
88
  )
45
- from apsg.feature import G
89
+ from apsg.feature import (
90
+ VelocityGradient3 as velgrad,
91
+ )
92
+ from apsg.math import (
93
+ Matrix2 as matrix2,
94
+ )
95
+ from apsg.math import (
96
+ Matrix3 as matrix,
97
+ )
98
+ from apsg.math import (
99
+ Vector2 as vec2,
100
+ )
101
+ from apsg.math import (
102
+ Vector3 as vec,
103
+ )
46
104
  from apsg.plotting import (
105
+ FlinnPlot,
106
+ HsuPlot,
107
+ RamsayPlot,
108
+ RosePlot,
47
109
  StereoGrid,
48
110
  StereoNet,
49
- RosePlot,
50
111
  VollmerPlot,
51
- RamsayPlot,
52
- FlinnPlot,
53
- HsuPlot,
54
112
  quicknet,
55
113
  )
56
114
 
@@ -60,6 +118,7 @@ __all__ = (
60
118
  "vec2",
61
119
  "matrix",
62
120
  "matrix2",
121
+ "dir2",
63
122
  "lin",
64
123
  "fol",
65
124
  "pair",
@@ -67,6 +126,7 @@ __all__ = (
67
126
  "cone",
68
127
  "vecset",
69
128
  "vec2set",
129
+ "dir2set",
70
130
  "linset",
71
131
  "folset",
72
132
  "pairset",
@@ -98,6 +158,6 @@ __all__ = (
98
158
  "quicknet",
99
159
  )
100
160
 
101
- __version__ = "1.3.4"
161
+ __version__ = "1.3.5"
102
162
  __author__ = "Ondrej Lexa"
103
163
  __email__ = "lexa.ondrej@gmail.com"
apsg/feature/__init__.py CHANGED
@@ -1,39 +1,42 @@
1
1
  # -*- coding: utf-8 -*-
2
2
  import sys
3
- from apsg.feature._geodata import Lineation, Foliation, Pair, Fault, Cone
3
+
4
4
  from apsg.feature._container import (
5
- FeatureSet,
6
- Vector2Set,
7
- Vector3Set,
8
- LineationSet,
9
- FoliationSet,
10
- PairSet,
11
- FaultSet,
5
+ ClusterSet,
12
6
  ConeSet,
7
+ Direction2Set,
13
8
  EllipseSet,
14
9
  EllipsoidSet,
10
+ FaultSet,
11
+ FeatureSet,
12
+ FoliationSet,
13
+ G,
14
+ LineationSet,
15
15
  OrientationTensor2Set,
16
16
  OrientationTensor3Set,
17
- G,
18
- ClusterSet,
19
- )
20
- from apsg.feature._tensor3 import (
21
- DeformationGradient3,
22
- VelocityGradient3,
23
- Stress3,
24
- Ellipsoid,
25
- OrientationTensor3,
17
+ PairSet,
18
+ Vector2Set,
19
+ Vector3Set,
26
20
  )
21
+ from apsg.feature._geodata import Cone, Direction, Fault, Foliation, Lineation, Pair
22
+ from apsg.feature._paleomag import Core
27
23
  from apsg.feature._tensor2 import (
28
24
  DeformationGradient2,
29
- VelocityGradient2,
30
- Stress2,
31
25
  Ellipse,
32
26
  OrientationTensor2,
27
+ Stress2,
28
+ VelocityGradient2,
29
+ )
30
+ from apsg.feature._tensor3 import (
31
+ DeformationGradient3,
32
+ Ellipsoid,
33
+ OrientationTensor3,
34
+ Stress3,
35
+ VelocityGradient3,
33
36
  )
34
- from apsg.feature._paleomag import Core
35
37
 
36
38
  __all__ = (
39
+ "Direction",
37
40
  "Lineation",
38
41
  "Foliation",
39
42
  "Pair",
@@ -50,6 +53,7 @@ __all__ = (
50
53
  "Ellipse",
51
54
  "OrientationTensor2",
52
55
  "Vector2Set",
56
+ "Direction2Set",
53
57
  "FeatureSet",
54
58
  "Vector3Set",
55
59
  "LineationSet",
@@ -1,17 +1,19 @@
1
- import sys
1
+ import csv
2
2
  from itertools import combinations
3
- import numpy as np
3
+ from os.path import basename
4
+
4
5
  import matplotlib.pyplot as plt
6
+ import numpy as np
7
+ from scipy.cluster.hierarchy import dendrogram, fcluster, linkage
5
8
  from scipy.stats import vonmises
6
- from scipy.cluster.hierarchy import linkage, fcluster, dendrogram
7
9
 
8
10
  from apsg.config import apsg_conf
9
- from apsg.math._vector import Vector2, Vector3
10
- from apsg.helpers._math import acosd
11
- from apsg.feature._geodata import Lineation, Foliation, Pair, Fault, Cone
12
- from apsg.feature._tensor3 import OrientationTensor3, Ellipsoid, DeformationGradient3
13
- from apsg.feature._tensor2 import OrientationTensor2, Ellipse
11
+ from apsg.feature._geodata import Cone, Direction, Fault, Foliation, Lineation, Pair
14
12
  from apsg.feature._statistics import KentDistribution, vonMisesFisher
13
+ from apsg.feature._tensor2 import Ellipse, OrientationTensor2
14
+ from apsg.feature._tensor3 import DeformationGradient3, Ellipsoid, OrientationTensor3
15
+ from apsg.helpers._math import acosd
16
+ from apsg.math._vector import Axial2, Axial3, Vector2, Vector3
15
17
 
16
18
 
17
19
  class FeatureSet:
@@ -22,12 +24,11 @@ class FeatureSet:
22
24
  __slots__ = ("data", "name")
23
25
 
24
26
  def __init__(self, data, name="Default"):
25
- dtype_cls = getattr(sys.modules[__name__], type(self).__feature_type__)
26
27
  assert all(
27
- [isinstance(obj, dtype_cls) for obj in data]
28
- ), f"Data must be instances of {type(self).__feature_type__}"
28
+ [isinstance(obj, self.__feature_class__) for obj in data]
29
+ ), f"Data must be instances of {self.__feature_class__.__name__}"
29
30
  # cast to correct instances
30
- self.data = tuple([dtype_cls(d) for d in data])
31
+ self.data = tuple([self.__feature_class__(d) for d in data])
31
32
  self.name = name
32
33
  self._cache = {}
33
34
 
@@ -117,7 +118,7 @@ class Vector2Set(FeatureSet):
117
118
  Class to store set of ``Vector2`` features
118
119
  """
119
120
 
120
- __feature_type__ = "Vector2"
121
+ __feature_class__ = Vector2
121
122
 
122
123
  def __repr__(self):
123
124
  return f"V2({len(self)}) {self.name}"
@@ -283,7 +284,6 @@ class Vector2Set(FeatureSet):
283
284
  resultant.
284
285
 
285
286
  """
286
- dtype_cls = getattr(sys.modules[__name__], type(self).__feature_type__)
287
287
  v = Vector3Set(self)
288
288
  v_data = list(v)
289
289
  alldone = np.all(v.angle(v.R()) <= 90)
@@ -294,10 +294,10 @@ class Vector2Set(FeatureSet):
294
294
  v_data[ix] = -v_data[ix]
295
295
  v = Vector3Set(v_data)
296
296
  alldone = np.all(v.angle(v.R()) <= 90)
297
- return type(self)([dtype_cls(vec) for vec in v], name=self.name)
297
+ return type(self)([self.__feature_class__(vec) for vec in v], name=self.name)
298
298
 
299
299
  @classmethod
300
- def from_direction(cls, angles, name="Default"):
300
+ def from_directions(cls, angles, name="Default"):
301
301
  """Create ``Vector2Set`` object from arrays of direction angles
302
302
 
303
303
  Args:
@@ -309,8 +309,7 @@ class Vector2Set(FeatureSet):
309
309
  Example:
310
310
  >>> f = vec2set.from_angles([120,130,140,125, 132. 131])
311
311
  """
312
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
313
- return cls([dtype_cls(a) for a in angles], name=name)
312
+ return cls([cls.__feature_class__(a) for a in angles], name=name)
314
313
 
315
314
  @classmethod
316
315
  def from_xy(cls, x, y, name="Default"):
@@ -327,8 +326,7 @@ class Vector2Set(FeatureSet):
327
326
  >>> v = vec2set.from_xy([-0.4330127, -0.4330127, -0.66793414],
328
327
  [0.75, 0.25, 0.60141061])
329
328
  """
330
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
331
- return cls([dtype_cls(xx, yy) for xx, yy in zip(x, y)], name=name)
329
+ return cls([cls.__feature_class__(xx, yy) for xx, yy in zip(x, y)], name=name)
332
330
 
333
331
  @classmethod
334
332
  def random(cls, n=100, name="Default"):
@@ -344,8 +342,7 @@ class Vector2Set(FeatureSet):
344
342
  >>> l = vec2set.random(100)
345
343
 
346
344
  """
347
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
348
- return cls([dtype_cls.random() for i in range(n)], name=name)
345
+ return cls([cls.__feature_class__.random() for i in range(n)], name=name)
349
346
 
350
347
  @classmethod
351
348
  def random_vonmises(cls, n=100, position=0, kappa=5, name="Default"):
@@ -361,9 +358,19 @@ class Vector2Set(FeatureSet):
361
358
  Example:
362
359
  >>> l = linset.random_fisher(position=lin(120,50))
363
360
  """
364
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
365
361
  angles = np.degrees(vonmises.rvs(kappa, loc=np.radians(position), size=n))
366
- return cls([dtype_cls(a) for a in angles], name=name)
362
+ return cls([cls.__feature_class__(a) for a in angles], name=name)
363
+
364
+
365
+ class Direction2Set(Vector2Set):
366
+ """
367
+ Class to store set of ``Direction`` features
368
+ """
369
+
370
+ __feature_class__ = Direction
371
+
372
+ def __repr__(self):
373
+ return f"D2({len(self)}) {self.name}"
367
374
 
368
375
 
369
376
  class Vector3Set(FeatureSet):
@@ -371,7 +378,7 @@ class Vector3Set(FeatureSet):
371
378
  Class to store set of ``Vector3`` features
372
379
  """
373
380
 
374
- __feature_type__ = "Vector3"
381
+ __feature_class__ = Vector3
375
382
 
376
383
  def __repr__(self):
377
384
  return f"V3({len(self)}) {self.name}"
@@ -597,7 +604,6 @@ class Vector3Set(FeatureSet):
597
604
  resultant.
598
605
 
599
606
  """
600
- dtype_cls = getattr(sys.modules[__name__], type(self).__feature_type__)
601
607
  v = Vector3Set(self)
602
608
  v_data = list(v)
603
609
  alldone = np.all(v.angle(v.R()) <= 90)
@@ -608,7 +614,7 @@ class Vector3Set(FeatureSet):
608
614
  v_data[ix] = -v_data[ix]
609
615
  v = Vector3Set(v_data)
610
616
  alldone = np.all(v.angle(v.R()) <= 90)
611
- return type(self)([dtype_cls(vec) for vec in v], name=self.name)
617
+ return type(self)([self.__feature_class__(vec) for vec in v], name=self.name)
612
618
 
613
619
  @classmethod
614
620
  def from_csv(cls, filename, acol=0, icol=1):
@@ -627,9 +633,6 @@ class Vector3Set(FeatureSet):
627
633
  >>> gl = linset.from_csv('file2.csv', acol=1, icol=2) #doctest: +SKIP
628
634
 
629
635
  """
630
- from os.path import basename
631
- import csv
632
-
633
636
  with open(filename) as csvfile:
634
637
  has_header = csv.Sniffer().has_header(csvfile.read(1024))
635
638
  csvfile.seek(0)
@@ -665,7 +668,6 @@ class Vector3Set(FeatureSet):
665
668
  Note: Written values are rounded according to `ndigits` settings in apsg_conf
666
669
 
667
670
  """
668
- import csv
669
671
 
670
672
  n = apsg_conf["ndigits"]
671
673
 
@@ -692,8 +694,9 @@ class Vector3Set(FeatureSet):
692
694
  >>> f = folset.from_array([120,130,140], [10,20,30])
693
695
  >>> l = linset.from_array([120,130,140], [10,20,30])
694
696
  """
695
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
696
- return cls([dtype_cls(azi, inc) for azi, inc in zip(azis, incs)], name=name)
697
+ return cls(
698
+ [cls.__feature_class__(azi, inc) for azi, inc in zip(azis, incs)], name=name
699
+ )
697
700
 
698
701
  @classmethod
699
702
  def from_xyz(cls, x, y, z, name="Default"):
@@ -712,8 +715,10 @@ class Vector3Set(FeatureSet):
712
715
  [0.75, 0.25, 0.60141061],
713
716
  [0.5, 0.8660254, 0.43837115])
714
717
  """
715
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
716
- return cls([dtype_cls(xx, yy, zz) for xx, yy, zz in zip(x, y, z)], name=name)
718
+ return cls(
719
+ [cls.__feature_class__(xx, yy, zz) for xx, yy, zz in zip(x, y, z)],
720
+ name=name,
721
+ )
717
722
 
718
723
  @classmethod
719
724
  def random_normal(cls, n=100, position=Vector3(0, 0, 1), sigma=20, name="Default"):
@@ -733,7 +738,6 @@ class Vector3Set(FeatureSet):
733
738
 
734
739
  """
735
740
  data = []
736
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
737
741
  orig = Vector3(0, 0, 1)
738
742
  ax = orig.cross(position)
739
743
  ang = orig.angle(position)
@@ -742,7 +746,7 @@ class Vector3Set(FeatureSet):
742
746
  np.random.normal(loc=0, scale=sigma, size=n),
743
747
  ):
744
748
  v = orig.rotate(Vector3(s, 0), r).rotate(ax, ang)
745
- data.append(dtype_cls(v))
749
+ data.append(cls.__feature_class__(v))
746
750
  return cls(data, name=name)
747
751
 
748
752
  @classmethod
@@ -759,9 +763,8 @@ class Vector3Set(FeatureSet):
759
763
  Example:
760
764
  >>> l = linset.random_fisher(position=lin(120,50))
761
765
  """
762
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
763
766
  dc = vonMisesFisher(position, kappa, n)
764
- return cls([dtype_cls(d) for d in dc], name=name)
767
+ return cls([cls.__feature_class__(d) for d in dc], name=name)
765
768
 
766
769
  @classmethod
767
770
  def random_fisher2(cls, n=100, position=Vector3(0, 0, 1), kappa=20, name="Default"):
@@ -807,11 +810,10 @@ class Vector3Set(FeatureSet):
807
810
  >>> l = linset.random_kent(p, n=300, kappa=30)
808
811
  """
809
812
  assert issubclass(type(p), Pair), "Argument must be Pair object."
810
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
811
813
  if beta is None:
812
814
  beta = kappa / 2
813
815
  kd = KentDistribution(p.lvec, p.fvec.cross(p.lvec), p.fvec, kappa, beta)
814
- return cls([dtype_cls(d) for d in kd.rvs(n)], name=name)
816
+ return cls([cls.__feature_class__(d) for d in kd.rvs(n)], name=name)
815
817
 
816
818
  @classmethod
817
819
  def uniform_sfs(cls, n=100, name="Default"):
@@ -830,14 +832,13 @@ class Vector3Set(FeatureSet):
830
832
  >>> v.ortensor().eigenvalues()
831
833
  (0.3334645347163635, 0.33333474915201167, 0.33320071613162483)
832
834
  """
833
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
834
835
  phi = (1 + np.sqrt(5)) / 2
835
836
  i2 = 2 * np.arange(n) - n + 1
836
837
  theta = 2 * np.pi * i2 / phi
837
838
  sp = i2 / n
838
839
  cp = np.sqrt((n + i2) * (n - i2)) / n
839
840
  dc = np.array([cp * np.sin(theta), cp * np.cos(theta), sp]).T
840
- return cls([dtype_cls(d) for d in dc], name=name)
841
+ return cls([cls.__feature_class__(d) for d in dc], name=name)
841
842
 
842
843
  @classmethod
843
844
  def uniform_gss(cls, n=100, name="Default"):
@@ -855,7 +856,6 @@ class Vector3Set(FeatureSet):
855
856
  >>> v.ortensor().eigenvalues()
856
857
  (0.33335688569571587, 0.33332315115436933, 0.33331996314991513)
857
858
  """
858
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
859
859
  inc = np.pi * (3 - np.sqrt(5))
860
860
  off = 2 / n
861
861
  k = np.arange(n)
@@ -863,7 +863,7 @@ class Vector3Set(FeatureSet):
863
863
  r = np.sqrt(1 - y * y)
864
864
  phi = k * inc
865
865
  dc = np.array([np.cos(phi) * r, y, np.sin(phi) * r]).T
866
- return cls([dtype_cls(d) for d in dc], name=name)
866
+ return cls([cls.__feature_class__(d) for d in dc], name=name)
867
867
 
868
868
 
869
869
  class LineationSet(Vector3Set):
@@ -871,7 +871,7 @@ class LineationSet(Vector3Set):
871
871
  Class to store set of ``Lineation`` features
872
872
  """
873
873
 
874
- __feature_type__ = "Lineation"
874
+ __feature_class__ = Lineation
875
875
 
876
876
  def __repr__(self):
877
877
  return f"L({len(self)}) {self.name}"
@@ -882,7 +882,7 @@ class FoliationSet(Vector3Set):
882
882
  Class to store set of ``Foliation`` features
883
883
  """
884
884
 
885
- __feature_type__ = "Foliation"
885
+ __feature_class__ = Foliation
886
886
 
887
887
  def __repr__(self):
888
888
  return f"S({len(self)}) {self.name}"
@@ -897,7 +897,7 @@ class PairSet(FeatureSet):
897
897
  Class to store set of ``Pair`` features
898
898
  """
899
899
 
900
- __feature_type__ = "Pair"
900
+ __feature_class__ = Pair
901
901
 
902
902
  def __repr__(self):
903
903
  return f"P({len(self)}) {self.name}"
@@ -995,9 +995,6 @@ class PairSet(FeatureSet):
995
995
  def from_csv(cls, filename, delimiter=",", facol=0, ficol=1, lacol=2, licol=3):
996
996
  """Read ``PairSet`` from csv file"""
997
997
 
998
- from os.path import basename
999
- import csv
1000
-
1001
998
  with open(filename) as csvfile:
1002
999
  has_header = csv.Sniffer().has_header(csvfile.read(1024))
1003
1000
  csvfile.seek(0)
@@ -1063,7 +1060,6 @@ class PairSet(FeatureSet):
1063
1060
  Note: Written values are rounded according to `ndigits` settings in apsg_conf
1064
1061
 
1065
1062
  """
1066
- import csv
1067
1063
 
1068
1064
  n = apsg_conf["ndigits"]
1069
1065
 
@@ -1095,10 +1091,9 @@ class PairSet(FeatureSet):
1095
1091
  name: name of ``PairSet`` object. Default is 'Default'
1096
1092
  """
1097
1093
 
1098
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
1099
1094
  return cls(
1100
1095
  [
1101
- dtype_cls(fazi, finc, lazi, linc)
1096
+ cls.__feature_class__(fazi, finc, lazi, linc)
1102
1097
  for fazi, finc, lazi, linc in zip(fazis, fincs, lazis, lincs)
1103
1098
  ],
1104
1099
  name=name,
@@ -1110,7 +1105,7 @@ class FaultSet(PairSet):
1110
1105
  Class to store set of ``Fault`` features
1111
1106
  """
1112
1107
 
1113
- __feature_type__ = "Fault"
1108
+ __feature_class__ = Fault
1114
1109
 
1115
1110
  def __repr__(self):
1116
1111
  return f"F({len(self)}) {self.name}"
@@ -1120,6 +1115,11 @@ class FaultSet(PairSet):
1120
1115
  """Return array of sense values"""
1121
1116
  return np.array([f.sense for f in self])
1122
1117
 
1118
+ @property
1119
+ def sense_str(self):
1120
+ """Return array of sense characters"""
1121
+ return np.array([f.sense_str for f in self])
1122
+
1123
1123
  @property
1124
1124
  def p_vector(self, ptangle=90):
1125
1125
  """Return p-axes of FaultSet as Vector3Set"""
@@ -1196,13 +1196,18 @@ class FaultSet(PairSet):
1196
1196
 
1197
1197
  @classmethod
1198
1198
  def from_csv(
1199
- cls, filename, delimiter=",", facol=0, ficol=1, lacol=2, licol=3, scol=4
1199
+ cls,
1200
+ filename,
1201
+ delimiter=",",
1202
+ sense_str=False,
1203
+ facol=0,
1204
+ ficol=1,
1205
+ lacol=2,
1206
+ licol=3,
1207
+ scol=4,
1200
1208
  ):
1201
1209
  """Read ``FaultSet`` from csv file"""
1202
1210
 
1203
- from os.path import basename
1204
- import csv
1205
-
1206
1211
  with open(filename) as csvfile:
1207
1212
  has_header = csv.Sniffer().has_header(csvfile.read(1024))
1208
1213
  csvfile.seek(0)
@@ -1220,48 +1225,73 @@ class FaultSet(PairSet):
1220
1225
  faname, finame = reader.fieldnames[facol], reader.fieldnames[ficol]
1221
1226
  laname, liname = reader.fieldnames[lacol], reader.fieldnames[licol]
1222
1227
  sname = reader.fieldnames[scol]
1223
- r = [
1224
- (
1225
- float(row[faname]),
1226
- float(row[finame]),
1227
- float(row[laname]),
1228
- float(row[liname]),
1229
- int(row[sname]),
1230
- )
1231
- for row in reader
1232
- ]
1228
+ if sense_str:
1229
+ r = [
1230
+ (
1231
+ float(row[faname]),
1232
+ float(row[finame]),
1233
+ float(row[laname]),
1234
+ float(row[liname]),
1235
+ row[sname],
1236
+ )
1237
+ for row in reader
1238
+ ]
1239
+ else:
1240
+ r = [
1241
+ (
1242
+ float(row[faname]),
1243
+ float(row[finame]),
1244
+ float(row[laname]),
1245
+ float(row[liname]),
1246
+ int(row[sname]),
1247
+ )
1248
+ for row in reader
1249
+ ]
1233
1250
  else:
1234
1251
  reader = csv.reader(csvfile, dialect=dialect)
1252
+
1235
1253
  r = [
1236
1254
  (
1237
1255
  float(row[facol]),
1238
1256
  float(row[ficol]),
1239
1257
  float(row[lacol]),
1240
1258
  float(row[licol]),
1241
- int(row[scol]),
1259
+ row[scol],
1242
1260
  )
1243
1261
  for row in reader
1244
1262
  ]
1245
1263
  else:
1246
1264
  if has_header:
1247
1265
  reader = csv.DictReader(csvfile, dialect=dialect)
1248
- r = [
1249
- (
1250
- float(row[facol]),
1251
- float(row[ficol]),
1252
- float(row[lacol]),
1253
- float(row[licol]),
1254
- int(row[scol]),
1255
- )
1256
- for row in reader
1257
- ]
1266
+ if sense_str:
1267
+ r = [
1268
+ (
1269
+ float(row[facol]),
1270
+ float(row[ficol]),
1271
+ float(row[lacol]),
1272
+ float(row[licol]),
1273
+ row[scol],
1274
+ )
1275
+ for row in reader
1276
+ ]
1277
+ else:
1278
+ r = [
1279
+ (
1280
+ float(row[facol]),
1281
+ float(row[ficol]),
1282
+ float(row[lacol]),
1283
+ float(row[licol]),
1284
+ int(row[scol]),
1285
+ )
1286
+ for row in reader
1287
+ ]
1258
1288
  else:
1259
1289
  raise ValueError("No header line in CSV file...")
1260
1290
 
1261
1291
  fazi, finc, lazi, linc, sense = zip(*r)
1262
1292
  return cls.from_array(fazi, finc, lazi, linc, sense, name=basename(filename))
1263
1293
 
1264
- def to_csv(self, filename, delimiter=","):
1294
+ def to_csv(self, filename, delimiter=",", sense_str=False):
1265
1295
  """Save ``FaultSet`` object to csv file
1266
1296
 
1267
1297
  Args:
@@ -1269,11 +1299,11 @@ class FaultSet(PairSet):
1269
1299
 
1270
1300
  Keyword Args:
1271
1301
  delimiter (str): values delimiter. Default ','
1302
+ sense_str (bool): save sense as N, R, S or D. Default False
1272
1303
 
1273
1304
  Note: Written values are rounded according to `ndigits` settings in apsg_conf
1274
1305
 
1275
1306
  """
1276
- import csv
1277
1307
 
1278
1308
  n = apsg_conf["ndigits"]
1279
1309
 
@@ -1284,19 +1314,30 @@ class FaultSet(PairSet):
1284
1314
  for dt in self:
1285
1315
  fazi, finc = dt.fol.geo
1286
1316
  lazi, linc = dt.lin.geo
1287
- writer.writerow(
1288
- {
1289
- "fazi": round(fazi, n),
1290
- "finc": round(finc, n),
1291
- "lazi": round(lazi, n),
1292
- "linc": round(linc, n),
1293
- "sense": dt.sense,
1294
- }
1295
- )
1317
+ if sense_str:
1318
+ writer.writerow(
1319
+ {
1320
+ "fazi": round(fazi, n),
1321
+ "finc": round(finc, n),
1322
+ "lazi": round(lazi, n),
1323
+ "linc": round(linc, n),
1324
+ "sense": dt.sense_str,
1325
+ }
1326
+ )
1327
+ else:
1328
+ writer.writerow(
1329
+ {
1330
+ "fazi": round(fazi, n),
1331
+ "finc": round(finc, n),
1332
+ "lazi": round(lazi, n),
1333
+ "linc": round(linc, n),
1334
+ "sense": dt.sense,
1335
+ }
1336
+ )
1296
1337
 
1297
1338
  @classmethod
1298
1339
  def from_array(cls, fazis, fincs, lazis, lincs, senses, name="Default"):
1299
- """Create ``PairSet`` from arrays of azimuths and inclinations
1340
+ """Create ``FaultSet`` from arrays of azimuths, inclinations and senses
1300
1341
 
1301
1342
  Args:
1302
1343
  azis: list or array of azimuths
@@ -1306,10 +1347,9 @@ class FaultSet(PairSet):
1306
1347
  name: name of ``PairSet`` object. Default is 'Default'
1307
1348
  """
1308
1349
 
1309
- dtype_cls = getattr(sys.modules[__name__], cls.__feature_type__)
1310
1350
  return cls(
1311
1351
  [
1312
- dtype_cls(fazi, finc, lazi, linc, sense)
1352
+ cls.__feature_class__(fazi, finc, lazi, linc, sense)
1313
1353
  for fazi, finc, lazi, linc, sense in zip(
1314
1354
  fazis, fincs, lazis, lincs, senses
1315
1355
  )
@@ -1323,7 +1363,7 @@ class ConeSet(FeatureSet):
1323
1363
  Class to store set of ``Cone`` features
1324
1364
  """
1325
1365
 
1326
- __feature_type__ = "Cone"
1366
+ __feature_class__ = Cone
1327
1367
 
1328
1368
  def __repr__(self):
1329
1369
  return f"C({len(self)}) {self.name}"
@@ -1334,7 +1374,7 @@ class EllipseSet(FeatureSet):
1334
1374
  Class to store set of ``Ellipse`` features
1335
1375
  """
1336
1376
 
1337
- __feature_type__ = "Ellipse"
1377
+ __feature_class__ = Ellipse
1338
1378
 
1339
1379
  @property
1340
1380
  def S1(self) -> np.ndarray:
@@ -1391,7 +1431,7 @@ class OrientationTensor2Set(EllipseSet):
1391
1431
  Class to store set of ``OrientationTensor2`` features
1392
1432
  """
1393
1433
 
1394
- __feature_type__ = "OrientationTensor2"
1434
+ __feature_class__ = OrientationTensor2
1395
1435
 
1396
1436
 
1397
1437
  class EllipsoidSet(FeatureSet):
@@ -1399,7 +1439,7 @@ class EllipsoidSet(FeatureSet):
1399
1439
  Class to store set of ``Ellipsoid`` features
1400
1440
  """
1401
1441
 
1402
- __feature_type__ = "Ellipsoid"
1442
+ __feature_class__ = Ellipsoid
1403
1443
 
1404
1444
  @property
1405
1445
  def strength(self) -> np.ndarray:
@@ -1633,7 +1673,7 @@ class OrientationTensor3Set(EllipsoidSet):
1633
1673
  Class to store set of ``OrientationTensor3`` features
1634
1674
  """
1635
1675
 
1636
- __feature_type__ = "OrientationTensor3"
1676
+ __feature_class__ = OrientationTensor3
1637
1677
 
1638
1678
 
1639
1679
  class ClusterSet(object):
@@ -1660,7 +1700,7 @@ class ClusterSet(object):
1660
1700
  isinstance(d, Vector2Set)
1661
1701
  or isinstance(d, Vector3Set)
1662
1702
  or isinstance(d, PairSet)
1663
- ), "Only vec2set or vecset could be clustered"
1703
+ ), "Only vec2set, vecset and pairset could be clustered"
1664
1704
  self.data = d.copy()
1665
1705
  self.maxclust = kwargs.get("maxclust", 2)
1666
1706
  self.angle = kwargs.get("angle", None)
@@ -1712,7 +1752,10 @@ class ClusterSet(object):
1712
1752
  """
1713
1753
 
1714
1754
  self.method = kwargs.get("method", self.method)
1715
- self.Z = linkage(self.pdist, method=self.method, metric=angle_metric)
1755
+ if issubclass(self.__feature_class__, (Axial2, Axial3)):
1756
+ self.Z = linkage(self.pdist, method=self.method, metric=angle_metric_axial)
1757
+ else:
1758
+ self.Z = linkage(self.pdist, method=self.method, metric=angle_metric)
1716
1759
 
1717
1760
  def dendrogram(self, **kwargs):
1718
1761
  """Show dendrogram
@@ -1805,4 +1848,8 @@ def G(lst, name="Default"):
1805
1848
 
1806
1849
 
1807
1850
  def angle_metric(u, v):
1851
+ return np.degrees(np.arccos(np.dot(u, v)))
1852
+
1853
+
1854
+ def angle_metric_axial(u, v):
1808
1855
  return np.degrees(np.arccos(np.abs(np.dot(u, v))))
apsg/feature/_geodata.py CHANGED
@@ -1,15 +1,50 @@
1
1
  import warnings
2
+
2
3
  import numpy as np
3
4
 
5
+ from apsg.decorator._decorator import ensure_arguments, ensure_first_arg_same
4
6
  from apsg.helpers._notation import (
5
7
  geo2vec_planar,
6
- vec2geo_planar,
7
- vec2geo_planar_signed,
8
8
  vec2geo_linear,
9
9
  vec2geo_linear_signed,
10
+ vec2geo_planar,
11
+ vec2geo_planar_signed,
10
12
  )
11
- from apsg.decorator._decorator import ensure_first_arg_same, ensure_arguments
12
- from apsg.math._vector import Vector3, Axial3
13
+ from apsg.math._vector import Axial2, Axial3, Vector3
14
+
15
+
16
+ class Direction(Axial2):
17
+ """
18
+ A class to represent axial (non-oriented) 2D linear feature (direction).
19
+
20
+ There are different way to create ``Direction`` object:
21
+
22
+ - without arguments create default ``Direction`` D:0
23
+ - with single argument `d`, where:
24
+
25
+ - `d` could be Vector2-like object
26
+ - `d` could be string 'x' or 'y' - principal axes of coordinate system
27
+ - `d` could be tuple of (x, y) - vector components
28
+ - with 1 argument direction
29
+ - with 2 numerical arguments defining vector components
30
+
31
+ Args:
32
+ direction (float): plunge direction of linear feature in degrees
33
+
34
+ Example:
35
+ >>> dir2()
36
+ >>> dir2('y')
37
+ >>> dir2(45)
38
+ >>> d = dir2(1, -1)
39
+ """
40
+
41
+ def __repr__(self):
42
+ return f"D:{self.direction:.0f}"
43
+
44
+ def to_json(self):
45
+ """Return as JSON dict"""
46
+ azi, inc = vec2geo_linear_signed(self)
47
+ return {"datatype": type(self).__name__, "args": (azi, inc)}
13
48
 
14
49
 
15
50
  class Lineation(Axial3):
@@ -446,15 +481,15 @@ class Fault(Pair):
446
481
  elif isinstance(sense, str):
447
482
  p = Pair(fvec, lvec)
448
483
  if sense.lower() == "s":
449
- if p.rax == p.rax.lower():
450
- res = -1
451
- else:
452
- res = 1
453
- elif sense.lower() == "d":
454
- if p.rax == p.rax.lower():
484
+ if p.rax.is_upper():
455
485
  res = 1
456
486
  else:
457
487
  res = -1
488
+ elif sense.lower() == "d":
489
+ if p.rax.is_upper():
490
+ res = -1
491
+ else:
492
+ res = 1
458
493
  elif sense.lower() == "n":
459
494
  res = 1
460
495
  elif sense.lower() == "r":
@@ -464,7 +499,8 @@ class Fault(Pair):
464
499
  def __repr__(self):
465
500
  fazi, finc = self.fol.geo
466
501
  lazi, linc = self.lin.geo
467
- schar = [" ", "+", "-"][self.sense]
502
+ schar = self.sense_str
503
+ # schar = [" ", "+", "-"][self.sense]
468
504
  return f"F:{fazi:.0f}/{finc:.0f}-{lazi:.0f}/{linc:.0f} {schar}"
469
505
 
470
506
  @ensure_first_arg_same
@@ -519,6 +555,24 @@ class Fault(Pair):
519
555
  else:
520
556
  return -1
521
557
 
558
+ @property
559
+ def sense_str(self):
560
+ if abs(self.rax.geo[1]) > 75:
561
+ if self.rax.z > 0:
562
+ schar = "D"
563
+ elif self.rax.z < 0:
564
+ schar = "S"
565
+ else:
566
+ schar = " "
567
+ else:
568
+ if self.sense < 0:
569
+ schar = "R"
570
+ elif self.sense > 0:
571
+ schar = "N"
572
+ else:
573
+ schar = " "
574
+ return schar
575
+
522
576
  def p_vector(self, ptangle=90):
523
577
  """Return P axis as ``Vector3``"""
524
578
  return self.fvec.rotate(self.lvec.cross(self.fvec), -ptangle / 2)
apsg/feature/_tensor3.py CHANGED
@@ -440,6 +440,30 @@ class Stress3(Tensor3):
440
440
 
441
441
  return cls([[xx, xy, xz], [xy, yy, yz], [xz, yz, zz]])
442
442
 
443
+ @classmethod
444
+ def from_ratio(cls, r=0.5, mag=1):
445
+ """
446
+ Return ``Stress`` tensor with given shape ration.
447
+
448
+ Keyword Args:
449
+ r (float): shape ratio between 0 and 1. Default 0.5
450
+ mag (float): magnitude of differential stress. Default 1.
451
+
452
+ Example:
453
+ >>> S = stress.from_ratio(r=0.25, mag=10)
454
+ >>> S
455
+ Stress3
456
+ [[-5. 0. 0. ]
457
+ [ 0. -2.5 0. ]
458
+ [ 0. 0. 5. ]]
459
+
460
+ """
461
+
462
+ xx = -mag / 2
463
+ yy = xx + r * mag
464
+ zz = mag / 2
465
+ return cls([[xx, 0, 0], [0, yy, 0], [0, 0, zz]])
466
+
443
467
  @property
444
468
  def mean_stress(self):
445
469
  """
apsg/math/_matrix.py CHANGED
@@ -4,10 +4,6 @@ from apsg.config import apsg_conf
4
4
  from apsg.decorator._decorator import ensure_first_arg_same
5
5
  from apsg.math._vector import Vector3, Vector2
6
6
 
7
- """
8
- TO BE ADDED
9
- """
10
-
11
7
 
12
8
  class Matrix:
13
9
  """Base class for Matrix2 and Matrix3"""
@@ -128,7 +124,7 @@ class Matrix:
128
124
  return self._coefs[1][1]
129
125
 
130
126
  @property
131
- def I(self):
127
+ def I(self): # noqa: E743
132
128
  return type(self)(np.linalg.inv(self))
133
129
 
134
130
  @property
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: apsg
3
- Version: 1.3.4
3
+ Version: 1.3.5
4
4
  Summary: APSG - The package for structural geologists
5
5
  Author-email: Ondrej Lexa <lexa.ondrej@gmail.com>
6
6
  Maintainer-email: Ondrej Lexa <lexa.ondrej@gmail.com>
@@ -1,4 +1,4 @@
1
- apsg/__init__.py,sha256=-6B9IAuTnyKs_jwpjcUz4lRL5QvrcH1pzUyazuh5gwc,1966
1
+ apsg/__init__.py,sha256=4i4ax3NRSxmyaPTssR_c_6j17kFU7CAbEfW02aeXjwc,2849
2
2
  apsg/config.py,sha256=X3_yXT96xXlVxFA94EfYFKJbrcGIHT0PvB9s8EKmYOg,4569
3
3
  apsg/shell.py,sha256=1D0PeB7qXzlpiOf2QYGo6OJlEVN1KJPYld1GEREBiFg,707
4
4
  apsg/database/__init__.py,sha256=7Rvcf1KBBBNhoM28ZlvQ01CkScQTroFkoS4d1kD55Ws,315
@@ -6,19 +6,19 @@ apsg/database/_alchemy.py,sha256=geV3Q6ZLdGvjzxry7GTHHIQq6t_ccsUxZvuv13CLYFQ,194
6
6
  apsg/database/_sdbread.py,sha256=EP0hSp6_4-DZZptkMNDgKnQ3GD58mpr_SAgFohKu6xQ,11017
7
7
  apsg/decorator/__init__.py,sha256=fZ-dxpldQIk6-2JhVnCj-Tsl8bz2nvoGOyG7uXKvBfg,160
8
8
  apsg/decorator/_decorator.py,sha256=8TMSrcVvhU5UCbNMrnyrW3-65qo20_6N2ShtXd3bP-k,1194
9
- apsg/feature/__init__.py,sha256=XGLq3GXpiWtzX6ROtuSc0kCPJEPTVRoIUtr8VpRZOWI,1664
10
- apsg/feature/_container.py,sha256=2hQtc6a5Z6puwfHcia4AAXsFoD8OuXO-Iqfbq6zRmnw,59283
11
- apsg/feature/_geodata.py,sha256=NJ6jZJbq_-DX2ZLADEf5AMsPzz0T17RLizIq3uH_i7U,22553
9
+ apsg/feature/__init__.py,sha256=XkyS-X8lUaXCVaXgoK0GETmTCCGqB1BmSwZRZG3blZQ,1733
10
+ apsg/feature/_container.py,sha256=Rx7rEO1DQB-dUDXIbJJlig-Ly0ler25eYLRK5uKd3aY,60443
11
+ apsg/feature/_geodata.py,sha256=tjgPSKXyEOXiAN3iPeHpm8kpj__jI4ahtTpDuWhQNBE,24008
12
12
  apsg/feature/_paleomag.py,sha256=WIICUOEJGGFHbCDF0gbW7lMt10jI0el0UVhkbMH7XAs,14911
13
13
  apsg/feature/_statistics.py,sha256=WwBU2D7SyIHMpWnO7WFqbIpSkz6KUCT9xkG8oVzgskQ,13916
14
14
  apsg/feature/_tensor2.py,sha256=XzGuSU46RkeVHQu3pqKzgzf0cYZCgbs9eP_yfYa82SU,13451
15
- apsg/feature/_tensor3.py,sha256=Z6mHoQ4Dm_ZokVMlhIUnkvHhZfUHG4VbpIkWHamRY7M,29720
15
+ apsg/feature/_tensor3.py,sha256=uRC-Vi3HFNwOM33wg5r5N8JUwOeKC64PyRV5J9QbSTk,30329
16
16
  apsg/helpers/__init__.py,sha256=6SF_Srq6fS-uomvpwdW0JSQs-tmWnzUoTk4a1K_J1aE,532
17
17
  apsg/helpers/_helper.py,sha256=1Xf1yiNYIEoUJqXH4C0390osGWk6AERr_LrCSTnsRVw,146
18
18
  apsg/helpers/_math.py,sha256=NLHw9UGiSlNggwZB-o0ORpG2sY00qIcbMbDJftRWFE8,896
19
19
  apsg/helpers/_notation.py,sha256=oSxvZdv06eJ5GotS2IwbAJ-8UTZ49z8slqdhUDQJ9e8,2486
20
20
  apsg/math/__init__.py,sha256=qJa4JP79nXn-6Xb7aaZUPWmz0qadmC4F0tGORTOZDVI,211
21
- apsg/math/_matrix.py,sha256=BGFljlkZ7khpoHZkFq-vFwWV7lUiXGAezURuKlN8eTI,10418
21
+ apsg/math/_matrix.py,sha256=4LcafIEdpnJ0yoeoD1bypfeyyVPeDDzSbQKKlF5nerg,10411
22
22
  apsg/math/_vector.py,sha256=mD8kYggfGys-XWtnE90X2ZcTGBtDN49wEAUXQYXj3dI,16587
23
23
  apsg/pandas/__init__.py,sha256=jmlsulHmH9IbErJgSWlJx1J33-v--O5zxSE4kdt6fUI,456
24
24
  apsg/pandas/_pandas_api.py,sha256=MKV7MIhPIeSMH8ryjC858F3MjyB47cA_bAKtlKbFlNc,13576
@@ -30,9 +30,9 @@ apsg/plotting/_projection.py,sha256=ix67PwOU2WGjryEcsHlVIMpcVC9yxy45ycOV9k5x_Q8,
30
30
  apsg/plotting/_roseplot.py,sha256=jbaUXSb3DIcXs0pWAQUTZfdlA2XcbquT0yHLYDjLirQ,12808
31
31
  apsg/plotting/_stereogrid.py,sha256=awh7MwN1WgszhOlr6UgR20wHQJ8u778-Tf_w1uflrV4,11869
32
32
  apsg/plotting/_stereonet.py,sha256=uHJXluFMkeaI4yzD9pGc4DIlgjZA01INySLKtUHLAlU,36624
33
- apsg-1.3.4.dist-info/licenses/LICENSE,sha256=lY0kfpVRrzcgVZq7pI6rLK5WYiUMWe0bdKpDelN6hk8,1120
34
- apsg-1.3.4.dist-info/METADATA,sha256=7HV-9ufjlab_qZ0vL3D-Il9UObgVg_FTRUfOZxGmENY,8298
35
- apsg-1.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
- apsg-1.3.4.dist-info/entry_points.txt,sha256=SowP7_uRI0NJuzznKBXyM9BJcSwBxbXo6Iz5LUo9mEQ,42
37
- apsg-1.3.4.dist-info/top_level.txt,sha256=xWxwi0nqqOyKdmpsszfR-bmqnNpgVbhnLRuIKGJnaUM,5
38
- apsg-1.3.4.dist-info/RECORD,,
33
+ apsg-1.3.5.dist-info/licenses/LICENSE,sha256=lY0kfpVRrzcgVZq7pI6rLK5WYiUMWe0bdKpDelN6hk8,1120
34
+ apsg-1.3.5.dist-info/METADATA,sha256=9Y4Y2Ixu8KOY_UgxJ_3l4m4sL726P8eRRjlmMbW7PoE,8298
35
+ apsg-1.3.5.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
36
+ apsg-1.3.5.dist-info/entry_points.txt,sha256=SowP7_uRI0NJuzznKBXyM9BJcSwBxbXo6Iz5LUo9mEQ,42
37
+ apsg-1.3.5.dist-info/top_level.txt,sha256=xWxwi0nqqOyKdmpsszfR-bmqnNpgVbhnLRuIKGJnaUM,5
38
+ apsg-1.3.5.dist-info/RECORD,,
File without changes