# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
#
# Copyright(C) 2013-2018 Max-Planck-Society
#
# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik
# and financially supported by the Studienstiftung des deutschen Volkes.
from __future__ import absolute_import, division, print_function
from .compat import *
from .domains.domain import Domain
class DomainTuple(object):
"""Ordered sequence of Domain objects.
This class holds a tuple of :class:`Domain` objects, which together form
the space on which a :class:`Field` is defined.
Notes
-----
DomainTuples should never be created using the constructor, but rather
via the factory function :attr:`make`!
"""
_tupleCache = {}
def __init__(self, domain, _callingfrommake=False):
if not _callingfrommake:
raise NotImplementedError
self._dom = self._parse_domain(domain)
self._axtuple = self._get_axes_tuple()
shape_tuple = tuple(sp.shape for sp in self._dom)
self._shape = reduce(lambda x, y: x + y, shape_tuple, ())
self._size = reduce(lambda x, y: x * y, self._shape, 1)
def _get_axes_tuple(self):
i = 0
res = [None]*len(self._dom)
for idx, thing in enumerate(self._dom):
nax = len(thing.shape)
res[idx] = tuple(range(i, i+nax))
i += nax
return res
@staticmethod
def make(domain):
"""Returns a DomainTuple matching `domain`.
This function checks whether a matching DomainTuple already exists.
If yes, this object is returned, otherwise a new DomainTuple object
is created and returned.
Parameters
----------
domain : Domain or tuple of Domain or DomainTuple
The geometrical structure for which the DomainTuple shall be
obtained.
"""
if isinstance(domain, DomainTuple):
return domain
domain = DomainTuple._parse_domain(domain)
obj = DomainTuple._tupleCache.get(domain)
if obj is not None:
return obj
obj = DomainTuple(domain, _callingfrommake=True)
DomainTuple._tupleCache[domain] = obj
return obj
@staticmethod
def _parse_domain(domain):
if domain is None:
return ()
if isinstance(domain, Domain):
return (domain,)
if not isinstance(domain, tuple):
domain = tuple(domain)
for d in domain:
if not isinstance(d, Domain):
raise TypeError(
"Given object contains something that is not an "
"instance of Domain class.")
return domain
def __getitem__(self, i):
return self._dom[i]
@property
def shape(self):
"""tuple of int: number of pixels along each axis
The shape of the array-like object required to store information
living on the DomainTuple.
"""
return self._shape
@property
def local_shape(self):
"""tuple of int: number of pixels along each axis on the local task
The shape of the array-like object required to store information
living on part of the domain which is stored on the local MPI task.
"""
from .dobj import local_shape
return local_shape(self._shape)
@property
def size(self):
"""int : total number of pixels.
Equivalent to the products over all entries in the object's shape.
"""
return self._size
@property
def axes(self):
"""tuple of tuple of int : axis indices of the underlying domains"""
return self._axtuple
def __len__(self):
return len(self._dom)
def __hash__(self):
return self._dom.__hash__()
def __eq__(self, x):
if self is x:
return True
return self is DomainTuple.make(x)
def __ne__(self, x):
return not self.__eq__(x)
def __str__(self):
res = "DomainTuple, len: " + str(len(self))
for i in self:
res += "\n" + str(i)
return res