Commit 52acd28b authored by dboe's avatar dboe
Browse files

new class Maps integrated partially. bugfix in utils

parent a6a4ab23
...@@ -56,7 +56,8 @@ class Base_Check(object): ...@@ -56,7 +56,8 @@ class Base_Check(object):
atol=ATOL) atol=ATOL)
self.assertTrue(value) self.assertTrue(value)
obj_cs = type(self._inst).merged(*merge_list, coord_sys=tfields.bases.CYLINDER) obj_cs = type(self._inst).merged(*merge_list,
coord_sys=tfields.bases.CYLINDER)
for i in range(len(merge_list)): for i in range(len(merge_list)):
value = np.allclose(merge_list[1], value = np.allclose(merge_list[1],
obj_cs[i * len(self._inst): (i + 1) * obj_cs[i * len(self._inst): (i + 1) *
...@@ -114,6 +115,9 @@ class TensorMaps_Empty_Test(TensorFields_Empty_Test): ...@@ -114,6 +115,9 @@ class TensorMaps_Empty_Test(TensorFields_Empty_Test):
self._maps = [] self._maps = []
self._maps_fields = [] self._maps_fields = []
def test_maps(self):
self.assertIsNotNone(self._inst.maps)
class TensorFields_Copy_Test(TensorFields_Empty_Test): class TensorFields_Copy_Test(TensorFields_Empty_Test):
def setUp(self): def setUp(self):
......
...@@ -50,15 +50,18 @@ class AbstractNdarray(np.ndarray): ...@@ -50,15 +50,18 @@ class AbstractNdarray(np.ndarray):
Whene inheriting, three attributes are of interest: Whene inheriting, three attributes are of interest:
Attributes: Attributes:
__slots__ (list of str): If you want to add attributes to __slots__ (List(str)): If you want to add attributes to
your AbstractNdarray subclass, add the attribute name to __slots__ your AbstractNdarray subclass, add the attribute name to __slots__
__slot_defaults__ (list): if __slot_defaults__ is None, the __slot_defaults__ (list): if __slot_defaults__ is None, the
defaults for the attributes in __slots__ will be None defaults for the attributes in __slots__ will be None
other values will be treaded as defaults to the corresponding other values will be treaded as defaults to the corresponding
arg at the same position in the __slots__ list. arg at the same position in the __slots__ list.
__slotDtype__ (list of types): for the conversion of the __slot_dtype__ (List(dtypes)): for the conversion of the
args in __slots__ to numpy arrays. None values mean no args in __slots__ to numpy arrays. None values mean no
conversion. conversion.
__slot_setters__ (List(callable)): Because __slots__ and properties are
mutually exclusive this is a possibility to take care of proper
attribute handling. None will be passed for 'not set'.
Args: Args:
array (array-like): input array array (array-like): input array
...@@ -71,7 +74,7 @@ class AbstractNdarray(np.ndarray): ...@@ -71,7 +74,7 @@ class AbstractNdarray(np.ndarray):
__slots__ = [] __slots__ = []
__slot_defaults__ = [] __slot_defaults__ = []
__slotDtypes__ = [] __slot_dtypes__ = []
__slot_setters__ = [] __slot_setters__ = []
def __new__(cls, array, **kwargs): # pragma: no cover def __new__(cls, array, **kwargs): # pragma: no cover
...@@ -96,16 +99,16 @@ class AbstractNdarray(np.ndarray): ...@@ -96,16 +99,16 @@ class AbstractNdarray(np.ndarray):
def _update_slot_kwargs(cls, kwargs): def _update_slot_kwargs(cls, kwargs):
""" """
set the defaults in kwargs according to __slot_defaults__ set the defaults in kwargs according to __slot_defaults__
and convert the kwargs according to __slotDtypes__ and convert the kwargs according to __slot_dtypes__
""" """
slotDefaults = cls.__slot_defaults__ + [None] * ( slot_defaults = cls.__slot_defaults__ + [None] * (
len(cls.__slots__) - len(cls.__slot_defaults__) len(cls.__slots__) - len(cls.__slot_defaults__)
) )
slotDtypes = cls.__slotDtypes__ + [None] * ( slot_dtypes = cls.__slot_dtypes__ + [None] * (
len(cls.__slots__) - len(cls.__slotDtypes__) len(cls.__slots__) - len(cls.__slot_dtypes__)
) )
for attr, default, dtype in zip( for attr, default, dtype in zip(
cls.__slots__, slotDefaults, slotDtypes cls.__slots__, slot_defaults, slot_dtypes
): ):
if attr == "_cache": if attr == "_cache":
continue continue
...@@ -217,22 +220,32 @@ class AbstractNdarray(np.ndarray): ...@@ -217,22 +220,32 @@ class AbstractNdarray(np.ndarray):
@classmethod @classmethod
@contextmanager @contextmanager
def _bypass_setter(cls, slot, demand_existence=False): def _bypass_setters(cls, *slots, empty_means_all=True):
""" """
Temporarily remove the setter in __slot_setters__ corresponding to slot Temporarily remove the setter in __slot_setters__ corresponding to slot
position in __slot__. You should know what you do, when using this. position in __slot__. You should know what you do, when using this.
"""
slot_index = cls.__slots__.index(slot) if slot in cls.__slots__ else None Args:
if slot_index is None: *slots (str): attribute names in __slots__
if demand_existence: empty_means_all (bool): defines behaviour when slots is empty.
raise ValueError("Slot {slot} not existing".format(**locals())) When True: if slots is empty mute all slots in __slots__
else: """
yield if not slots and empty_means_all:
return slots = cls.__slots__
setter = cls.__slot_setters__[slot_index] slot_indices = []
cls.__slot_setters__[slot_index] = None setters = []
for slot in slots:
slot_index = cls.__slots__.index(slot)
if len(cls.__slot_setters__) < slot_index + 1:
# no setter to be found
continue
slot_indices.append(slot_index)
setter = cls.__slot_setters__[slot_index]
setters.append(setter)
cls.__slot_setters__[slot_index] = None
yield yield
cls.__slot_setters__[slot_index] = setter for slot_index, setter in zip(slot_indices, setters):
cls.__slot_setters__[slot_index] = setter
def copy(self, *args, **kwargs): def copy(self, *args, **kwargs):
""" """
...@@ -447,12 +460,13 @@ class AbstractNdarray(np.ndarray): ...@@ -447,12 +460,13 @@ class AbstractNdarray(np.ndarray):
for index in sorted(list(sub_dict)): for index in sorted(list(sub_dict)):
bulk_type = sub_dict[index].get("bulk_type").tolist() bulk_type = sub_dict[index].get("bulk_type").tolist()
if isinstance(bulk_type, bytes): if isinstance(bulk_type, bytes):
# asthonishingly, this is not necessary under linux. Found under nt. ??? # asthonishingly, this is not necessary under linux.
# Found under nt. ???
bulk_type = bulk_type.decode("UTF-8") bulk_type = bulk_type.decode("UTF-8")
bulk_type = getattr(tfields, bulk_type) bulk_type = getattr(tfields, bulk_type)
list_dict[key].append(bulk_type._from_dict(**sub_dict[index])) list_dict[key].append(bulk_type._from_dict(**sub_dict[index]))
with cls._bypass_setter('fields'): with cls._bypass_setters('fields'):
''' '''
Build the normal way Build the normal way
''' '''
...@@ -1548,6 +1562,15 @@ def as_tensors_list(tensors_list): ...@@ -1548,6 +1562,15 @@ def as_tensors_list(tensors_list):
tensors_list = new_list tensors_list = new_list
return tensors_list return tensors_list
def as_maps(maps):
"""
Setter for TensorMaps.maps
Copies input
"""
if maps is not None:
maps = Maps(maps)
return maps
class TensorFields(Tensors): class TensorFields(Tensors):
""" """
...@@ -1636,8 +1659,6 @@ class TensorFields(Tensors): ...@@ -1636,8 +1659,6 @@ class TensorFields(Tensors):
obj = super(TensorFields, cls).__new__(cls, tensors, **kwargs) obj = super(TensorFields, cls).__new__(cls, tensors, **kwargs)
if issubclass(type(tensors), TensorFields): if issubclass(type(tensors), TensorFields):
if tensors.fields is None:
raise ValueError("Tensor fields were None")
obj.fields = tensors.fields obj.fields = tensors.fields
elif not fields: elif not fields:
obj.fields = [] obj.fields = []
...@@ -1701,7 +1722,7 @@ class TensorFields(Tensors): ...@@ -1701,7 +1722,7 @@ class TensorFields(Tensors):
index = index[0] index = index[0]
if item.fields: if item.fields:
# circumvent the setter here. # circumvent the setter here.
with self._bypass_setter('fields'): with self._bypass_setters('fields'):
item.fields = [ item.fields = [
field.__getitem__(index) for field in item.fields field.__getitem__(index) for field in item.fields
] ]
...@@ -1873,6 +1894,88 @@ class TensorFields(Tensors): ...@@ -1873,6 +1894,88 @@ class TensorFields(Tensors):
return artist return artist
class Container(AbstractNdarray):
"""
Store lists of tfields objects. Save mechanisms are provided
Examples:
>>> import numpy as np
>>> import tfields
>>> sphere = tfields.Mesh3D.grid(
... (1, 1, 1),
... (-np.pi, np.pi, 3),
... (-np.pi / 2, np.pi / 2, 3),
... coord_sys='spherical')
>>> sphere2 = sphere.copy() * 3
>>> c = tfields.Container([sphere, sphere2])
>>> c.save("~/tmp/spheres.npz")
>>> c1 = tfields.Container.load("~/tmp/spheres.npz")
"""
__slots__ = ["items", "labels"]
def __new__(cls, items, **kwargs):
if issubclass(type(items), cls):
kwargs.setdefault('labels', items.labels)
items = items.items
kwargs["items"] = items
cls._update_slot_kwargs(kwargs)
empty = np.empty(0, int)
obj = empty.view(cls)
""" set kwargs to slots attributes """
for attr in kwargs:
if attr not in cls._iter_slots():
raise AttributeError(
"Keyword argument {attr} not accepted "
"for class {cls}".format(**locals())
)
setattr(obj, attr, kwargs[attr])
return obj
def __getitem__(self, index):
return self.items[index]
def __setitem__(self, index, value):
self.items[index] = value
def __iter__(self):
return iter(self.items)
def __len__(self):
return len(self.items)
class Maps(Container):
"""
A Maps object is a container for TensorFields sorted by dimension.
"""
def __new__(cls, maps, **kwargs):
if not issubclass(type(maps), Maps):
dims = [dim(obj) for obj in maps]
dims, maps = tfields.lib.util.multi_sort(dims, maps)
kwargs['labels'] = dims
maps_cp = []
for mp in maps:
mp = TensorFields(mp, dtype=int)
if not mp.rank == 1:
raise ValueError(
"Incorrect map rank {mp.rank}".format(**locals())
)
maps_cp.append(mp)
maps = maps_cp
obj = super().__new__(cls, maps, **kwargs)
return obj
@property
def dims(self):
return self.labels
class TensorMaps(TensorFields): class TensorMaps(TensorFields):
""" """
Args: Args:
...@@ -1917,19 +2020,14 @@ class TensorMaps(TensorFields): ...@@ -1917,19 +2020,14 @@ class TensorMaps(TensorFields):
""" """
__slots__ = ['coord_sys', 'name', 'fields', 'maps'] __slots__ = ['coord_sys', 'name', 'fields', 'maps']
__slot_setters__ = [tfields.bases.get_coord_system_name,
None,
as_tensors_list,
as_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 = Maps(kwargs.pop("maps", []))
maps = kwargs.pop("maps", [])
maps_cp = []
for mp in maps:
mp = TensorFields(mp, dtype=int)
if not mp.rank == 1:
raise ValueError(
"Incorrect map rank {mp.rank}".format(**locals())
)
maps_cp.append(mp)
maps = maps_cp
obj = super(TensorMaps, cls).__new__(cls, tensors, *fields, **kwargs) obj = super(TensorMaps, cls).__new__(cls, tensors, *fields, **kwargs)
obj.maps = maps obj.maps = maps
return obj return obj
...@@ -1987,7 +2085,7 @@ class TensorMaps(TensorFields): ...@@ -1987,7 +2085,7 @@ class TensorMaps(TensorFields):
if issubclass(type(item), TensorMaps): if issubclass(type(item), TensorMaps):
if isinstance(index, tuple): if isinstance(index, tuple):
index = index[0] index = index[0]
if item.maps: if len(item.maps) == 0:
item.maps = [mp.copy() for mp in item.maps] item.maps = [mp.copy() for mp in item.maps]
indices = np.array(range(len(self))) indices = np.array(range(len(self)))
keep_indices = indices[index] keep_indices = indices[index]
...@@ -2505,55 +2603,6 @@ class TensorMaps(TensorFields): ...@@ -2505,55 +2603,6 @@ class TensorMaps(TensorFields):
return paths return paths
class Container(AbstractNdarray):
"""
Store lists of tfields objects. Save mechanisms are provided
Examples:
>>> import numpy as np
>>> import tfields
>>> sphere = tfields.Mesh3D.grid(
... (1, 1, 1),
... (-np.pi, np.pi, 3),
... (-np.pi / 2, np.pi / 2, 3),
... coord_sys='spherical')
>>> sphere2 = sphere.copy() * 3
>>> c = tfields.Container([sphere, sphere2])
>>> c.save("~/tmp/spheres.npz")
>>> c1 = tfields.Container.load("~/tmp/spheres.npz")
"""
__slots__ = ["items", "labels"]
def __new__(cls, items, **kwargs):
if issubclass(type(items), cls):
items = items.items
kwargs.setdefault(items.labels)
kwargs["items"] = items
cls._update_slot_kwargs(kwargs)
empty = np.empty(0, int)
obj = empty.view(cls)
""" set kwargs to slots attributes """
for attr in kwargs:
if attr not in cls._iter_slots():
raise AttributeError(
"Keyword argument {attr} not accepted "
"for class {cls}".format(**locals())
)
setattr(obj, attr, kwargs[attr])
return obj
class Maps(Container):
def __new__(cls, items, **kwargs):
if not issubclass(type(items), cls):
print("ASDF")
super().__new__(items, **kwargs)
if __name__ == "__main__": # pragma: no cover if __name__ == "__main__": # pragma: no cover
import doctest import doctest
doctest.testmod() doctest.testmod()
......
...@@ -60,42 +60,57 @@ def multi_sort(array, *others, **kwargs): ...@@ -60,42 +60,57 @@ def multi_sort(array, *others, **kwargs):
Sort all given lists parralel with array sorting, ie rearrange the items in Sort all given lists parralel with array sorting, ie rearrange the items in
the other lists in the same way, you rearrange them for array due to array the other lists in the same way, you rearrange them for array due to array
sorting sorting
Args: Args:
array (list) array (iterable)
*others (list) *others (iterable)
**kwargs: **kwargs:
method (function): sorting function. Default is 'sorted' method (function): sorting function. Default is 'sorted'
...: further arguments are passed to method. Default rest is ...: further arguments are passed to method. Default rest is
'key=array[0]' 'key=array[0]'
reversed (bool): wether to reverse the results or not reversed (bool): wether to reverse the results or not
cast_type (type): type of returned iterables
Examples: Examples:
>>> from tfields.lib.util import multi_sort >>> from tfields.lib.util import multi_sort
>>> multi_sort([1,2,3,6,4], [1,2,3,4,5]) >>> multi_sort([1,2,3,6,4], [1,2,3,4,5])
[[1, 2, 3, 4, 6], [1, 2, 3, 5, 4]] ([1, 2, 3, 4, 6], [1, 2, 3, 5, 4])
>>> a, b = multi_sort([1,2,3,6,4], [1,2,3,4,5]) >>> a, b = multi_sort([1,2,3,6,4], [1,2,3,4,5])
>>> b >>> b
[1, 2, 3, 5, 4] [1, 2, 3, 5, 4]
Expanded to sort as many objects as needed Expanded to sort as many objects as needed
>>> multi_sort([1,2,3,6,4], [1,2,3,4,5], [6,5,4,3,2]) >>> multi_sort([1,2,3,6,4], [1,2,3,4,5], [6,5,4,3,2])
[[1, 2, 3, 4, 6], [1, 2, 3, 5, 4], [6, 5, 4, 2, 3]] ([1, 2, 3, 4, 6], [1, 2, 3, 5, 4], [6, 5, 4, 2, 3])
Reverse argument Reverse argument
>>> multi_sort([1,2,3,6,4], [1,2,3,4,5], [6,5,4,3,2], reverse=True) >>> multi_sort([1,2,3,6,4], [1,2,3,4,5], [6,5,4,3,2], reverse=True)
[[6, 4, 3, 2, 1], [4, 5, 3, 2, 1], [3, 2, 4, 5, 6]] ([6, 4, 3, 2, 1], [4, 5, 3, 2, 1], [3, 2, 4, 5, 6])
Returns:
tuple(cast_type): One iterable for each
>>> multi_sort([], [], [])
([], [], [])
>>> multi_sort([], [], [], cast_type=tuple)
((), (), ())
""" """
method = kwargs.pop('method', None) method = kwargs.pop('method', None)
cast_type = kwargs.pop('cast_type', list)
if not array:
return tuple(cast_type(x) for x in [array] + list(others))
if method is None: if method is None:
method = sorted method = sorted
if 'key' not in kwargs: if 'key' not in kwargs:
kwargs['key'] = lambda pair: pair[0] kwargs['key'] = lambda pair: pair[0]
reverse = kwargs.pop('reverse', False) reverse = kwargs.pop('reverse', False)
if not reverse: if reverse:
cast_type = list
else:
cast_type = lambda x: list(reversed(x)) cast_type = lambda x: list(reversed(x))
return [cast_type(x) for x in zip(*method(zip(array, *others), **kwargs))]
return tuple(cast_type(x) for x in zip(*method(zip(array, *others), **kwargs)))
if __name__ == '__main__': if __name__ == '__main__':
......
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