Commit 4f46ffdf authored by dboe's avatar dboe
Browse files

merged returns template and can be used with cut

parent 22e1d81a
import tfields
import numpy as np import numpy as np
import unittest
from tempfile import NamedTemporaryFile from tempfile import NamedTemporaryFile
import pickle import pickle
import unittest
import tfields
ATOL = 1e-8 ATOL = 1e-8
...@@ -39,16 +39,15 @@ class Base_Check(object): ...@@ -39,16 +39,15 @@ class Base_Check(object):
def test_basic_merge(self): def test_basic_merge(self):
# create 3 copies with different coord_sys # create 3 copies with different coord_sys
from IPython import embed; embed()
merge_list = [self._inst.copy() for i in range(3)] merge_list = [self._inst.copy() for i in range(3)]
merge_list[0].transform(tfields.bases.CARTESIAN) merge_list[0].transform(tfields.bases.CARTESIAN)
merge_list[1].transform(tfields.bases.CYLINDER) merge_list[1].transform(tfields.bases.CYLINDER)
merge_list[2].transform(tfields.bases.SPHERICAL) merge_list[2].transform(tfields.bases.SPHERICAL)
# merge them and check that the first coord_sys is taken # merge them and check that the first coord_sys is taken
obj = type(self._inst).merged(*merge_list) obj = type(self._inst).merged(*merge_list)
self.assertTrue(obj.coord_sys == tfields.bases.CARTESIAN) self.assertTrue(obj.coord_sys == tfields.bases.CARTESIAN)
# check that all copies are the same also with new coord_sys # check that all copies are the same also with new coord_sys
for i in range(len(merge_list)): for i in range(len(merge_list)):
value = np.allclose(merge_list[0], value = np.allclose(merge_list[0],
...@@ -114,7 +113,7 @@ class TensorMaps_Empty_Test(TensorFields_Empty_Test): ...@@ -114,7 +113,7 @@ class TensorMaps_Empty_Test(TensorFields_Empty_Test):
self._inst = tfields.TensorMaps([], dim=3) self._inst = tfields.TensorMaps([], dim=3)
self._maps = [] self._maps = []
self._maps_fields = [] self._maps_fields = []
class TensorFields_Copy_Test(TensorFields_Empty_Test): class TensorFields_Copy_Test(TensorFields_Empty_Test):
def setUp(self): def setUp(self):
......
...@@ -655,12 +655,19 @@ class Tensors(AbstractNdarray): ...@@ -655,12 +655,19 @@ class Tensors(AbstractNdarray):
Factory method Factory method
Merges all tensor inputs to one tensor Merges all tensor inputs to one tensor
Args:
**kwargs: passed to cls
dim (int):
return_templates (bool): return the templates which can be used
together with cut to retrieve the original objects
Examples: Examples:
>>> import numpy as np >>> import numpy as np
>>> import tfields >>> import tfields
>>> import tfields.bases >>> import tfields.bases
Use of most frequent coordinate system The new object with turn out in the most frequent coordinate
system if not specified explicitly
>>> vec_a = tfields.Tensors([[0, 0, 0], [0, 0, 1], [0, -1, 0]]) >>> vec_a = tfields.Tensors([[0, 0, 0], [0, 0, 1], [0, -1, 0]])
>>> vec_b = tfields.Tensors([[5, 4, 1]], coord_sys=tfields.bases.cylinder) >>> vec_b = tfields.Tensors([[5, 4, 1]], coord_sys=tfields.bases.cylinder)
...@@ -686,18 +693,29 @@ class Tensors(AbstractNdarray): ...@@ -686,18 +693,29 @@ class Tensors(AbstractNdarray):
... len(merge) + 3, ... len(merge) + 3,
... 1))]) ... 1))])
>>> obj_list = [tfields.Tensors([[1, 2, 3]], coord_sys=tfields.bases.CYLINDER), >>> obj_list = [tfields.Tensors([[1, 2, 3]],
... coord_sys=tfields.bases.CYLINDER),
... tfields.Tensors([[3] * 3]), ... tfields.Tensors([[3] * 3]),
... tfields.Tensors([[5, 1, 3]])] ... tfields.Tensors([[5, 1, 3]])]
>>> merge2 = tfields.Tensors.merged(*obj_list, coord_sys=tfields.bases.CARTESIAN) >>> merge2 = tfields.Tensors.merged(
... *obj_list, coord_sys=tfields.bases.CARTESIAN)
>>> assert merge2.equal([[-0.41614684, 0.90929743, 3.], >>> assert merge2.equal([[-0.41614684, 0.90929743, 3.],
... [3, 3, 3], [5, 1, 3]], atol=1e-8) ... [3, 3, 3], [5, 1, 3]], atol=1e-8)
The return_templates argument allows to retrieve a template which
can be used with the cut method.
>>> merge, templates = tfields.Tensors.merged(
... vec_a, vec_b, vec_c, return_templates=True)
>>> assert merge.cut(templates[0]).equal(vec_a)
>>> assert merge.cut(templates[1]).equal(vec_b)
>>> assert merge.cut(templates[2]).equal(vec_c)
""" """
""" get most frequent coord_sys or predefined coord_sys """ """ get most frequent coord_sys or predefined coord_sys """
coord_sys = kwargs.get("coord_sys", None) coord_sys = kwargs.get("coord_sys", None)
dimension = kwargs.get("dim", None) return_templates = kwargs.pop("return_templates", False)
if coord_sys is None: if coord_sys is None:
bases = [] bases = []
for t in objects: for t in objects:
...@@ -735,11 +753,26 @@ class Tensors(AbstractNdarray): ...@@ -735,11 +753,26 @@ class Tensors(AbstractNdarray):
for i, obj in enumerate(remainingObjects): for i, obj in enumerate(remainingObjects):
tensors = np.append(tensors, obj, axis=0) tensors = np.append(tensors, obj, axis=0)
if len(tensors) == 0 and dimension is None: if len(tensors) == 0 and not 'dim' in kwargs:
# if you can not determine the tensor dimension, search for the
# first object with some entries
for obj in objects: for obj in objects:
kwargs["dim"] = dim(obj) if len(obj) != 0:
kwargs['dim'] = dim(obj)
break
return cls.__new__(cls, tensors, **kwargs) if not return_templates:
return cls.__new__(cls, tensors, **kwargs)
else:
tensor_lengths = [len(o) for o in objects]
cum_tensor_lengths = [sum(tensor_lengths[:i])
for i in range(len(objects))]
templates = [
tfields.TensorFields(
obj,
np.arange(tensor_lengths[i]) + cum_tensor_lengths[i])
for i, obj in enumerate(objects)]
return cls.__new__(cls, tensors, **kwargs), templates
@classmethod @classmethod
def grid(cls, *base_vectors, **kwargs): def grid(cls, *base_vectors, **kwargs):
...@@ -1241,14 +1274,36 @@ class Tensors(AbstractNdarray): ...@@ -1241,14 +1274,36 @@ class Tensors(AbstractNdarray):
mask = tfields.evalf(np.array(self), expression, coords=coords) mask = tfields.evalf(np.array(self), expression, coords=coords)
return mask return mask
def cut(self, expression, coord_sys=None): def _cut_sympy(self, expression):
if len(self) == 0:
return self.copy()
mask = self.evalf(expression) # coord_sys is handled by tmp_transform
mask.astype(bool)
inst = self[mask].copy()
# template
indices = np.arange(len(self))[mask]
# TODO: maybe np.empty instead of inst below. Reason why i copy is
# that the template is such marked for where it comes from.
# We have a trade-off with small memory consumpiton here.
template = tfields.TensorFields(inst, indices)
return inst, template
def _cut_template(self, template):
return self[template.fields[0]]
def cut(self, expression, coord_sys=None, return_template=False, **kwargs):
""" """
Default cut method for Points3D. Works on a copy. Extract a part of the object according to the logic given
by <expression>.
Args: Args:
expression (sympy logical expression): logical expression which will be evalfuated. expression (sympy logical expression|tfields.TensorFields): logical
use symbols x, y and z expression which will be evaluated. use symbols x, y and z.
coord_sys (str): coord_sys to evalfuate the expression in. If tfields.TensorFields or subclass is given, the expression
refers to a template.
coord_sys (str): coord_sys to evaluate the expression in. Only
active for template expression
Examples: Examples:
>>> import tfields >>> import tfields
...@@ -1264,19 +1319,44 @@ class Tensors(AbstractNdarray): ...@@ -1264,19 +1319,44 @@ class Tensors(AbstractNdarray):
combinations of cuts combinations of cuts
>>> p.cut((x > 0) & (z < 0)).equal([[1, 2, -6], [1, 0, -1]]) >>> cut_expression = (x > 0) & (z < 0)
>>> combi_cut = p.cut(cut_expression)
>>> combi_cut.equal([[1, 2, -6], [1, 0, -1]])
True
Templates can be used to speed up the repeated cuts on the same
underlying tensor with the same expression but new fields.
First let us cut a but request the template on return:
>>> field1 = list(range(len(p)))
>>> tf = tfields.TensorFields(p, field1)
>>> tf_cut, template = tf.cut(cut_expression,
... return_template=True)
Now repeat the cut with a new field:
>>> field2 = p
>>> tf.fields.append(field2)
>>> tf_template_cut = tf.cut(template)
>>> tf_template_cut.equal(combi_cut)
True
>>> tf_template_cut.fields[0].equal([2, 4])
True
>>> tf_template_cut.fields[1].equal(combi_cut)
True True
Returns: Returns:
copy of self with cut applied copy of self with cut applied
[optional: template - requires <return_template> switch]
""" """
if len(self) == 0: with self.tmp_transform(coord_sys or self.coord_sys):
return self.copy() if issubclass(type(expression), TensorFields):
mask = self.evalf(expression, coord_sys=coord_sys or self.coord_sys) template = expression
mask.astype(bool) obj = self._cut_template(template)
inst = self[mask].copy() else:
return inst obj, template = self._cut_sympy(expression, **kwargs)
if return_template:
return obj, template
return obj
def distances(self, other, **kwargs): def distances(self, other, **kwargs):
""" """
...@@ -1676,7 +1756,13 @@ class TensorFields(Tensors): ...@@ -1676,7 +1756,13 @@ class TensorFields(Tensors):
"Got objects of types {types} instead.".format(**locals()) "Got objects of types {types} instead.".format(**locals())
) )
inst = super(TensorFields, cls).merged(*objects, **kwargs) return_value = super(TensorFields, cls).merged(*objects, **kwargs)
return_templates = kwargs.get('return_templates', False)
if return_templates:
inst, templates = return_value
else:
inst, templates = (return_value, None)
fields = [] fields = []
if all([len(obj.fields) == len(objects[0].fields) for obj in objects]): if all([len(obj.fields) == len(objects[0].fields) for obj in objects]):
...@@ -1686,7 +1772,10 @@ class TensorFields(Tensors): ...@@ -1686,7 +1772,10 @@ class TensorFields(Tensors):
) )
fields.append(field) fields.append(field)
inst = cls.__new__(cls, inst, *fields) inst = cls.__new__(cls, inst, *fields)
return inst if return_templates:
return inst, templates
else:
return inst
@property @property
def names(self): def names(self):
...@@ -1830,6 +1919,7 @@ class TensorMaps(TensorFields): ...@@ -1830,6 +1919,7 @@ class TensorMaps(TensorFields):
__slots__ = ['coord_sys', 'name', 'fields', 'maps'] __slots__ = ['coord_sys', 'name', 'fields', 'maps']
def __new__(cls, tensors, *fields, **kwargs): def __new__(cls, tensors, *fields, **kwargs):
# TODO: the type of maps should rather be a dictionary with keys: dim
maps = kwargs.pop("maps", []) maps = kwargs.pop("maps", [])
maps_cp = [] maps_cp = []
for mp in maps: for mp in maps:
...@@ -1946,26 +2036,53 @@ class TensorMaps(TensorFields): ...@@ -1946,26 +2036,53 @@ class TensorMaps(TensorFields):
) )
) )
tensor_lengths = [len(o) for o in objects] tensor_lengths = [len(o) for o in objects]
cum_tensor_lengths = [ cum_tensor_lengths = [sum(tensor_lengths[:i])
sum(tensor_lengths[:i]) for i in range(len(objects)) for i in range(len(objects))]
]
maps = [] return_value = super(TensorMaps, cls).merged(*objects, **kwargs)
dims = [] return_templates = kwargs.get('return_templates', False)
for i, o in enumerate(objects): if return_templates:
for map_field in o.maps: inst, templates = return_value
else:
inst, templates = (return_value, None)
dim_maps_dict = {} # {dim: {(obj_index(i), map_index(j): maps_field}}
for i, obj in enumerate(objects):
for j, map_field in enumerate(obj.maps):
map_field = map_field + cum_tensor_lengths[i] map_field = map_field + cum_tensor_lengths[i]
try: if map_field.dim not in dim_maps_dict:
mp_idx = dims.index(map_field.dim) dim_maps_dict[map_field.dim] = {}
except ValueError: dim_maps_dict[map_field.dim][(i, j)] = map_field
maps.append(map_field)
dims.append(map_field.dim) # for i, obj in enumerate(objects):
else: maps = []
maps[mp_idx] = TensorFields.merged(maps[mp_idx], map_field) for dimension in sorted(dim_maps_dict.keys()):
# sort by object index
keys = dim_maps_dict[dimension].keys()
obj_indices = [key[0] for key in keys]
map_indices = [key[1] for key in keys]
dim_maps = [dim_maps_dict[dimension][key] for key in keys]
obj_indices, map_indices, dim_maps = tfields.lib.util.multi_sort(
obj_indices, map_indices, dim_maps)
return_value = TensorFields.merged(
*dim_maps,
return_templates=return_templates,
)
if return_templates:
mp, map_templates = return_value
for i in obj_indices:
j = map_indices[i]
templates[i].maps[j] = map_templates[i]
else:
mp = return_value
maps.append(mp)
inst = super(TensorMaps, cls).merged(*objects, **kwargs)
inst = cls.__new__(cls, inst, maps=maps) inst = cls.__new__(cls, inst, maps=maps)
return inst if 'return_templates' in kwargs:
return inst, templates
else:
return inst
def equal(self, other, **kwargs): def equal(self, other, **kwargs):
""" """
......
...@@ -742,7 +742,7 @@ class Mesh3D(tfields.TensorMaps): ...@@ -742,7 +742,7 @@ class Mesh3D(tfields.TensorMaps):
**kwargs)) **kwargs))
if merge_functions is None: if merge_functions is None:
merge_functions = [lambda x: np.mean(x, axis=0)] * len(fields) merge_functions = [lambda x: np.mean(x, axis=0)] * len(fields)
# build point_face_assignment if not given. # build point_face_assignment if not given.
if point_face_assignment is not None: if point_face_assignment is not None:
if len(point_face_assignment) != len(tensor_field): if len(point_face_assignment) != len(tensor_field):
...@@ -764,7 +764,7 @@ class Mesh3D(tfields.TensorMaps): ...@@ -764,7 +764,7 @@ class Mesh3D(tfields.TensorMaps):
for field, map_field, merge_function in \ for field, map_field, merge_function in \
zip(fields, empty_map_fields, merge_functions): zip(fields, empty_map_fields, merge_functions):
for i, f_index in enumerate(point_face_assignment_set): for i, f_index in enumerate(point_face_assignment_set):
# if i % 1000 == 0: # if i % 1000 == 0:
# print(i, len(point_face_assignment_set)) # print(i, len(point_face_assignment_set))
if f_index == -1: if f_index == -1:
# point could not be mapped # point could not be mapped
...@@ -1048,8 +1048,7 @@ class Mesh3D(tfields.TensorMaps): ...@@ -1048,8 +1048,7 @@ class Mesh3D(tfields.TensorMaps):
maps=maps) maps=maps)
return inst return inst
def cut(self, expression, coord_sys=None, at_intersection=None, def cut(self, *args, **kwargs):
return_template=False):
""" """
cut method for Mesh3D. cut method for Mesh3D.
Args: Args:
...@@ -1143,16 +1142,7 @@ class Mesh3D(tfields.TensorMaps): ...@@ -1143,16 +1142,7 @@ class Mesh3D(tfields.TensorMaps):
* optional: template * optional: template
""" """
with self.tmp_transform(coord_sys or self.coord_sys): return super().cut(*args, **kwargs)
if isinstance(expression, Mesh3D):
template = expression
obj = self._cut_template(template)
else:
at_intersection = at_intersection or "remove"
obj, template = self._cut_sympy(expression, at_intersection=at_intersection)
if return_template:
return obj, template
return obj
def disjoint_parts(self, return_template=False): def disjoint_parts(self, return_template=False):
""" """
......
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