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):
atol=ATOL)
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)):
value = np.allclose(merge_list[1],
obj_cs[i * len(self._inst): (i + 1) *
......@@ -114,6 +115,9 @@ class TensorMaps_Empty_Test(TensorFields_Empty_Test):
self._maps = []
self._maps_fields = []
def test_maps(self):
self.assertIsNotNone(self._inst.maps)
class TensorFields_Copy_Test(TensorFields_Empty_Test):
def setUp(self):
......
......@@ -50,15 +50,18 @@ class AbstractNdarray(np.ndarray):
Whene inheriting, three attributes are of interest:
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__
__slot_defaults__ (list): if __slot_defaults__ is None, the
defaults for the attributes in __slots__ will be None
other values will be treaded as defaults to the corresponding
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
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:
array (array-like): input array
......@@ -71,7 +74,7 @@ class AbstractNdarray(np.ndarray):
__slots__ = []
__slot_defaults__ = []
__slotDtypes__ = []
__slot_dtypes__ = []
__slot_setters__ = []
def __new__(cls, array, **kwargs): # pragma: no cover
......@@ -96,16 +99,16 @@ class AbstractNdarray(np.ndarray):
def _update_slot_kwargs(cls, kwargs):
"""
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__)
)
slotDtypes = cls.__slotDtypes__ + [None] * (
len(cls.__slots__) - len(cls.__slotDtypes__)
slot_dtypes = cls.__slot_dtypes__ + [None] * (
len(cls.__slots__) - len(cls.__slot_dtypes__)
)
for attr, default, dtype in zip(
cls.__slots__, slotDefaults, slotDtypes
cls.__slots__, slot_defaults, slot_dtypes
):
if attr == "_cache":
continue
......@@ -217,22 +220,32 @@ class AbstractNdarray(np.ndarray):
@classmethod
@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
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
if slot_index is None:
if demand_existence:
raise ValueError("Slot {slot} not existing".format(**locals()))
else:
yield
return
setter = cls.__slot_setters__[slot_index]
cls.__slot_setters__[slot_index] = None
Args:
*slots (str): attribute names in __slots__
empty_means_all (bool): defines behaviour when slots is empty.
When True: if slots is empty mute all slots in __slots__
"""
if not slots and empty_means_all:
slots = cls.__slots__
slot_indices = []
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
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):
"""
......@@ -447,12 +460,13 @@ class AbstractNdarray(np.ndarray):
for index in sorted(list(sub_dict)):
bulk_type = sub_dict[index].get("bulk_type").tolist()
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 = getattr(tfields, bulk_type)
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
'''
......@@ -1548,6 +1562,15 @@ def as_tensors_list(tensors_list):
tensors_list = new_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):
"""
......@@ -1636,8 +1659,6 @@ class TensorFields(Tensors):
obj = super(TensorFields, cls).__new__(cls, tensors, **kwargs)
if issubclass(type(tensors), TensorFields):
if tensors.fields is None:
raise ValueError("Tensor fields were None")
obj.fields = tensors.fields
elif not fields:
obj.fields = []
......@@ -1701,7 +1722,7 @@ class TensorFields(Tensors):
index = index[0]
if item.fields:
# circumvent the setter here.
with self._bypass_setter('fields'):
with self._bypass_setters('fields'):
item.fields = [
field.__getitem__(index) for field in item.fields
]
......@@ -1873,6 +1894,88 @@ class TensorFields(Tensors):
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):
"""
Args:
......@@ -1917,19 +2020,14 @@ class TensorMaps(TensorFields):
"""
__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):
# TODO: the type of maps should rather be a dictionary with keys: dim
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
maps = Maps(kwargs.pop("maps", []))
obj = super(TensorMaps, cls).__new__(cls, tensors, *fields, **kwargs)
obj.maps = maps
return obj
......@@ -1987,7 +2085,7 @@ class TensorMaps(TensorFields):
if issubclass(type(item), TensorMaps):
if isinstance(index, tuple):
index = index[0]
if item.maps:
if len(item.maps) == 0:
item.maps = [mp.copy() for mp in item.maps]
indices = np.array(range(len(self)))
keep_indices = indices[index]
......@@ -2505,55 +2603,6 @@ class TensorMaps(TensorFields):
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
import doctest
doctest.testmod()
......
......@@ -60,42 +60,57 @@ def multi_sort(array, *others, **kwargs):
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
sorting
Args:
array (list)
*others (list)
array (iterable)
*others (iterable)
**kwargs:
method (function): sorting function. Default is 'sorted'
...: further arguments are passed to method. Default rest is
method (function): sorting function. Default is 'sorted'
...: further arguments are passed to method. Default rest is
'key=array[0]'
reversed (bool): wether to reverse the results or not
cast_type (type): type of returned iterables
Examples:
>>> from tfields.lib.util import multi_sort
>>> 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])
>>> b
[1, 2, 3, 5, 4]
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])
[[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
>>> 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)
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:
method = sorted
if 'key' not in kwargs:
kwargs['key'] = lambda pair: pair[0]
reverse = kwargs.pop('reverse', False)
if not reverse:
cast_type = list
else:
if reverse:
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__':
......
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