Skip to content

Commit

Permalink
StereoNet tensor method added
Browse files Browse the repository at this point in the history
  • Loading branch information
ondrolexa committed Sep 23, 2023
1 parent ef390ad commit 9019bad
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 54 deletions.
10 changes: 10 additions & 0 deletions src/apsg/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,16 @@
pivot="mid",
units="dots",
),
stereonet_default_tensor_kwargs=dict(
planes=True,
alpha=None,
color=None,
ls="-",
lw=1.5,
marker="o",
mew=1,
ms=9,
),
stereonet_default_contour_kwargs=dict(
alpha=None,
antialiased=True,
Expand Down
2 changes: 1 addition & 1 deletion src/apsg/database/_alchemy.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
from datetime import datetime
import contextlib

from apsg.feature._geodata import Lineation, Foliation, Pair, Fault
from apsg.feature._geodata import Lineation, Foliation, Pair
from apsg.feature._container import LineationSet, FoliationSet

from sqlalchemy import create_engine, event
Expand Down
3 changes: 2 additions & 1 deletion src/apsg/decorator/_decorator.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ def wrapper(self, *args, **kwargs):
return method(self, *nargs, **kwargs)
else:
raise TypeError(
f'Unsupported arguments for {method.__name__}. Must be {" or ".join([dt.__name__ for dt in datatypes])}'
f'Unsupported arguments for {method.__name__}. \
Must be {" or ".join([dt.__name__ for dt in datatypes])}'
)

return wrapper
Expand Down
1 change: 1 addition & 0 deletions src/apsg/feature/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"Stress2",
"Ellipse",
"OrientationTensor2",
"Vector2Set",
"FeatureSet",
"Vector3Set",
"LineationSet",
Expand Down
28 changes: 15 additions & 13 deletions src/apsg/feature/_container.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from scipy.cluster.hierarchy import linkage, fcluster, dendrogram

from apsg.config import apsg_conf
from apsg.helpers._math import sind, cosd, atan2d
from apsg.math._vector import Vector2, Vector3
from apsg.helpers._math import acosd
from apsg.feature._geodata import Lineation, Foliation, Pair, Fault, Cone
Expand Down Expand Up @@ -75,7 +74,7 @@ def __getitem__(self, key):
return type(self)([self.data[ix] for ix in idxs])
else:
raise TypeError(
"Wrong index. Only slice, int and numpy array for fancy indexing are allowed."
"Wrong index. Only slice, int and np.array are allowed for indexing."
)

def __iter__(self):
Expand Down Expand Up @@ -103,9 +102,9 @@ def bootstrap(self, n=100, size=None):
>>> l = Vector3Set.random_fisher(n=100, position=lin(120,40))
>>> sm = [lb.R() for lb in l.bootstrap()]
>>> l.fisher_statistics()
{'k': 19.912361106049794, 'a95': 3.2490273703993973, 'csd': 18.151964734256303}
{'k': 19.91236110604979, 'a95': 3.249027370399397, 'csd': 18.15196473425630}
>>> Vector3Set(sm).fisher_statistics()
{'k': 1735.3602067018592, 'a95': 0.33932243564473413, 'csd': 1.9444205467798013}
{'k': 1735.360206701859, 'a95': 0.3393224356447341, 'csd': 1.944420546779801}
"""
if size is None:
size = len(self)
Expand Down Expand Up @@ -198,7 +197,7 @@ def transform(self, F, **kwargs):
"""Return affine transformation of all features ``FeatureSet`` by matrix 'F'.
Args:
F: Transformation matrix. Should be array-like value e.g. ``DeformationGradient3``
F: Transformation matrix. Array-like value e.g. ``DeformationGradient3``
Keyword Args:
norm: normalize transformed features. True or False. Default False
Expand Down Expand Up @@ -333,7 +332,8 @@ def from_xy(cls, x, y, name="Default"):

@classmethod
def random(cls, n=100, name="Default"):
"""Method to create ``FeatureSet`` of features with uniformly distributed random orientation.
"""Method to create ``FeatureSet`` of features with uniformly distributed
random orientation.
Keyword Args:
n: number of objects to be generated
Expand Down Expand Up @@ -473,7 +473,7 @@ def transform(self, F, **kwargs):
"""Return affine transformation of all features ``FeatureSet`` by matrix 'F'.
Args:
F: Transformation matrix. Should be array-like value e.g. ``DeformationGradient3``
F: Transformation matrix. Array-like value e.g. ``DeformationGradient3``
Keyword Args:
norm: normalize transformed features. True or False. Default False
Expand Down Expand Up @@ -745,8 +745,8 @@ def random_normal(cls, n=100, position=Vector3(0, 0, 1), sigma=20, name="Default

@classmethod
def random_fisher(cls, n=100, position=Vector3(0, 0, 1), kappa=20, name="Default"):
"""Return ``FeatureSet`` of random vectors sampled from von Mises Fisher distribution
around center position with concentration kappa.
"""Return ``FeatureSet`` of random vectors sampled from von Mises Fisher
distribution around center position with concentration kappa.
Args:
n: number of objects to be generated
Expand All @@ -766,8 +766,8 @@ def random_fisher2(cls, n=100, position=Vector3(0, 0, 1), kappa=20, name="Defaul
"""Method to create ``FeatureSet`` of vectors distributed according to
Fisher distribution.
Note: For proper von Mises Fisher distrinbution implementation use ``random.fisher``
method.
Note: For proper von Mises Fisher distrinbution implementation use
``random.fisher`` method.
Args:
n: number of objects to be generated
Expand Down Expand Up @@ -1527,14 +1527,16 @@ def aMAD(self) -> np.ndarray:
@property
def MAD_l(self) -> np.ndarray:
"""
Return maximum angular deviation (MAD) of linearly distributed vectors. Kirschvink 1980
Return maximum angular deviation (MAD) of linearly distributed vectors.
Kirschvink 1980
"""
return np.array([e.MAD_l for e in self])

@property
def MAD_p(self) -> np.ndarray:
"""
Return maximum angular deviation (MAD) of planarly distributed vectors. Kirschvink 1980
Return maximum angular deviation (MAD) of planarly distributed vectors.
Kirschvink 1980
"""
return np.array([e.MAD_p for e in self])

Expand Down
1 change: 0 additions & 1 deletion src/apsg/feature/_paleomag.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@

import numpy as np

from apsg.config import apsg_conf
from apsg.helpers._helper import eformat
from apsg.math._vector import Vector3
from apsg.feature._geodata import Lineation, Foliation, Pair
Expand Down
24 changes: 14 additions & 10 deletions src/apsg/feature/_tensor3.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ class DeformationGradient3(Matrix3):

@classmethod
def from_ratios(cls, Rxy=1, Ryz=1):
"""Return isochoric ``DeformationGradient3`` tensor with axial stretches defined by strain ratios.
Default is identity tensor.
"""Return isochoric ``DeformationGradient3`` tensor with axial stretches
defined by strain ratios. Default is identity tensor.
Keyword Args:
Rxy (float): XY strain ratio
Expand Down Expand Up @@ -122,8 +122,8 @@ def from_vectors_axis(cls, v1, v2, a):
Return ``DeformationGradient3`` representing rotation of vector v1 to v2 around
axis a.
If v1.angle(a) is not equal to v2.angle(b), the minimum adjustment of rotation axis
is done automatically.
If v1.angle(a) is not equal to v2.angle(b), the minimum adjustment of rotation
axis is done automatically.
Args:
v1: ``Vector3`` like object
Expand Down Expand Up @@ -200,13 +200,15 @@ def R(self):

@property
def U(self):
"""Return stretching part of ``DeformationGradient3`` from right polar decomposition."""
"""Return stretching part of ``DeformationGradient3`` from right polar
decomposition."""
_, U = spla.polar(self, "right")
return type(self)(U)

@property
def V(self):
"""Return stretching part of ``DeformationGradient3`` from left polar decomposition."""
"""Return stretching part of ``DeformationGradient3`` from left polar
decomposition."""
_, V = spla.polar(self, "left")
return type(self)(V)

Expand All @@ -231,7 +233,8 @@ def axisangle(self):

def velgrad(self, time=1):
"""
Return ``VelocityGradient3`` calculated as matrix logarithm divided by given time.
Return ``VelocityGradient3`` calculated as matrix logarithm divided by given
time.
Keyword Args:
time (float): total time. Default 1
Expand Down Expand Up @@ -498,8 +501,8 @@ def I3(self):
@property
def diagonalized(self):
"""
Returns diagonalized Stress tensor and orthogonal matrix R, which transforms actual
coordinate system to the principal one.
Returns diagonalized Stress tensor and orthogonal matrix R, which transforms
actual coordinate system to the principal one.
"""
return (
Expand Down Expand Up @@ -599,7 +602,8 @@ class Ellipsoid(Tensor3):
"""

def __repr__(self) -> str:
return f"{Matrix3.__repr__(self)}\n(S1:{self.S1:.3g}, S2:{self.S2:.3g}, S3:{self.S3:.3g})"
return f"{Matrix3.__repr__(self)}\n\
(S1:{self.S1:.3g}, S2:{self.S2:.3g}, S3:{self.S3:.3g})"

@classmethod
def from_defgrad(cls, F, form="left", **kwargs) -> "Ellipsoid":
Expand Down
6 changes: 4 additions & 2 deletions src/apsg/math/_matrix.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,8 @@ def __init__(self, *args):

@classmethod
def from_comp(cls, xx=1, xy=0, yx=0, yy=1):
"""Return ``Matrix2`` defined by individual components. Default is identity tensor.
"""Return ``Matrix2`` defined by individual components. Default is identity
tensor.
Keyword Args:
xx (float): tensor component M_xx
Expand Down Expand Up @@ -310,7 +311,8 @@ def __init__(self, *args):

@classmethod
def from_comp(cls, xx=1, xy=0, xz=0, yx=0, yy=1, yz=0, zx=0, zy=0, zz=1):
"""Return ``Matrix3`` defined by individual components. Default is identity tensor.
"""Return ``Matrix3`` defined by individual components. Default is identity
tensor.
Keyword Args:
xx (float): tensor component M_xx
Expand Down
4 changes: 3 additions & 1 deletion src/apsg/math/_vector.py
Original file line number Diff line number Diff line change
Expand Up @@ -508,7 +508,9 @@ def random(cls):

@ensure_first_arg_same
def rotate(self, axis, theta):
"""Return the vector rotated around axis through angle theta. Right hand rule applies"""
"""Return the vector rotated around axis through angle theta. Right-hand rule
applies
"""
v = Vector3(self) # ensure vector
k = Vector3(axis.uv())
return type(self)(
Expand Down
23 changes: 0 additions & 23 deletions src/apsg/pandas/_pandas_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,6 @@
from apsg.feature import FoliationSet, LineationSet, Vector3Set, FaultSet
from apsg.plotting import StereoNet

"""
import pandas as pd
from apsg import *
from apsg.pandas import *
df = pd.DataFrame({
'name':['A', 'B', 'C'],
'azi':[120, 140, 135],
'inc':[30, 28, 42]
})
df.apsg.create_fols().fol.R()
df = pd.DataFrame({
'name':['A', 'B', 'C'],
'fazi':[120, 140, 135],
'finc':[30, 28, 42],
'lazi':[170, 120, 95],
'linc':[20, 26, 22]
})
df.apsg.create_fols(columns=['fazi', 'finc']).apsg.create_lins(columns=['lazi', 'linc'])
"""


@pd.api.extensions.register_extension_dtype
class Vec3Dtype(PandasExtensionDtype):
Expand Down
23 changes: 22 additions & 1 deletion src/apsg/plotting/_plot_artists.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
from apsg.config import apsg_conf
from apsg.math._vector import Vector3
from apsg.feature._geodata import Foliation, Pair, Fault, Cone
from apsg.feature._tensor3 import Ellipsoid
from apsg.feature._tensor3 import Tensor3, Ellipsoid
from apsg.feature._container import (
Vector3Set,
Vector2Set,
Expand Down Expand Up @@ -270,6 +270,20 @@ def parse_kwargs(self, kwargs):
)


class StereoNet_Tensor(StereoNet_Artists):
def __init__(self, factory, *args, **kwargs):
super().__init__(factory, *args, **kwargs)
self.stereonet_method = "_tensor"
self.args = args[:1] # take max 1 args
self.parse_kwargs(kwargs)

def parse_kwargs(self, kwargs):
super().update_kwargs("stereonet_default_tensor_kwargs")
self.kwargs.update((k, kwargs[k]) for k in self.kwargs.keys() & kwargs.keys())
if not isinstance(self.kwargs["label"], str):
self.kwargs["label"] = self.args[0].label()


class StereoNet_Contour(StereoNet_Artists):
def __init__(self, factory, *args, **kwargs):
super().__init__(factory, *args, **kwargs)
Expand Down Expand Up @@ -377,6 +391,13 @@ def create_arrow(*args, **kwargs):
else:
raise TypeError("Not valid arguments for Stereonet arrow")

@staticmethod
def create_tensor(*args, **kwargs):
if all([issubclass(type(arg), Tensor3) for arg in args[:1]]):
return StereoNet_Tensor("create_tensor", *args, **kwargs)
else:
raise TypeError("Not valid arguments for Stereonet arrow")

@staticmethod
def create_contour(*args, **kwargs):
if len(args) == 0:
Expand Down
Loading

0 comments on commit 9019bad

Please sign in to comment.