Commit 64f4270c authored by Daniel Böckenhoff (Laptop)'s avatar Daniel Böckenhoff (Laptop)
Browse files

mesh3D runs; triangles mostly

parent f32720cb
......@@ -6,10 +6,10 @@ from .lib import *
# __all__ = ['core', 'points3D']
from .core import Tensors, TensorFields, TensorMaps
from .points3D import Points3D
from .mask import getMask
from .mask import evalf
# methods:
from .mask import getMask # NOQA
from .mask import evalf # NOQA
from .lib import * # NOQA
# classes:
......
......@@ -692,7 +692,7 @@ class Tensors(AbstractNdarray):
if condition is None:
condition = np.array([True for i in range(len(self))])
elif isinstance(condition, sympy.Basic):
condition = self.getMask(condition)
condition = self.evalf(condition)
if isinstance(coordinate, list) or isinstance(coordinate, tuple):
for c in coordinate:
self.mirror(c, condition)
......@@ -803,7 +803,7 @@ class Tensors(AbstractNdarray):
raise ValueError("Multiple occurences of value {}"
.format(tensor))
def getMoment(self, moment):
def moments(self, moment):
"""
Returns:
Moments of the distribution.
......@@ -813,9 +813,9 @@ class Tensors(AbstractNdarray):
Args:
moment (int): n-th moment
"""
return tfields.lib.stats.getMoment(self, moment)
return tfields.lib.stats.moments(self, moment)
def closestPoints(self, other, **kwargs):
def closest(self, other, **kwargs):
"""
Args:
other (Tensors): closest points to what? -> other
......@@ -827,26 +827,27 @@ class Tensors(AbstractNdarray):
>>> m = tfields.Tensors([[1,0,0], [0,1,0], [1,1,0], [0,0,1],
... [1,0,1]])
>>> p = tfields.Tensors([[1.1,1,0], [0,0.1,1], [1,0,1.1]])
>>> p.closestPoints(m)
>>> p.closest(m)
array([2, 3, 4])
"""
with other.tmp_transform(self.coordSys):
# balanced_tree option gives huge speedup!
kdTree = sp.spatial.cKDTree(other, 1000,
balanced_tree=False)
res = kdTree.query(self, **kwargs)
kd_tree = sp.spatial.cKDTree(other, 1000,
balanced_tree=False)
res = kd_tree.query(self, **kwargs)
array = res[1]
return array
def getMask(self, cutExpression=None, coordSys=None):
def evalf(self, expression=None, coordSys=None):
"""
Args:
cutExpression (sympy logical expression)
coordSys (str): coordSys to evaluate the expression in.
Returns: np.array of dtype bool with lenght of number of points in self.
This array is True, where cutExpression evaluates True.
expression (sympy logical expression)
coordSys (str): coordSys to evalfuate the expression in.
Returns:
np.ndarray: mask of dtype bool with lenght of number of points in self.
This array is True, where expression evalfuates True.
Examples:
>>> import tfields
>>> import numpy
......@@ -854,33 +855,33 @@ class Tensors(AbstractNdarray):
>>> x, y, z = sympy.symbols('x y z')
>>> p = tfields.Points3D([[1., 2., 3.], [4., 5., 6.], [1, 2, -6],
... [-5, -5, -5], [1,0,-1], [0,1,-1]])
>>> np.array_equal(p.getMask(x > 0),
>>> np.array_equal(p.evalf(x > 0),
... [True, True, True, False, True, False])
True
And combination
>>> np.array_equal(p.getMask((x > 0) & (y < 3)),
>>> np.array_equal(p.evalf((x > 0) & (y < 3)),
... [True, False, True, False, True, False])
True
Or combination
>>> np.array_equal(p.getMask((x > 0) | (y > 3)),
>>> np.array_equal(p.evalf((x > 0) | (y > 3)),
... [True, True, True, False, True, False])
True
"""
coords = sympy.symbols('x y z')
with self.tmp_transform(coordSys or self.coordSys):
mask = tfields.getMask(self, cutExpression, coords=coords)
mask = tfields.evalf(self, expression, coords=coords)
return mask
def cut(self, cutExpression, coordSys=None):
def cut(self, expression, coordSys=None):
"""
Default cut method for Points3D. Works on a copy.
Args:
cutExpression (sympy logical expression): logical expression which will be evaluated.
expression (sympy logical expression): logical expression which will be evalfuated.
use symbols x, y and z
coordSys (str): coordSys to evaluate the expression in.
coordSys (str): coordSys to evalfuate the expression in.
Examples:
>>> import tfields
>>> import sympy
......@@ -903,7 +904,7 @@ class Tensors(AbstractNdarray):
"""
if len(self) == 0:
return self.copy()
mask = self.getMask(cutExpression, coordSys=coordSys or self.coordSys)
mask = self.evalf(expression, coordSys=coordSys or self.coordSys)
mask.astype(bool)
inst = self[mask].copy()
return inst
......@@ -933,15 +934,18 @@ class Tensors(AbstractNdarray):
other.transform(self.coordSys)
return sp.spatial.distance.cdist(self, other, **kwargs)
def minDistances(self, other=None, **kwargs):
def min_dists(self, other=None, **kwargs):
"""
Args:
other(array or None)
other(array | None): if None: closest distance to self
**kwargs:
memory_saving (bool): for very large array comparisons
default False
... rest is forwarded to sp.spatial.distance.cdist
Returns:
np.array: minimal distances of self to other
Examples:
>>> import tfields
......@@ -950,13 +954,13 @@ class Tensors(AbstractNdarray):
... (0, 2, 3),
... (0, 0, 1))
>>> p[4,2] = 1
>>> dMin = p.minDistances()
>>> dMin = p.min_dists()
>>> expected = [1] * 9
>>> expected[4] = np.sqrt(2)
>>> np.array_equal(dMin, expected)
True
>>> dMin2 = p.minDistances(memory_saving=True)
>>> dMin2 = p.min_dists(memory_saving=True)
>>> bool((dMin2 == dMin).all())
True
......@@ -1001,6 +1005,60 @@ class Tensors(AbstractNdarray):
distsInEpsilon = dists <= epsilon
return [indices[die] for die in distsInEpsilon]
def _weights(self, weights, rigid=True):
"""
transformer method for weights inputs.
Args:
weights (np.ndarray | None):
If weights is None, use np.ones
Otherwise just pass the weights.
rigid (bool): demand equal weights and tensor length
Returns:
weight array
"""
# set weights to 1.0 if weights is None
if weights is None:
weights = np.ones(len(self))
if rigid:
if not len(weights) == len(self):
raise ValueError("Equal number of weights as tensors demanded.")
return weights
def cov_eig(self, weights=None):
"""
Calculate the covariance eigenvectors with lenghts of eigenvalues
Args:
weights (np.array | int | None): index to scalars to weight with
"""
# weights = self.getNormedWeightedAreas(weights=weights)
weights = self._weights(weights)
cov = np.cov(self.T,
ddof=0,
aweights=weights)
# calculate eigenvalues and eigenvectors of covariance
evalfs, evecs = np.linalg.eigh(cov)
idx = evalfs.argsort()[::-1]
evalfs = evalfs[idx]
evecs = evecs[:, idx]
e = np.concatenate((evecs, evalfs.reshape(1, 3)))
return e.T.reshape(12, )
def main_axes(self, weights=None):
"""
Returns:
Main Axes eigen-vectors
"""
# weights = self.getNormedWeightedAreas(weights=weights)
weights = self._weights(weights)
mean = self.moments(1)
relative_coords = self - mean
cov = np.cov(relative_coords.T,
ddof=0,
aweights=weights)
# calculate eigenvalues and eigenvectors of covariance
evalfs, evecs = np.linalg.eigh(cov)
return (evecs * evalfs.T).T
class TensorFields(Tensors):
"""
......@@ -1068,7 +1126,7 @@ class TensorFields(Tensors):
This error can be suppressed by setting rigid=False
>>> loose = tfields.TensorFields([1, 2, 3], [3], rigid=False)
>>> assert len(loose) != 1
"""
__slots__ = ['coordSys', 'fields']
......@@ -1192,6 +1250,18 @@ class TensorFields(Tensors):
mask &= field.equal(other.fields[i], **kwargs)
return mask
def _weights(self, weights, rigid=True):
"""
Expansion of Tensors._weights with integer inputs
Args:
weights (np.ndarray | int | None):
if weights is int: use field at index <weights>
else: see Tensors._weights
"""
if isinstance(weights, int) :
weights = self.fields[weights]
return super(TensorFields, self)._weights(weights, rigid=rigid)
class TensorMaps(TensorFields):
"""
......@@ -1431,7 +1501,7 @@ class TensorMaps(TensorFields):
>>> m = TensorMaps([[1,2,3], [3,3,3], [0,0,0], [5,6,7]],
... maps=[[[0, 1, 2], [1, 2, 3]]])
>>> from sympy.abc import x,y,z
>>> vertexMask = m.getMask(z < 6)
>>> vertexMask = m.evalf(z < 6)
>>> faceMask = m.to_maps_masks(vertexMask)
>>> assert np.array_equal(faceMask, [[True, False]])
......
......@@ -11,15 +11,16 @@ import numpy as np
import sympy
def getMask(array, cutExpression=None, coords=None):
def evalf(array, cutExpression=None, coords=None):
"""
Linking sympy and numpy by retrieving a mask according to the cutExpression
Args:
array (numpy ndarray)
cutExpression (sympy logical expression)
coordSys (str): coordSys to evaluate the expression in.
Returns: np.array which is True, where cutExpression evaluates True.
coordSys (str): coordSys to evalfuate the expression in.
Returns:
np.array: mask which is True, where cutExpression evalfuates True.
Examples:
>>> import sympy
>>> import numpy as np
......@@ -27,20 +28,20 @@ def getMask(array, cutExpression=None, coords=None):
>>> x, y, z = sympy.symbols('x y z')
>>> a = np.array([[1., 2., 3.], [4., 5., 6.], [1, 2, -6], [-5, -5, -5], [1,0,-1], [0,1,-1]])
>>> assert np.array_equal(tfields.getMask(a, x > 0),
>>> assert np.array_equal(tfields.evalf(a, x > 0),
... np.array([ True, True, True, False, True, False]))
And combination
>>> assert np.array_equal(tfields.getMask(a, (x > 0) & (y < 3)),
>>> assert np.array_equal(tfields.evalf(a, (x > 0) & (y < 3)),
... np.array([True, False, True, False, True, False]))
Or combination
>>> assert np.array_equal(tfields.getMask(a, (x > 0) | (y > 3)),
>>> assert np.array_equal(tfields.evalf(a, (x > 0) | (y > 3)),
... np.array([True, True, True, False, True, False]))
If array of other shape than (?, 3) is given, the coords need to be specified
>>> a0, a1 = sympy.symbols('a0 a1')
>>> assert np.array_equal(tfields.getMask([[0., 1.], [-1, 3]], a1 > 2,
>>> assert np.array_equal(tfields.evalf([[0., 1.], [-1, 3]], a1 > 2,
... coords=[a0, a1]),
... np.array([False, True], dtype=bool))
......
......@@ -36,7 +36,7 @@ def fields_to_scalars(fields):
return np.array(fields)
def faces_to_maps(faces, *fields):
return [tfields.TensorFields(faces, *fields, dtype=int)]
return [tfields.TensorFields(faces, *fields, dtype=int, dim=3)]
def maps_to_faces(maps):
if len(maps) == 0:
......@@ -74,10 +74,9 @@ class Mesh3D(tfields.TensorMaps):
going from Mesh3D to Triangles3D instance is easy and will be cached.
>>> m = tfields.Mesh3D([[1,0,0], [0,1,0], [0,0,0]], faces=[[0, 1, 2]]);
>>> m.triangles
Triangles3D([[ 1., 0., 0.],
[ 0., 1., 0.],
[ 0., 0., 0.]])
>>> assert m.triangles.equal(tfields.Triangles3D([[ 1., 0., 0.],
... [ 0., 1., 0.],
... [ 0., 0., 0.]]))
a list of scalars is assigned to each face
>>> mScalar = tfields.Mesh3D([[1,0,0], [0,1,0], [0,0,0]], faces=[[0, 1, 2]], faceScalars=[.5]);
......@@ -95,9 +94,7 @@ class Mesh3D(tfields.TensorMaps):
... [ 2., 0., 0.],
... [ 0., 3., 0.]])
True
>>> msum.faces
array([[0, 1, 2],
[3, 4, 5]])
>>> assert np.array_equal(msum.faces, [[0, 1, 2], [3, 4, 5]])
Saving and reading
>>> from tempfile import NamedTemporaryFile
......@@ -112,9 +109,9 @@ class Mesh3D(tfields.TensorMaps):
"""
def __new__(cls, tensors, **kwargs):
fields = kwargs.pop('fields', [])
if not issubclass(type(tensors), Mesh3D):
kwargs['dim'] = 3
fields = kwargs.pop('fields', [])
faces = kwargs.pop('faces', None)
faceScalars = kwargs.pop('faceScalars', [])
maps = kwargs.pop('maps', None)
......@@ -288,7 +285,10 @@ class Mesh3D(tfields.TensorMaps):
"""
if self.faces.size == 0:
return tfields.Triangles3D([])
return tfields.Triangles3D(self[self.faces.flatten()], triangleScalars=self.faceScalars)
tris = tfields.Tensors.merged(*[self[mp.flatten()] for mp in self.maps])
map_fields = [mp.fields for mp in self.maps]
fields = [tfields.Tensors.merged(*fields) for fields in zip(*map_fields)]
return tfields.Triangles3D(tris, *fields)
@decoTools.cached_property()
def planes(self):
......@@ -313,22 +313,22 @@ class Mesh3D(tfields.TensorMaps):
def createFromTxtFile(cls, filePath):
return tfields.Triangles3D.createFromTxtFile(filePath).getMesh3D()
def __getattr__(self, name):
"""
getter methods are forwarded to self.triangles
Examples:
>>> m = tfields.Mesh3D([]);
>>> m.getAreas
<bound method Triangles3D.getAreas of Triangles3D([], shape=(0, 3), dtype=float64)>
"""
if name.startswith('get'):
if not hasattr(tfields.Triangles3D, name) or name == 'getMask':
raise AttributeError("Could not forward attribute {0}".format(name))
else:
return getattr(self.triangles, name)
else:
raise AttributeError("No attribute with name {0}".format(name))
# def __getattr__(self, name):
# """
# getter methods are forwarded to self.triangles
# Examples:
# >>> m = tfields.Mesh3D([]);
# >>> m.getAreas
# <bound method Triangles3D.getAreas of Triangles3D([], shape=(0, 3), dtype=float64)>
# """
# if name.startswith('get'):
# if not hasattr(tfields.Triangles3D, name) or name == 'getMask':
# raise AttributeError("Could not forward attribute {0}".format(name))
# else:
# return getattr(self.triangles, name)
# else:
# raise AttributeError("No attribute with name {0}".format(name))
def getNFaces(self):
return self.faces.shape[0]
......
This diff is collapsed.
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment