diff --git a/hysop/backend/device/autotunable_kernel.py b/hysop/backend/device/autotunable_kernel.py index 170f3348ec1006dbc3ccc0f649018a7ce0ae9ba0..0bec40f408d6309089a942fc41f4f91b8037ac1d 100644 --- a/hysop/backend/device/autotunable_kernel.py +++ b/hysop/backend/device/autotunable_kernel.py @@ -8,8 +8,7 @@ from hysop.backend.device.kernel_autotuner_config import KernelAutotunerConfig from hysop.backend.device.codegen.structs.mesh_info import MeshInfoStruct from hysop.fields.cartesian_discrete_field import CartesianDiscreteScalarFieldView -class AutotunableKernel(object): - __metaclass__ = ABCMeta +class AutotunableKernel(object, metaclass=ABCMeta): def __init__(self, autotuner_config, build_opts, dump_src=None, symbolic_mode=None, **kwds): diff --git a/hysop/backend/device/codegen/functions/complex.py b/hysop/backend/device/codegen/functions/complex.py index 5b29dd56fdfb8ec2a6d08407e137bf36f98c99b5..3f2e68fafd318f57828c3f10eeaac70542b675df 100644 --- a/hysop/backend/device/codegen/functions/complex.py +++ b/hysop/backend/device/codegen/functions/complex.py @@ -8,9 +8,7 @@ from hysop.backend.device.codegen.base.utils import WriteOnceDict, Ar from hysop.backend.device.codegen.base.statistics import WorkStatistics from hysop.backend.device.opencl.opencl_types import OpenClTypeGen, basetype -class OpenClComplexOperator(OpenClFunctionCodeGenerator): - - __metaclass__ = ABCMeta +class OpenClComplexOperator(OpenClFunctionCodeGenerator, metaclass=ABCMeta): def __init__(self, typegen, ftype, vectorization, output=None, known_args=None): diff --git a/hysop/backend/device/codegen/kernels/custom_symbolic.py b/hysop/backend/device/codegen/kernels/custom_symbolic.py index 066f9382e1b54b44bd229aa16a489092c311a623..df977b761da2fcbe0fe802e3ea2129baf676ae4f 100644 --- a/hysop/backend/device/codegen/kernels/custom_symbolic.py +++ b/hysop/backend/device/codegen/kernels/custom_symbolic.py @@ -384,9 +384,7 @@ class SymbolicCodegenContext(object): return args -class CustomSymbolicKernelGenerator(KernelCodeGenerator): - - __metaclass__ = ABCMeta +class CustomSymbolicKernelGenerator(KernelCodeGenerator, metaclass=ABCMeta): @classmethod def create(cls, expr_info, **kwds): diff --git a/hysop/backend/device/device_platform.py b/hysop/backend/device/device_platform.py index 096eec3849a77312ff17b68ff3134b36c969742a..63df8bdde35f4b5b43af1ddf31cc535a704fd871 100644 --- a/hysop/backend/device/device_platform.py +++ b/hysop/backend/device/device_platform.py @@ -1,9 +1,7 @@ from abc import ABCMeta, abstractmethod -class Platform(object): - - __metaclass__ = ABCMeta +class Platform(object, metaclass=ABCMeta): def __init__(self, hardware_topo, platform_handle, platform_id, **kwds): super(Platform, self).__init__(**kwds) @@ -12,11 +10,11 @@ class Platform(object): self._discover_devices(hardware_topo, platform_handle) # we do not keep a reference to platform_handle as we # need to pickle this object - + @property def platform_id(self): return self._platform_id - + @property def logical_devices(self): return self._logical_devices @@ -24,7 +22,7 @@ class Platform(object): @property def physical_devices(self): return [dev.physical_device() for dev in self.logical_devices()] - + @abstractmethod def _discover_devices(self, hardware_topo, platform_handle): pass diff --git a/hysop/backend/device/kernel_autotuner.py b/hysop/backend/device/kernel_autotuner.py index 7bb7792d9d61e1fe51110e9cf8910080b787b9e3..f4a6e9096f3a119a2c86ffea11a1420332bb00a9 100644 --- a/hysop/backend/device/kernel_autotuner.py +++ b/hysop/backend/device/kernel_autotuner.py @@ -18,8 +18,7 @@ from hysop.backend.device.codegen import CodeGeneratorWarning class KernelGenerationError(RuntimeError): pass -class KernelAutotuner(object): - __metaclass__ = ABCMeta +class KernelAutotuner(object, metaclass=ABCMeta): FULL_RESULTS_KEY = '__FULL_RESULTS__' DUMP_LAST_TUNED_KERNEL = False diff --git a/hysop/backend/device/kernel_autotuner_config.py b/hysop/backend/device/kernel_autotuner_config.py index ee4483e49fc367fe0adc538371c6efdff2a211a0..b8ed3d302944b7072493ecf13160c523a157d41b 100644 --- a/hysop/backend/device/kernel_autotuner_config.py +++ b/hysop/backend/device/kernel_autotuner_config.py @@ -4,9 +4,7 @@ from hysop.constants import AutotunerFlags, \ __DEBUG__, __VERBOSE__, __KERNEL_DEBUG__, \ DEFAULT_AUTOTUNER_FLAG, DEFAULT_AUTOTUNER_PRUNE_THRESHOLD -class KernelAutotunerConfig(object): - - __metaclass__ = ABCMeta +class KernelAutotunerConfig(object, metaclass=ABCMeta): _default_initial_runs = { AutotunerFlags.ESTIMATE: 1, diff --git a/hysop/backend/device/kernel_config.py b/hysop/backend/device/kernel_config.py index 334ab9e34cbbad72498c1a64e739fda4b42f81ba..6db66240464c2c952b580e87fe46ddae7904743a 100644 --- a/hysop/backend/device/kernel_config.py +++ b/hysop/backend/device/kernel_config.py @@ -5,9 +5,7 @@ from hysop.constants import Precision from hysop.backend.device.kernel_autotuner_config import KernelAutotunerConfig from hysop.tools.types import check_instance, first_not_None -class KernelConfig(object): - - __metaclass__ = ABCMeta +class KernelConfig(object, metaclass=ABCMeta): def __init__(self, autotuner_config=None, user_build_options=None, @@ -15,7 +13,7 @@ class KernelConfig(object): precision=None, float_dump_mode=None, use_short_circuit_ops=None, - unroll_loops=None): + unroll_loops=None): autotuner_config = first_not_None(autotuner_config, self.default_autotuner_config()) user_build_options = first_not_None(user_build_options, []) @@ -23,7 +21,7 @@ class KernelConfig(object): precision = first_not_None(precision, Precision.SAME) use_short_circuit_ops = first_not_None(use_short_circuit_ops, False) unroll_loops = first_not_None(unroll_loops, False) - + if (float_dump_mode is None): if __KERNEL_DEBUG__: float_dump_mode = 'dec' diff --git a/hysop/backend/device/logical_device.py b/hysop/backend/device/logical_device.py index d9e74c926d3b17934518065e1063164ecec49768..44682869e544b2956bc2fea40e91470877ce1fc2 100644 --- a/hysop/backend/device/logical_device.py +++ b/hysop/backend/device/logical_device.py @@ -10,11 +10,9 @@ class UnknownDeviceAttribute(object): return 'unknown' -class LogicalDevice(object): +class LogicalDevice(object, metaclass=ABCMeta): - __metaclass__ = ABCMeta - - def __init__(self, platform, platform_handle, device_id, device_handle, + def __init__(self, platform, platform_handle, device_id, device_handle, hardware_topo, **kargs): super(LogicalDevice,self).__init__(**kargs) self._platform = platform @@ -22,11 +20,11 @@ class LogicalDevice(object): physical_devices = self._match_physical_devices(hardware_topo=hardware_topo) physical_devices = to_tuple(physical_devices) self._physical_devices = physical_devices - + vendor = hardware_topo.pciids.find_vendor(self.vendor_id()) - self._vendor_handle = vendor + self._vendor_handle = vendor self._device_handle = None - + # identifying device without device_id is not easy for CPUs # so we do not look for a device handle if (physical_devices is not None): @@ -50,15 +48,15 @@ class LogicalDevice(object): return ' [0x{:04x}]'.format(did) else: return '' - + @property def device_id(self): return self._device_id - + @property def platform(self): return self._platform - + @property def physical_devices(self): return self._physical_devices @@ -66,7 +64,7 @@ class LogicalDevice(object): @abstractmethod def _match_physical_devices(self, hardware_topo): pass - + @abstractmethod def _determine_performance_and_affinity(self, hardware_topo): pass @@ -153,14 +151,14 @@ class LogicalDevice(object): @abstractmethod def max_global_alloc_size(self): pass - + @abstractmethod def local_mem_size(self): pass @abstractmethod def local_mem_type(self): pass - + #DEVICE SPLITTING @abstractmethod @@ -194,7 +192,7 @@ class LogicalDevice(object): @abstractmethod def fp64_config(self): pass - + #IMAGES def has_image_support(self): pass @@ -206,14 +204,14 @@ class LogicalDevice(object): pass def max_samplers(self): pass - + def has_1d_image_support(self): pass def has_2d_image_support(self): pass def has_3d_image_support(self): pass - + def has_1d_image_write_support(self): pass def has_2d_image_write_support(self): @@ -225,7 +223,7 @@ class LogicalDevice(object): pass def has_2d_array_image_support(self): pass - + def max_1d_image_size(self): pass def max_1d_image_array_size(self): @@ -238,7 +236,7 @@ class LogicalDevice(object): def max_3d_image_size(self): pass - + def has_2d_image_from_buffer_support(self): pass @@ -254,7 +252,7 @@ class LogicalDevice(object): def image_max_array_size(self): pass - + #ATOMICS @abstractmethod def has_global_int32_atomics(self): @@ -268,7 +266,7 @@ class LogicalDevice(object): @abstractmethod def has_global_float64_atomics(self): pass - + @abstractmethod def has_local_int32_atomics(self): pass @@ -281,7 +279,7 @@ class LogicalDevice(object): @abstractmethod def has_local_float64_atomics(self): pass - + @abstractmethod def has_mixed_int32_atomics(self): pass @@ -294,7 +292,7 @@ class LogicalDevice(object): @abstractmethod def has_mixed_float64_atomics(self): pass - + @abstractmethod def has_int32_hardware_atomic_counters(self): pass diff --git a/hysop/backend/device/opencl/opencl_allocator.py b/hysop/backend/device/opencl/opencl_allocator.py index 44d8f8b0c7b6ee5fc8232ac7042cdacb156d9e00..e44b2a0f475d162cfda743d8f65f1fd47e880d08 100644 --- a/hysop/backend/device/opencl/opencl_allocator.py +++ b/hysop/backend/device/opencl/opencl_allocator.py @@ -5,11 +5,10 @@ from hysop.backend.device.opencl import cl, cl_api from hysop.core.memory.allocator import AllocatorBase from hysop.backend.device.opencl.opencl_buffer import OpenClBuffer -class OpenClAllocator(AllocatorBase): +class OpenClAllocator(AllocatorBase, metaclass=ABCMeta): """ Base class for OpenCl backend allocators. """ - __metaclass__=ABCMeta def __init__(self, queue, mem_flags=cl.mem_flags.READ_WRITE, verbose=None): super(OpenClAllocator, self).__init__(verbose=verbose) @@ -25,7 +24,7 @@ class OpenClAllocator(AllocatorBase): def max_alloc_size(self): """Max allocatable size in bytes.""" return self._max_alloc_size - + def get_queue(self): return self._queue def get_context(self): @@ -47,10 +46,10 @@ class OpenClAllocator(AllocatorBase): """ super(OpenClAllocator, self).allocate(nbytes=nbytes, **kwds) try: - return self._allocate_impl(nbytes=nbytes) + return self._allocate_impl(nbytes=nbytes) except cl.Error as e: raise MemoryError(str(e)) - + def is_on_host(self): """ Return true if buffers are allocated in host memory. @@ -98,7 +97,7 @@ class OpenClImmediateAllocator(OpenClAllocator): raise MemoryError(str(e)) return buf - + def memory_pool(self, name, **kwds): """ Construct a memory pool from this allocator. @@ -107,4 +106,4 @@ class OpenClImmediateAllocator(OpenClAllocator): if isinstance(self, MemoryPool): msg='allocator is already a memory pool.' raise RuntimeError(msg) - return OpenClMemoryPool(allocator=self, name=name, verbose=None, **kwds) + return OpenClMemoryPool(allocator=self, name=name, verbose=None, **kwds) diff --git a/hysop/backend/device/opencl/opencl_kernel_launcher.py b/hysop/backend/device/opencl/opencl_kernel_launcher.py index 0d203fc6377340fc907a8610d471512945852782..b96823f279fec274279c15a8bc14f062beb63301 100644 --- a/hysop/backend/device/opencl/opencl_kernel_launcher.py +++ b/hysop/backend/device/opencl/opencl_kernel_launcher.py @@ -251,11 +251,10 @@ class OpenClKernelListLauncher(object): statistics = property(_get_statistics) -class LauncherI(object): +class LauncherI(object, metaclass=ABCMeta): """ Interface for any object that has the ability to be a launcher. """ - __metaclass__ = ABCMeta def __init__(self, name, **kwds): """ @@ -595,11 +594,9 @@ class OpenClParametrizedKernelLauncher(OpenClKernelLauncher): parameters_map = property(_get_parameters_map) -class OpenClKernelParameterGenerator(object): +class OpenClKernelParameterGenerator(object, metaclass=ABCMeta): """Abstract base for opencl kernel parameter yielders.""" - __metaclass__ = ABCMeta - def __iter__(self): return self.new_generator() diff --git a/hysop/backend/device/opencl/opencl_operator.py b/hysop/backend/device/opencl/opencl_operator.py index b88796c7b012d79a6338f8bc5ae68514950a3ea8..5359257012ec78f11fe07bf35150ebde20c662c4 100644 --- a/hysop/backend/device/opencl/opencl_operator.py +++ b/hysop/backend/device/opencl/opencl_operator.py @@ -21,11 +21,10 @@ from hysop.topology.topology import Topology, TopologyView from hysop.topology.topology_descriptor import TopologyDescriptor from hysop.fields.discrete_field import DiscreteScalarFieldView -class OpenClOperator(ComputationalGraphOperator): +class OpenClOperator(ComputationalGraphOperator, metaclass=ABCMeta): """ Abstract class for discrete operators working on OpenCL backends. """ - __metaclass__ = ABCMeta __default_method = { OpenClKernelConfig: OpenClKernelConfig() diff --git a/hysop/backend/hardware/hardware_backend.py b/hysop/backend/hardware/hardware_backend.py index 21fa5ab05a1aaad52ea4acc2393367c6721fc7c8..7c80319116777024ad6c075c76e3cf07d1600498 100644 --- a/hysop/backend/hardware/hardware_backend.py +++ b/hysop/backend/hardware/hardware_backend.py @@ -1,19 +1,17 @@ from abc import ABCMeta, abstractmethod -class HardwareBackend(object): - - __metaclass__ = ABCMeta +class HardwareBackend(object, metaclass=ABCMeta): def __init__(self, hardware_topo, **kargs): super(HardwareBackend,self).__init__(**kargs) self._platforms = {} self._discover_platforms(hardware_topo) - + @property def platforms(self): return self._platforms - + @abstractmethod def _discover_platforms(self, hardware_topo): pass diff --git a/hysop/backend/hardware/hwinfo.py b/hysop/backend/hardware/hwinfo.py index 227203cac644e4e2c4fdb7b00ddf4f353d499bd6..5510a00d87c11b46c202a95d3f2175516cffe451 100644 --- a/hysop/backend/hardware/hwinfo.py +++ b/hysop/backend/hardware/hwinfo.py @@ -18,14 +18,12 @@ from hysop.tools.cache import load_data_from_cache, update_cache, machine_id from hysop.backend.hardware.pci_ids import PCIIds from hysop.core.mpi import is_multihost, interhost_comm, host_rank -class TopologyObject(object): +class TopologyObject(object, metaclass=ABCMeta): """ XML parser base to parse lstopo (hardware info) xml output. See hwloc(7) and lstopo(1) man. """ - __metaclass__ = ABCMeta - _print_indent = ' '*2 def __init__(self, parent, element, pciids=None): @@ -181,8 +179,7 @@ class TopologyObject(object): self._attributes['distances'] = np.reshape(np.asarray(values, dtype=np.float32),(nbobjs,nbobjs,)) -class HardwareStatistics(object): - __metaclass__ = ABCMeta +class HardwareStatistics(object, metaclass=ABCMeta): def _minmax(self, values, op=lambda x: x, dtype=np.int32): return 'mean={}, min={}, max={}'.format(op(np.mean(values).astype(dtype)), diff --git a/hysop/backend/host/fortran/fortran_operator.py b/hysop/backend/host/fortran/fortran_operator.py index a7a1d68c56d39f49c8771368c62fca1373ddf642..fbf4d5af2aa60ad849dbd159910520c0540c66c3 100644 --- a/hysop/backend/host/fortran/fortran_operator.py +++ b/hysop/backend/host/fortran/fortran_operator.py @@ -2,7 +2,7 @@ discrete operators working with fortran. * :class:`~hysop.backend.host.fortran.FortranOperator` is an abstract class - used to provide a common interface to all discrete operators working with + used to provide a common interface to all discrete operators working with fortran. """ from abc import ABCMeta @@ -12,12 +12,11 @@ from hysop.constants import MemoryOrdering from hysop.backend.host.host_operator import HostOperator from hysop.fields.discrete_field import DiscreteScalarFieldView -class FortranOperator(HostOperator): +class FortranOperator(HostOperator, metaclass=ABCMeta): """ Abstract class for discrete operators working with fortran. """ - __metaclass__ = ABCMeta - + @debug def get_field_requirements(self): requirements = super(FortranOperator, self).get_field_requirements() @@ -33,7 +32,7 @@ class FortranOperator(HostOperator): if self.discretized: return super(FortranOperator, self).discretize() - + @debug def setup(self, work): super(FortranOperator, self).setup(work) diff --git a/hysop/backend/host/host_directional_operator.py b/hysop/backend/host/host_directional_operator.py index dc4e9816f00d3f3bd7b3fcd4d711b49e90863ed2..f43dccf690e0dff5a384e3933c65e4169fb02497 100644 --- a/hysop/backend/host/host_directional_operator.py +++ b/hysop/backend/host/host_directional_operator.py @@ -4,15 +4,13 @@ from hysop.tools.decorators import debug from hysop.operator.directional.directional import DirectionalOperatorBase from hysop.backend.host.host_operator import HostOperator -class HostDirectionalOperator(DirectionalOperatorBase, HostOperator): +class HostDirectionalOperator(DirectionalOperatorBase, HostOperator, metaclass=ABCMeta): """ Abstract class for discrete directional operators working on host backends. - - Field requirements are set such that the current direction will + + Field requirements are set such that the current direction will be contiguous in memory. """ - - __metaclass__ = ABCMeta @debug def __init__(self, **kwds): diff --git a/hysop/backend/host/host_operator.py b/hysop/backend/host/host_operator.py index bf818f257ef66bf49f683a6097b2b4bd40cf0045..8c86415aba72e881abbce545799ea512ea369a9c 100644 --- a/hysop/backend/host/host_operator.py +++ b/hysop/backend/host/host_operator.py @@ -14,11 +14,10 @@ from hysop.core.graph.computational_operator import ComputationalGraphOperator from hysop.topology.topology_descriptor import TopologyDescriptor -class HostOperator(ComputationalGraphOperator): +class HostOperator(ComputationalGraphOperator, metaclass=ABCMeta): """ Abstract class for discrete operators working on OpenCL backends. """ - __metaclass__ = ABCMeta @debug def __init__(self, **kwds): diff --git a/hysop/core/arrays/array.py b/hysop/core/arrays/array.py index 27a8f7b48266d1e6fcbb87381cb659168a05eb93..a837463bd8ea8ea0cb382df5025d0e809977934a 100644 --- a/hysop/core/arrays/array.py +++ b/hysop/core/arrays/array.py @@ -7,26 +7,26 @@ from hysop.tools.types import check_instance from hysop.tools.numpywrappers import slices_empty from hysop.tools.decorators import required_property, optional_property - -class Array(object): + +class Array(object, metaclass=ABCMeta): """ Interface of an abstract array. - An array is a numpy.ndarray work-alike that stores its data and performs + An array is a numpy.ndarray work-alike that stores its data and performs its computations on various devices, depending on the backend. - All exposed functions should work exactly as in numpy. - Arithmetic methods in Array, when available, should at least support the + All exposed functions should work exactly as in numpy. + Arithmetic methods in Array, when available, should at least support the broadcasting of scalars. - For Fortran users, reverse the usual order of indices when accessing elements of an array. + For Fortran users, reverse the usual order of indices when accessing elements of an array. to be in line with Python semantics and the natural order of the data. - The fact is that Python indexing on lists and other sequences naturally leads to an - outside-to inside ordering (the first index gets the largest grouping, and the last + The fact is that Python indexing on lists and other sequences naturally leads to an + outside-to inside ordering (the first index gets the largest grouping, and the last gets the smallest element). - + See https://docs.scipy.org/doc/numpy-1.10.0/reference/internals.html for more information about C versus Fortran ordering in numpy. - - Numpy notation are used for axes, axe 0 is the slowest varying index and last axe is + + Numpy notation are used for axes, axe 0 is the slowest varying index and last axe is the fastest varying index. By default: 3D C-ordering is [0,1,2] which corresponds to ZYX transposition state. @@ -35,13 +35,11 @@ class Array(object): This means that when taking array byte strides, in the axis order, the strides are decreasing until the last stride wich is the size of array dtype in bytes. """ - - __metaclass__ = ABCMeta - + def __init__(self, handle, backend, **kwds): """ Build an Array instance. - + Parameters ---------- handle: buffer backend implementation @@ -51,7 +49,7 @@ class Array(object): Notes ----- This should never be called directly by the user. - Arrays should be constructed using array backend facilities, like zeros or empty. + Arrays should be constructed using array backend facilities, like zeros or empty. The parameters given here refer to a low-level method for instantiating an array. """ from hysop.core.arrays.all import ArrayBackend @@ -84,7 +82,7 @@ class Array(object): msg=msg.format(self.__class__) raise RuntimeError(msg) return self._backend - + handle = property(get_handle) backend = property(get_backend) @@ -111,13 +109,13 @@ class Array(object): return self.handle.__nonzero__() else: return True - + @classmethod def _not_implemented_yet(cls, funname): msg = '{}::{} has not been implemented yet.' msg=msg.format(cls.__name__, funname) raise NotImplementedError(msg) - + @classmethod def _unsupported_argument(cls, fname, argname, arg, default_value=None): if arg != default_value: @@ -125,34 +123,34 @@ class Array(object): msg+= 'supported and should be set to {}.' msg=msg.format(cls.__name__, fname, argname, default_value) raise NotImplementedError(msg) - + def wrap(self, handle): """ Wrap handle with the same initialization arguments as this instance. """ return self.backend.wrap(handle=handle) - + def _call(self, fname, *args, **kargs): """ Calls a handle function. """ f = getattr(self.handle, fname) return self.backend._call(f, *args, **kargs) - + @abstractmethod def as_symbolic_array(self, name, **kwds): """ Return a symbolic array variable that contain a reference to this array. """ pass - + @abstractmethod def as_symbolic_buffer(self, name, **kwds): """ Return a symbolic buffer variable that contain a reference to this array. """ pass - + @abstractmethod @required_property def get_ndim(self): @@ -160,7 +158,7 @@ class Array(object): Number of array dimensions. """ pass - + @abstractmethod @required_property def get_int_ptr(self): @@ -182,8 +180,8 @@ class Array(object): def set_shape(self): """ Set the shape of this buffer. - From the numpy doc: It is not always possible to change the shape of an array without - copying the data. If you want an error to be raised if the data is copied, you should + From the numpy doc: It is not always possible to change the shape of an array without + copying the data. If you want an error to be raised if the data is copied, you should assign the new shape to the shape attribute of the array. """ pass @@ -203,7 +201,7 @@ class Array(object): Tuple of ints that represents the byte step in each dimension when traversing an array. """ pass - + @abstractmethod @required_property def get_data(self): @@ -219,7 +217,7 @@ class Array(object): Base object if memory is from some other object. """ pass - + @abstractmethod @required_property def get_dtype(self): @@ -227,21 +225,21 @@ class Array(object): numpy.dtype representing the type stored into this buffer. """ pass - + @optional_property def get_flags(self): """ Information about the memory layout of the array. """ pass - + @optional_property def get_imag(self): """ The imaginary part of the array. """ pass - + @optional_property def get_real(self): """ @@ -255,7 +253,7 @@ class Array(object): An object to simplify the interaction of the array with the ctypes module. """ pass - + def get_T(self): """ Same as self.transpose(), except that self is returned if self.ndim < 2. @@ -264,13 +262,13 @@ class Array(object): return self else: return self.transpose() - + def get_size(self): """ Number of elements in the array. """ return prod(self.get_shape()) - + def get_itemsize(self): """ Number of bytes per element. @@ -282,8 +280,8 @@ class Array(object): Number of bytes in the whole buffer. """ return self.itemsize*self.size - - + + # array properties to be (re)defined ndim = property(get_ndim) shape = property(get_shape, set_shape) @@ -293,19 +291,19 @@ class Array(object): base = property(get_base) dtype = property(get_dtype) int_ptr = property(get_int_ptr) - + # optional array properties flags = property(get_flags) imag = property(get_imag) real = property(get_real) ctypes = property(get_ctypes) - + # deduced array properties, may be redefined size = property(get_size) itemsize = property(get_itemsize) nbytes = property(get_nbytes) T = property(get_T) - + @abstractmethod def get(self, handle=False): """ @@ -330,18 +328,18 @@ class Array(object): physical memory as other. """ return self.get_data_base() is other.get_data_base() - + def ctype(self): """ Equivalent C type corresponding to the numpy.dtype. """ self.__class__.not_implemented_yet('ctype') - + def get_order(self): """ Memory ordering. - Determine whether the array view is written in C-contiguous order - (last index varies the fastest), or FORTRAN-contiguous order + Determine whether the array view is written in C-contiguous order + (last index varies the fastest), or FORTRAN-contiguous order in memory (first index varies the fastest). If dimension is one, default_order is returned. """ @@ -366,7 +364,7 @@ class Array(object): Axe 0 is the slowest varying index, last axe is the fastest varying index. ie 3D C-ordering is [2,1,0] 3D fortran-ordering is [0,1,2] - + Thoses are the axes seen as a numpy view on memory, *only* strides are permutated for access, Those axes are found by reverse argsorting the array strides, using a stable sorting algorithm. @@ -416,14 +414,14 @@ class Array(object): Copy data from buffer src """ self.backend.memcpy(self, src, **kargs) - + def copy_to(self, dst, **kargs): """ Copy data from buffer to dst """ self.backend.memcpy(dst,self, **kargs) - - + + def transpose_to_state(self, state, **kargs): """ Transpose buffer to specified transposition state. @@ -437,7 +435,7 @@ class Array(object): for axe in target: axes.append(origin.index(axe)) return self.transpose(axes=axes) - + # np.ndarray like methods @@ -470,7 +468,7 @@ class Array(object): """ Returns the indices that would partition this array. """ - return self.backend.argpartition(a=self, kth=kth, axis=axis, kind=kind, + return self.backend.argpartition(a=self, kth=kth, axis=axis, kind=kind, order=order, **kargs) def argsort(self, axis=-1, kind='quicksort', order=None, **kargs): @@ -478,8 +476,8 @@ class Array(object): Returns the indices that would sort this array. """ return self.backend.argsort(a=self, axis=axis, kind=kind, order=order, **kargs) - - def astype(self, dtype, order=MemoryOrdering.SAME_ORDER, casting='unsafe', subok=True, + + def astype(self, dtype, order=MemoryOrdering.SAME_ORDER, casting='unsafe', subok=True, copy=True, **kargs): """ Copy of the array, cast to a specified type. @@ -490,7 +488,7 @@ class Array(object): def byteswap(self, inplace=False, **kargs): """ Swap the bytes of the array elements - Toggle between low-endian and big-endian data representation by returning + Toggle between low-endian and big-endian data representation by returning a byteswapped array, optionally swapped in-place. """ return self.backend.byteswap(a=self, inplace=inplace, **kargs) @@ -519,7 +517,7 @@ class Array(object): Complex-conjugate of all elements. """ return self.backend.conj(x=self, out=out, **kargs) - + def conjugate(self, out=None, **kargs): """ Return the complex conjugate, element-wise. @@ -537,7 +535,7 @@ class Array(object): Return the cumulative sum of the elements along the given axis. """ return self.backend.cumsum(a=self, axis=axis, dtype=dtype, out=out, **kargs) - + def copy(self, order=MemoryOrdering.SAME_ORDER, **kargs): """ Return a copy of the array. @@ -610,7 +608,7 @@ class Array(object): Rearranges the elements in the array in such a way that value of the element i in kth position is in the position it would be in a sorted array. """ - return self.backend.partition(a=self, kth=kth, axis=axis, kind=kind, + return self.backend.partition(a=self, kth=kth, axis=axis, kind=kind, order=order, **kargs) def prod(self, axis=None, dtype=None, out=None, **kargs): @@ -630,56 +628,56 @@ class Array(object): Return a flattened array. """ return self.backend.ravel(a=self, order=order, **kargs) - + def repeat(self, repeats, axis=None, **kargs): """ Repeat elements of an array. """ return self.backend.repeat(a=self, repeats=repeats, axis=axis, **kargs) - + def reshape(self, new_shape, order=default_order, **kargs): """ Returns an array containing the same data with a new shape. """ return self.backend.reshape(a=self, newshape=new_shape, order=order, **kargs) - + def resize(self, new_shape, refcheck=True, **kargs): """ Change shape and size of array in-place. """ return self.backend.resize(a=self, new_shape=new_shape, refcheck=refcheck, **kargs) - + def round(self, decimals=0, out=None, **kargs): """ Return a with each element rounded to the given number of decimals. """ return self.backend.around(a=self, decimals=decimals, out=out, **kargs) - + def searchsorted(self, v, side='left', sorter=None, **kargs): """ Find indices where elements of v should be inserted in a to maintain order. """ return self.backend.searchsorted(a=self, v=v, side=side, sorter=sorter, **kargs) - + def sort(self, axis=-1, kind='quicksort', order=None, **kargs): """ Sort an array, in-place. """ return self.backend.sort(a=self, axis=axis, kind=kind, order=order, **kargs) - + def squeeze(self, axis=None, **kargs): """ Remove single-dimensional entries from the shape of a. """ return self.backend.squeeze(a=self, axis=axis, **kargs) - + def std(self, axis=None, dtype=None, out=None, ddof=0, **kargs): """ Returns the standard deviation of the array elements along given axis. """ - return self.backend.std(a=self, axis=axis, dtype=dtype, out=out, + return self.backend.std(a=self, axis=axis, dtype=dtype, out=out, ddof=ddof) - + def sum(self, axis=None, dtype=None, out=None, **kargs): """ Return the sum of the array elements over the given axis. @@ -691,18 +689,18 @@ class Array(object): Return a view of the array with axis1 and axis2 interchanged. """ return self.backend.swapaxes(axis1=axis1, axis2=axis2, **kargs) - + def trace(self, offset=0, axis1=0, axis2=1, dtype=None, out=None, **kargs): """ Return the sum along diagonals of the array. """ - return self.backend.trace(a=self, offset=offset, + return self.backend.trace(a=self, offset=offset, axis1=axis1, axis2=axis2, dtype=dtype, out=out, **kargs) - + def transpose(self, axes=None, **kargs): """ Returns a view of the array with axes transposed. - """ + """ return self.backend.transpose(a=self, axes=axes, **kargs) def var(self, axis=None, dtype=None, out=None, ddof=0, **kargs): @@ -710,7 +708,7 @@ class Array(object): Returns the variance of the array elements, along given axis. """ return self.backend.var(a=self, axis=axis, dtype=dtype, out=out, ddof=ddof, **kargs) - + ## Array restricted methods def setflags(self, write=None, align=None, uic=None): @@ -720,13 +718,13 @@ class Array(object): msg='{}::set_flags() should not be called.' msg=msg.format(self.__class__.__name__) raise RuntimeError(msg) - + ## Array specific unimplemented methods def tofile(self, fid, sep='', format='%s', **kargs): """ Write array to a file as text or binary (default). - This is a convenience function for quick storage of array data. + This is a convenience function for quick storage of array data. Information on endianness and precision is lost. """ self.__class__.not_implemented_yet('tofile') @@ -780,7 +778,7 @@ class Array(object): New view of array with the same data. """ self._not_implemented_yet('view') - def astype(self, dtype, order=MemoryOrdering.SAME_ORDER, + def astype(self, dtype, order=MemoryOrdering.SAME_ORDER, casting='unsafe', subok=True, copy=True, **kargs): """ Copy of the array, cast to a specified type. @@ -793,7 +791,7 @@ class Array(object): """ self._not_implemented_yet('tobytes') - + # logical operators def __eq__(self, other): return self.backend.equal(self, other) @@ -830,7 +828,7 @@ class Array(object): return self.backend.divide(self, other) def __mod__(self, other): return self.backend.mod(self, other) - + def __and__ (self, other): return self.backend.bitwise_and(self,other) def __xor__ (self, other): @@ -856,7 +854,7 @@ class Array(object): return self.backend.divide(other, self) def __rmod__(self, other): return self.backend.mod(other, self) - + def __rand__ (other, self): return self.backend.bitwise_and(other, self) def __rxor__ (other, self): @@ -867,7 +865,7 @@ class Array(object): return self.backend.left_shift(other, self) def __rrshift__ (other, self): return self.backend.right_shift(other, self) - + def __iadd__(self, other): return self.backend.add(self, other, out=self) def __isub__(self, other): @@ -883,7 +881,7 @@ class Array(object): def __imod__(self, other): return self.backend.mod(self, other, out=self) - + def __str__(self): return self._handle.__str__() def __repr__(self): diff --git a/hysop/core/arrays/array_backend.py b/hysop/core/arrays/array_backend.py index 82b3812be7ccf85c92bc05de1aedd809892ffe7f..07fb46176d8ab34e68b3094031ec11440cbf3700 100644 --- a/hysop/core/arrays/array_backend.py +++ b/hysop/core/arrays/array_backend.py @@ -11,7 +11,7 @@ from hysop.tools.numerics import is_fp, is_complex, match_float_type, \ match_complex_type, complex_to_float_dtype from hysop.core.memory.allocator import AllocatorBase -class ArrayBackend(TaggedObject): +class ArrayBackend(TaggedObject, metaclass=ABCMeta): """ Interface of an abstract array backend. An array backend is a numpy work-alike collection of functions that @@ -69,8 +69,6 @@ class ArrayBackend(TaggedObject): explicit message through the _not_implemented_yet method. """ - __metaclass__ = ABCMeta - __registered_backends = {} """ Contains all registered backends. diff --git a/hysop/core/graph/allocator.py b/hysop/core/graph/allocator.py index c8d3f7a24eee0e172c5ff62b4452295913183837..08cbe0b85e27550b0beecc47cf83347bd436eeb0 100644 --- a/hysop/core/graph/allocator.py +++ b/hysop/core/graph/allocator.py @@ -6,8 +6,7 @@ from hysop.tools.types import check_instance from hysop.core.arrays.all import ArrayBackend, HostArrayBackend, OpenClArrayBackend -class MemoryRequestsAllocator(object): - __metaclass__ = ABCMeta +class MemoryRequestsAllocator(object, metaclass=ABCMeta): @classmethod @not_implemented diff --git a/hysop/core/graph/computational_graph.py b/hysop/core/graph/computational_graph.py index 7f5226ffbcaa550fca52de6834f015273e8dec05..d8a5eba08c01846028b2aaf699380e476b969cd2 100644 --- a/hysop/core/graph/computational_graph.py +++ b/hysop/core/graph/computational_graph.py @@ -20,13 +20,11 @@ from hysop.core.mpi import main_rank from abc import ABCMeta, abstractmethod -class ComputationalGraph(ComputationalGraphNode): +class ComputationalGraph(ComputationalGraphNode, metaclass=ABCMeta): """ Interface of an abstract graph of continuous operators (ie. a computational graph). """ - __metaclass__ = ABCMeta - __FORCE_REPORTS__ = False @debug diff --git a/hysop/core/graph/computational_node.py b/hysop/core/graph/computational_node.py index 160db9b45ac5d327f8b7e71f5ddf446133365357..bd394fa0682682c62e500fc18264a673b7463492 100644 --- a/hysop/core/graph/computational_node.py +++ b/hysop/core/graph/computational_node.py @@ -49,13 +49,11 @@ def topology_handled(f): return _check -class ComputationalGraphNode(OperatorBase): +class ComputationalGraphNode(OperatorBase, metaclass=ABCMeta): """ Interface of an abstract computational graph node. """ - __metaclass__ = ABCMeta - @debug def __init__(self, input_fields=None, output_fields=None, input_params=None, output_params=None, diff --git a/hysop/core/graph/computational_operator.py b/hysop/core/graph/computational_operator.py index b71d3bc4c53634d244aaf5061ad3998f726b5266..afccabccc2afb225a3e4276396e32034db064ad2 100644 --- a/hysop/core/graph/computational_operator.py +++ b/hysop/core/graph/computational_operator.py @@ -9,7 +9,7 @@ from hysop.topology.topology_descriptor import TopologyDescriptor from hysop.fields.field_requirements import DiscreteFieldRequirements from abc import ABCMeta -class ComputationalGraphOperator(ComputationalGraphNode): +class ComputationalGraphOperator(ComputationalGraphNode, metaclass=ABCMeta): """ Interface of an abstract computational graph operator. An operator is a single graph node with its own inputs and outputs. @@ -119,8 +119,6 @@ class ComputationalGraphOperator(ComputationalGraphNode): or at least, a child class of hysop.core.graph.computational_graph.ComputationalGraph. """ - __metaclass__ = ABCMeta - @debug def __init__(self, input_fields=None, output_fields=None, **kwds): """ diff --git a/hysop/core/graph/continuous.py b/hysop/core/graph/continuous.py index ae2e358bc73b519e02a706555795024111979d3c..75a4d5ec994459a1554e4ce813c985b9566fd823 100755 --- a/hysop/core/graph/continuous.py +++ b/hysop/core/graph/continuous.py @@ -17,16 +17,15 @@ from hysop.parameters.parameter import Parameter import hysop.tools.io_utils as io -class OperatorBase(TaggedObject): +class OperatorBase(TaggedObject, metaclass=ABCMeta): """ Abstract interface to continuous operators. """ - __metaclass__ = ABCMeta @debug def __init__(self, name, fields, tensor_fields, parameters, - mpi_params=None, - io_params=False, + mpi_params=None, + io_params=False, **kwds): """ Parameters @@ -56,7 +55,7 @@ class OperatorBase(TaggedObject): """ super(OperatorBase,self).__init__(tagged_cls=OperatorBase, tag_prefix='node', **kwds) - + check_instance(fields, tuple, values=ScalarField) check_instance(tensor_fields, tuple, values=TensorField) check_instance(parameters, tuple, values=Parameter) @@ -66,7 +65,7 @@ class OperatorBase(TaggedObject): for tfield in tensor_fields: for field in tfield: assert field in fields - + self.name = name self.fields = fields self.tensor_fields = tensor_fields @@ -80,14 +79,14 @@ class OperatorBase(TaggedObject): def _get_profiling_info(self): """Collect profiling informations of profiled attributes.""" pass - + def profiler_report(self): """Update profiler statistics and print report.""" if not __PROFILE__: return self._profiler.summarize() print(str(self._profiler)) - + def _set_io(self): """ Initialize the io params. @@ -118,7 +117,7 @@ class OperatorBase(TaggedObject): """ Initialize the mpi context, depending on local fields, domain and so on. - + Notes ----- This function is private and must not be called by @@ -127,22 +126,22 @@ class OperatorBase(TaggedObject): """ if len(self.fields) > 0: self.domain = self.fields[0].domain - + # Check if all fields have the same domain for field in self.fields: assert field.domain is self.domain, 'All fields of the operator\ must be defined on the same domain.' - + # Set/check mpi context if (self.mpi_params is None): self.mpi_params = MPIParams(comm=self.domain.task_comm, task_id=self.domain.current_task()) - + else: if (self.mpi_params is None): self.mpi_params = default_mpi_params() self.domain = None - + self._profiler = Profiler(obj=self, comm=self.mpi_params.comm) def short_description(self): diff --git a/hysop/core/graph/node_generator.py b/hysop/core/graph/node_generator.py index 070e8902f4086ff92242fe1c06701c5d79a1eae7..402f61facb655b41d06a826934980ff7ea30e4da 100644 --- a/hysop/core/graph/node_generator.py +++ b/hysop/core/graph/node_generator.py @@ -4,13 +4,11 @@ from hysop.tools.decorators import debug from hysop.tools.types import first_not_None from hysop.core.graph.computational_node import ComputationalGraphNode -class ComputationalGraphNodeGenerator(object): +class ComputationalGraphNodeGenerator(object, metaclass=ABCMeta): """ A class that can generate multiple hysop.core.graph.ComputationalGraphNode. """ - __metaclass__ = ABCMeta - @debug def __init__(self, candidate_input_tensors, candidate_output_tensors, name=None, pretty_name=None, diff --git a/hysop/core/memory/allocator.py b/hysop/core/memory/allocator.py index 098af43f77836009a39619629ac58d8a9ce672e7..4ef44783fa384bd7378a45df6cb179a2efc23677 100644 --- a/hysop/core/memory/allocator.py +++ b/hysop/core/memory/allocator.py @@ -7,13 +7,11 @@ from hysop.tools.units import bytes2str from hysop.tools.types import first_not_None from hysop.tools.handle import TaggedObject -class AllocatorBase(TaggedObject): +class AllocatorBase(TaggedObject, metaclass=ABCMeta): """ Base class for allocators. """ - __metaclass__=ABCMeta - is_deferred=False def __init__(self, verbose, **kwds): diff --git a/hysop/core/memory/mempool.py b/hysop/core/memory/mempool.py index b3dcd7c9eb0e0abcd1fa0d6b676fef84d2933e7a..7b0ffa361b4a5b3aa7ddbbf9a15d645026c50fd6 100644 --- a/hysop/core/memory/mempool.py +++ b/hysop/core/memory/mempool.py @@ -24,13 +24,11 @@ else: return p-1 -class MemoryPool(object): +class MemoryPool(object, metaclass=ABCMeta): """ pyopencl/pycuda like memory pool extended to be compatible for all backends. """ - __metaclass__ = ABCMeta - def __init__(self, name, allocator, max_alloc_bytes=None, mantissa_bits=4, verbose=None, **kwds): """ diff --git a/hysop/domain/domain.py b/hysop/domain/domain.py index 277c95371bb5a3700b7ac528e3630511a81b7e32..a7f696db40e31c50893c3c39e84e1df7d0b39352 100644 --- a/hysop/domain/domain.py +++ b/hysop/domain/domain.py @@ -15,9 +15,8 @@ from hysop.symbolic.frame import SymbolicFrame from hysop.deps import hashlib, np -class DomainView(TaggedObjectView): +class DomainView(TaggedObjectView, metaclass=ABCMeta): """Abstract base class for views on domains. """ - __metaclass__ = ABCMeta __slots__ = ('_domain', '_topology_state') @@ -169,9 +168,8 @@ class DomainView(TaggedObjectView): frame = property(_get_frame) -class Domain(RegisteredObject): +class Domain(RegisteredObject, metaclass=ABCMeta): """Abstract base class for the description of physical domains. """ - __metaclass__ = ABCMeta @debug def __new__(cls, dim, parent_comm=None, proc_tasks=None, **kwds): diff --git a/hysop/fields/discrete_field.py b/hysop/fields/discrete_field.py index 1490a15a88cf54eb1ce05e4d2a67137514ef9370..aafa62d9994c11db36448b5fa1a490de7221ecd9 100644 --- a/hysop/fields/discrete_field.py +++ b/hysop/fields/discrete_field.py @@ -23,18 +23,16 @@ from hysop.fields.continuous_field import Field, VectorField, TensorField, \ from hysop.domain.domain import DomainView from hysop.mesh.mesh import MeshView -class DiscreteScalarFieldViewContainerI(object): +class DiscreteScalarFieldViewContainerI(object, metaclass=ABCMeta): """ Common abstract interface for scalar and tensor-like container of discrete field views. """ - __metaclass__ = ABCMeta - @debug def __new__(cls, **kwds): return super(DiscreteScalarFieldViewContainerI, cls).__new__(cls, **kwds) - + @property def is_scalar(self): return (not self.is_tensor) @@ -73,7 +71,7 @@ class DiscreteScalarFieldViewContainerI(object): but including duplicate fields. """ return len(self.discrete_field_views()) - + def ids_to_components(self, ids): """Convert tensor coordinates into 1d offsets.""" check_instance(ids, tuple, values=(int,tuple), allow_none=True) @@ -412,13 +410,11 @@ class DiscreteScalarFieldViewContainerI(object): return self.long_description() -class DiscreteScalarFieldView(DiscreteScalarFieldViewContainerI, TaggedObjectView, VariableTag): +class DiscreteScalarFieldView(DiscreteScalarFieldViewContainerI, TaggedObjectView, VariableTag, metaclass=ABCMeta): """ View over a DiscreteScalarField (taking into account a topology state). """ - __metaclass__ = ABCMeta - __slots__ = ('_dfield', '_topology_state', '_topology_view', '_symbol') @property @@ -479,7 +475,7 @@ class DiscreteScalarFieldView(DiscreteScalarFieldViewContainerI, TaggedObjectVie return (obj is self) def __getitem__(self, slc): return self - + def discrete_field_views(self): return (self,) @@ -600,7 +596,7 @@ class DiscreteScalarFieldView(DiscreteScalarFieldViewContainerI, TaggedObjectVie memory_request_id = property(_get_memory_request_id) -class DiscreteScalarField(NamedScalarContainerI, TaggedObject): +class DiscreteScalarField(NamedScalarContainerI, TaggedObject, metaclass=ABCMeta): """ Discrete representation of scalar or vector fields, @@ -618,8 +614,6 @@ class DiscreteScalarField(NamedScalarContainerI, TaggedObject): depending on the discrete field topology backend and the ghost exchange strategy. """ - __metaclass__ = ABCMeta - @debug def __new__(cls, field, topology, register_discrete_field=True, name=None, pretty_name=None, @@ -728,7 +722,7 @@ class DiscreteTensorField(NamedTensorContainerI, DiscreteScalarFieldViewContaine Is also garanties that all fields shares the same domain, but contained discrete fields may be defined on different topologies. """ - + @property def is_tensor(self): return True diff --git a/hysop/fields/ghost_exchangers.py b/hysop/fields/ghost_exchangers.py index 75087be1275d7fc7b84bc90465c5e62ce0ce697b..014c3e6babe23b09c238907344527735df1a9de4 100644 --- a/hysop/fields/ghost_exchangers.py +++ b/hysop/fields/ghost_exchangers.py @@ -106,9 +106,8 @@ class LocalBoundaryExchanger(object): return exchange_ghosts -class GhostExchangerI(object): +class GhostExchangerI(object, metaclass=ABCMeta): """Abstract interface for a ghost exchanger.""" - __metaclass__ = ABCMeta @abstractmethod def exchange_ghosts(self, **kwds): diff --git a/hysop/mesh/mesh.py b/hysop/mesh/mesh.py index 4f30aa6003a63553b00f5b6a5cf9e27c54d72401..2b7993d7311299e973e5ae17ac1c0be83cd64441 100644 --- a/hysop/mesh/mesh.py +++ b/hysop/mesh/mesh.py @@ -17,16 +17,15 @@ from hysop.tools.decorators import debug from hysop.tools.types import check_instance from hysop.tools.handle import TaggedObject, TaggedObjectView -class MeshView(TaggedObjectView): +class MeshView(TaggedObjectView, metaclass=ABCMeta): """Abstract base class for views on meshes. """ - __metaclass__ = ABCMeta __slots__ = ('_mesh', '_topology_state') - + @debug def __new__(cls, mesh, topology_state, **kwds): return super(MeshView, cls).__new__(cls, obj_view=mesh, **kwds) - + @debug def __init__(self, mesh, topology_state, **kwds): """Initialize a MeshView.""" @@ -39,11 +38,11 @@ class MeshView(TaggedObjectView): def _get_mesh(self): """Return the original mesh on which the view is.""" return self._mesh - + def _get_topology_state(self): """Return the topology state""" return self._topology_state - + def _get_dim(self): """Return the dimension of the domain.""" return self._mesh._topology.domain.dim @@ -61,30 +60,29 @@ class MeshView(TaggedObjectView): def __str__(self): """Equivalent to self.long_description()""" return self.long_description() - + mesh = property(_get_mesh) topology_state = property(_get_topology_state) dim = property(_get_dim) -class Mesh(TaggedObject): +class Mesh(TaggedObject, metaclass=ABCMeta): """Abstract base class for local to process meshes.""" - __metaclass__ = ABCMeta def __new__(cls, topology, **kwds): return super(Mesh, cls).__new__(cls, **kwds) - + @debug def __init__(self, topology, **kwds): """Initialize a mesh.""" check_instance(topology, Topology) super(Mesh, self).__init__(tag_prefix='m', **kwds) self._topology = topology - + @abstractmethod def view(self, topology_state): """Return a view on this mesh using a topology state.""" pass - + def _get_topology(self): """Return a topology view on the original topology that defined this mesh.""" return self._topology diff --git a/hysop/numerics/fft/fft.py b/hysop/numerics/fft/fft.py index 494fb6550b4082ff1d1d9317362b08b7dfb63c7a..c54495669d0252bb8109006c7c4487c89d5fbd9b 100644 --- a/hysop/numerics/fft/fft.py +++ b/hysop/numerics/fft/fft.py @@ -89,7 +89,7 @@ class FFTQueueI(object): def execute(self, wait_for=None): """Execute all planned plans.""" pass - + @abstractmethod def __iadd__(self, *plans): """Add a plan to the queue.""" @@ -100,13 +100,12 @@ class FFTQueueI(object): return self.execute(**kwds) -class FFTPlanI(object): +class FFTPlanI(object, metaclass=ABCMeta): """ Common inteface for FFT plans. Basically just a functor that holds relevant data to execute a preconfigurated FFT-like tranform. """ - __metaclass__ = ABCMeta def __init__(self, verbose=__VERBOSE__): self.verbose = verbose @@ -119,14 +118,14 @@ class FFTPlanI(object): Return currently planned input array. """ pass - + @abstractmethod def output_array(self): """ Return currently planned output array. """ pass - + def setup(self, queue=None): """ Method that has to be called before any call to execute. @@ -136,11 +135,11 @@ class FFTPlanI(object): raise RuntimeError(msg) self._setup = True return self - + @property def required_buffer_size(self): """ - Return the required temporary buffer size in bytes to + Return the required temporary buffer size in bytes to compute the transform. """ assert self._setup @@ -151,7 +150,7 @@ class FFTPlanI(object): assert self._setup assert not self._allocated self._allocated = True - + @abstractmethod def execute(self): @@ -171,7 +170,7 @@ class FFTPlanI(object): self.execute(**kwds) -class FFTI(object): +class FFTI(object, metaclass=ABCMeta): """ Interface to compute local to process FFT-like transforms. Common inteface for all array backends, based on the numpy.fft interface. @@ -194,15 +193,15 @@ class FFTI(object): Other R2R transforms: DCT-IV and DCT-IV are only supported by the FFTW backend at this time. DCT-V to DCT-VIII and DST-V to DST-VII are not supported by any FFT backend. - + About floating point precision: - By default, both simple and double precision are supported. + By default, both simple and double precision are supported. numpy only supports double precision (simple precision is supported by casting). FFTW also supports long double precision. - + Normalization: The default normalization has the direct transforms unscaled and the inverse transform - is scaled by 1/N where N is the logical size of the transform. + is scaled by 1/N where N is the logical size of the transform. N should not to be confused with the physical size of the input arrays n: FFT, RFFT: N = n @@ -240,8 +239,7 @@ class FFTI(object): *Zero fill Those methods will be used by the n-dimensional planner. """ - __metaclass__ = ABCMeta - + __transform2fn = { TransformType.FFT: ('fft', {}), TransformType.IFFT: ('ifft', {}), @@ -264,9 +262,9 @@ class FFTI(object): TransformType.IDST_III: ('idst', {'type': 3}), TransformType.IDST_IV: ('idst', {'type': 4}), } - + @classmethod - def default_interface_from_backend(cls, backend, + def default_interface_from_backend(cls, backend, enable_opencl_host_buffer_mapping, **kwds): check_instance(backend, ArrayBackend) if (backend.kind is Backend.HOST): @@ -296,7 +294,7 @@ class FFTI(object): msg='Backend mismatch {} vs {}.' msg=msg.format(self.backend, backend) raise RuntimeError(msg) - + def get_transform(self, transform): check_instance(transform, TransformType) if (transform not in self.__transform2fn): @@ -308,7 +306,7 @@ class FFTI(object): fn = functools.partial(fn, **fkwds) return fn - def __init__(self, backend, + def __init__(self, backend, warn_on_allocation=True, error_on_allocation=False): """Initializes the interface and default supported real and complex types.""" @@ -325,7 +323,7 @@ class FFTI(object): self.backend = backend self.warn_on_allocation = warn_on_allocation self.error_on_allocation = error_on_allocation - + def allocate_output(self, out, shape, dtype): """Alocate output if required and check shape and dtype.""" if (out is None): @@ -342,7 +340,7 @@ class FFTI(object): assert out.dtype == dtype assert out.shape == shape return out - + @classmethod def default_interface(cls, **kwds): """Get the default FFT interface.""" @@ -399,17 +397,17 @@ class FFTI(object): out: array_like of np.complex64 or np.complex128 Complex output array of the same shape and dtype as the input. axis: int, optional - Axis over witch to compute the FFT. + Axis over witch to compute the FFT. Defaults to last axis. Returns ------- (shape, dtype) of the output array determined from the input array. - + Notes ----- N = a.shape[axis] - out[0] will contain the sum of the signal (zero-frequency term always real for + out[0] will contain the sum of the signal (zero-frequency term always real for real inputs). If N is even: @@ -439,9 +437,9 @@ class FFTI(object): out: array_like of np.complex64 or np.complex128 Complex output array of the same shape and dtype as the input. axis: int, optional - Axis over witch to compute the FFT. + Axis over witch to compute the FFT. Defaults to last axis. - + Returns ------- (shape, dtype, logical_size) of the output array determined from the input array. @@ -456,7 +454,7 @@ class FFTI(object): @abstractmethod def rfft(self, a, out, axis=-1, **kwds): """ - Compute the unscaled one-dimensional real to hermitian complex discrete Fourier + Compute the unscaled one-dimensional real to hermitian complex discrete Fourier Transform. Parameters @@ -468,17 +466,17 @@ class FFTI(object): out.shape[...] = a.shape[...] out.shape[axis] = a.shape[axis]//2 + 1 axis: int, optional - Axis over witch to compute the transform. + Axis over witch to compute the transform. Defaults to last axis. - + Returns ------- (shape, dtype) of the output array determined from the input array. - + Notes ----- - For real inputs there is no information in the negative frequency components that - is not already available from the positive frequency component because of the + For real inputs there is no information in the negative frequency components that + is not already available from the positive frequency component because of the Hermitian symmetry. N = out.shape[axis] = a.shape[axis]//2 + 1 @@ -504,7 +502,7 @@ class FFTI(object): @abstractmethod def irfft(self, a, out, n=None, axis=-1, **kwds): """ - Compute the one-dimensional hermitian complex to real discrete Fourier Transform + Compute the one-dimensional hermitian complex to real discrete Fourier Transform scaled by 1/N. Parameters @@ -521,22 +519,22 @@ class FFTI(object): Length of the transformed axis of the output. ie: n should be in [2*(a.shape[axis]-1), 2*(a.shape[axis]-1)+1] axis: int, optional - Axis over witch to compute the transform. + Axis over witch to compute the transform. Defaults to last axis. - + Notes ----- To get an odd number of output points, n or out must be specified. - + Returns ------- - (shape, dtype, logical_size) of the output array determined from the input array, + (shape, dtype, logical_size) of the output array determined from the input array, out and n. """ assert a.dtype in self.supported_ctypes cshape = a.shape rtype = complex_to_float_dtype(a.dtype) - + rshape_even, rshape_odd = list(a.shape), list(a.shape) rshape_even[axis] = 2*(cshape[axis]-1) rshape_odd[axis] = 2*(cshape[axis]-1) + 1 @@ -561,12 +559,12 @@ class FFTI(object): n = rshape[axis] else: rshape = rshape_odd - + rshape = tuple(rshape) logical_size = n assert rshape[axis] == logical_size return (rshape, rtype, logical_size) - + @abstractmethod def dct(self, a, out=None, type=2, axis=-1, **kwds): """ @@ -579,7 +577,7 @@ class FFTI(object): out: array_like Real output array of matching input type and shape. axis: int, optional - Axis over witch to compute the transform. + Axis over witch to compute the transform. Defaults to last axis. Returns ------- @@ -591,13 +589,13 @@ class FFTI(object): assert a.dtype == out.dtype assert np.array_equal(a.shape, out.shape) return (a.shape, a.dtype) - + @abstractmethod def idct(self, a, out=None, type=2, axis=-1, **kwds): """ Compute the one-dimensional Inverse Cosine Transform of specified type. - - Default scaling is 1/(2*N) for IDCT type (2,3,4) and + + Default scaling is 1/(2*N) for IDCT type (2,3,4) and 1/(2*N-2) for IDCT type 1. Parameters @@ -607,11 +605,11 @@ class FFTI(object): out: array_like Real output array of matching input type and shape. axis: int, optional - Axis over witch to compute the transform. + Axis over witch to compute the transform. Defaults to last axis. Returns ------- - (shape, dtype, inverse_type, logical_size) of the output array determined from the input + (shape, dtype, inverse_type, logical_size) of the output array determined from the input array. """ itype = [1,3,2,4][type-1] @@ -624,7 +622,7 @@ class FFTI(object): assert a.dtype == out.dtype assert np.array_equal(a.shape, out.shape) return (a.shape, a.dtype, itype, logical_size) - + @abstractmethod def dst(self, a, out=None, type=2, axis=-1, **kwds): """ @@ -637,7 +635,7 @@ class FFTI(object): out: array_like Real output array of matching input type and shape. axis: int, optional - Axis over witch to compute the transform. + Axis over witch to compute the transform. Defaults to last axis. Returns ------- @@ -655,7 +653,7 @@ class FFTI(object): """ Compute the one-dimensional Inverse Sine Transform of specified type. - Default scaling is 1/(2*N) for IDST type (2,3,4) and + Default scaling is 1/(2*N) for IDST type (2,3,4) and 1/(2*N+2) for IDST type 1. Parameters @@ -665,11 +663,11 @@ class FFTI(object): out: array_like Real output array of matching input type and shape. axis: int, optional - Axis over witch to compute the transform. + Axis over witch to compute the transform. Defaults to last axis. Returns ------- - (shape, dtype, inverse_type, logical_size) of the output array determined from the input + (shape, dtype, inverse_type, logical_size) of the output array determined from the input array. """ itype = [1,3,2,4][type-1] @@ -692,7 +690,7 @@ class FFTI(object): def plan_copy(self, tg, src, dst): """Plan a copy from src to dst.""" pass - + @abstractmethod def plan_accumulate(self, tg, src, dst): """Plan an accumulation from src into dst.""" @@ -707,7 +705,7 @@ class FFTI(object): def plan_fill_zeros(self, tg, a, slices): """Plan to fill every input slices of input array a with zeroes.""" pass - + @abstractmethod def plan_compute_energy(self, tg, fshape, src, dst, transforms, mutexes=None): """Plan to compute energy from src to energy.""" diff --git a/hysop/operator/adapt_timestep.py b/hysop/operator/adapt_timestep.py index b547e65382a45d6e81a66643352508259ae367b2..65e91de33adf39e47be1cd4fde59f1117aaebddd 100755 --- a/hysop/operator/adapt_timestep.py +++ b/hysop/operator/adapt_timestep.py @@ -15,8 +15,7 @@ from hysop.fields.continuous_field import Field from hysop.parameters import ScalarParameter, TensorParameter from hysop.core.mpi import MPI -class TimestepCriteria(ComputationalGraphOperator): - __metaclass__ = ABCMeta +class TimestepCriteria(ComputationalGraphOperator, metaclass=ABCMeta): @debug def __init__(self, parameter, input_params, output_params, diff --git a/hysop/operator/base/custom_symbolic_operator.py b/hysop/operator/base/custom_symbolic_operator.py index 408432613dc84f0854d736948b0dd372b430cd02..bba8012dfdb6db788b3c1733246c850f69acef3e 100644 --- a/hysop/operator/base/custom_symbolic_operator.py +++ b/hysop/operator/base/custom_symbolic_operator.py @@ -1264,11 +1264,10 @@ class SymbolicExpressionParser(object): i = symbols.index(expr) return symbols[axes[i]] -class CustomSymbolicOperatorBase(DirectionalOperatorBase): +class CustomSymbolicOperatorBase(DirectionalOperatorBase, metaclass=ABCMeta): """ Common implementation interface for custom symbolic (code generated) operators. """ - __metaclass__ = ABCMeta __default_method = { ComputeGranularity: 0, diff --git a/hysop/operator/base/derivative.py b/hysop/operator/base/derivative.py index 834416f58c9259745e2977787b7722f9a57a59f8..5b860814b449b6bb6a10b5ddba618af00e0a2c68 100644 --- a/hysop/operator/base/derivative.py +++ b/hysop/operator/base/derivative.py @@ -17,13 +17,11 @@ from hysop.fields.continuous_field import Field, ScalarField from hysop.operator.base.spectral_operator import SpectralOperatorBase -class SpaceDerivativeBase(object): +class SpaceDerivativeBase(object, metaclass=ABCMeta): """ Common implementation interface for derivative operators. """ - __metaclass__ = ABCMeta - @debug def __init__(self, F, dF, A=None, derivative=None, direction=None, diff --git a/hysop/operator/base/enstrophy.py b/hysop/operator/base/enstrophy.py index 1305461cad775707c2b004c18d5b7ad629306b41..f2c401b0548c3e6451d54cf6a7f76ba12b1d354f 100644 --- a/hysop/operator/base/enstrophy.py +++ b/hysop/operator/base/enstrophy.py @@ -9,13 +9,11 @@ from hysop.core.memory.memory_request import MemoryRequest from hysop.topology.cartesian_descriptor import CartesianTopologyDescriptors from hysop.parameters.scalar_parameter import ScalarParameter -class EnstrophyBase(object): +class EnstrophyBase(object, metaclass=ABCMeta): """ Common implementation interface for enstrophy. """ - __metaclass__ = ABCMeta - @debug def __init__(self, vorticity, enstrophy, WdotW, rho, rho_0, variables, name=None, pretty_name=None, **kwds): diff --git a/hysop/operator/base/external_force.py b/hysop/operator/base/external_force.py index 7cfb017985d830c467651d9b25a5a3bb7213a419..59d9cb5adcd6c9620d6755c54d0ad6ddaecd459e 100644 --- a/hysop/operator/base/external_force.py +++ b/hysop/operator/base/external_force.py @@ -15,9 +15,8 @@ from hysop.parameters.tensor_parameter import TensorParameter from hysop.parameters.scalar_parameter import ScalarParameter from hysop.tools.interface import NamedObjectI -class ExternalForce(NamedObjectI): +class ExternalForce(NamedObjectI, metaclass=ABCMeta): """Interface to implement a custom external force.""" - __metaclass__ = ABCMeta def __init__(self, name, dim, Fext, **kwds): super(ExternalForce, self).__init__(name=name, **kwds) diff --git a/hysop/operator/base/integrate.py b/hysop/operator/base/integrate.py index bfbda81770bf998931a54c9d320c2cfd6534721e..2a95335576cffc9f5c7e04b32bf80d9371848dc7 100644 --- a/hysop/operator/base/integrate.py +++ b/hysop/operator/base/integrate.py @@ -10,13 +10,11 @@ from hysop.topology.cartesian_descriptor import CartesianTopologyDescriptors from hysop.parameters.scalar_parameter import ScalarParameter, TensorParameter -class IntegrateBase(object): +class IntegrateBase(object, metaclass=ABCMeta): """ Common implementation interface for field integration. """ - __metaclass__ = ABCMeta - @debug def __init__(self, field, variables, name=None, pretty_name=None, cst=1, diff --git a/hysop/operator/base/memory_reordering.py b/hysop/operator/base/memory_reordering.py index 1d6c4c9019a52a33181979a02bce56115b58830c..c07791e5cf586468e31f5b17660f9a94e4608233 100644 --- a/hysop/operator/base/memory_reordering.py +++ b/hysop/operator/base/memory_reordering.py @@ -8,13 +8,11 @@ from hysop.fields.continuous_field import ScalarField from hysop.core.memory.memory_request import MemoryRequest from hysop.topology.cartesian_descriptor import CartesianTopologyDescriptors -class MemoryReorderingBase(object): +class MemoryReorderingBase(object, metaclass=ABCMeta): """ Common implementation interface for memory reordering operators. """ - __metaclass__ = ABCMeta - @debug def __init__(self, input_field, output_field, variables, target_memory_order, name=None, pretty_name=None, diff --git a/hysop/operator/base/redistribute_operator.py b/hysop/operator/base/redistribute_operator.py index aa5ad03f312d515ae93c37985494e904ade26269..0a7ebdf3a7aa5f692b9c6a4eaf2ea9d0ff31bee7 100644 --- a/hysop/operator/base/redistribute_operator.py +++ b/hysop/operator/base/redistribute_operator.py @@ -8,23 +8,21 @@ from hysop.core.graph.computational_operator import ComputationalGraphOperator from hysop.topology.topology import Topology from hysop.fields.continuous_field import ScalarField -class RedistributeOperatorBase(ComputationalGraphOperator): +class RedistributeOperatorBase(ComputationalGraphOperator, metaclass=ABCMeta): """ Abstract interface to redistribute operators. """ - - __metaclass__ = ABCMeta @classmethod @not_implemented def can_redistribute(cls, source_topo, target_topo): """ - Return true if this RedistributeOperatorBase can be applied + Return true if this RedistributeOperatorBase can be applied to redistribute a variable from source_topo to target_topo, else return False. """ pass - + @classmethod def supported_backends(cls): """ @@ -41,7 +39,7 @@ class RedistributeOperatorBase(ComputationalGraphOperator): the variable to be distributed source_topo: :class:`~hysop.topology.topology.Topology` source mesh topology - target_topo: :class:`~hysop.topology.topology.Topology` + target_topo: :class:`~hysop.topology.topology.Topology` target mesh topology name: str, optional name of this operator @@ -51,7 +49,7 @@ class RedistributeOperatorBase(ComputationalGraphOperator): check_instance(variable, ScalarField) check_instance(source_topo, Topology) check_instance(target_topo, Topology) - + input_fields = {variable: source_topo} output_fields = {variable: target_topo} @@ -69,41 +67,41 @@ class RedistributeOperatorBase(ComputationalGraphOperator): super(RedistributeOperatorBase, self).__init__( name=name, pretty_name=pretty_name, - input_fields=input_fields, + input_fields=input_fields, output_fields=output_fields, **kwds) - + self.variable = variable self.dtype = variable.dtype self.source_topo = source_topo self.target_topo = target_topo - + # Set domain and mpi params self._set_domain_and_tasks() @debug def get_field_requirements(self): reqs = super(RedistributeOperatorBase,self).get_field_requirements() - + for field in self.input_fields: - _, req = reqs.get_input_requirement(field) + _, req = reqs.get_input_requirement(field) req.axes = None - req.memory_order = None - + req.memory_order = None + for field in self.output_fields: - _, req = reqs.get_output_requirement(field) + _, req = reqs.get_output_requirement(field) req.axes = None - req.memory_order = None + req.memory_order = None return reqs - + @debug def get_node_requirements(self): from hysop.core.graph.node_requirements import OperatorRequirements reqs = super(RedistributeOperatorBase, self).get_node_requirements() reqs.enforce_unique_topology_shape=False return reqs - + @debug def initialize(self, topgraph_method=None, **kwds): super(RedistributeOperatorBase,self).initialize(topgraph_method) @@ -127,8 +125,8 @@ class RedistributeOperatorBase(ComputationalGraphOperator): return True def get_preserved_input_fields(self): return set(self.input_fields.keys()) - - + + def available_methods(self): return {} def default_method(self): diff --git a/hysop/operator/base/transpose_operator.py b/hysop/operator/base/transpose_operator.py index bd91896df16c2bcbb25e089450702082132f64a3..e08f3fcd4707c9e2562bc06151380f5660af9ad7 100644 --- a/hysop/operator/base/transpose_operator.py +++ b/hysop/operator/base/transpose_operator.py @@ -9,31 +9,29 @@ from hysop.fields.continuous_field import ScalarField from hysop.core.memory.memory_request import MemoryRequest from hysop.topology.cartesian_descriptor import CartesianTopologyDescriptors -class TransposeOperatorBase(object): +class TransposeOperatorBase(object, metaclass=ABCMeta): """ Common implementation interface for transposition operators. """ - __metaclass__ = ABCMeta - @debug def __init__(self, input_field, output_field, variables, axes, name=None, pretty_name=None, **kwds): """ Initialize a transposition operator operating on CartesianTopologyDescriptors. - + input_field: ScalarField Input continuous scalar field to be transposed, at least 2D. output_field: ScalarField Output continuous scalar field where the result is stored Transposed shape should match the input. - output_field can be the same as input_field resulting in + output_field can be the same as input_field resulting in an inplace transposition. variables: dict Dictionary of fields as keys and CartesianTopologyDescriptors as values. Should contain input and output field. axes: tuple or list of ints - Permutation of axes in numpy notations + Permutation of axes in numpy notations Axe dim-1 is the contiguous axe, axe 0 has the greatest stride in memory. kwds: dict Base class keyword arguments. @@ -54,7 +52,7 @@ class TransposeOperatorBase(object): input_fields = { input_field: variables[input_field] } output_fields = { output_field: variables[output_field] } - + saxes = ''.join([DirectionLabels[i] for i in axes]).lower() default_name = 'T{}_{}'.format(saxes, input_field.name) default_pname = u'T{}_{}'.format(saxes, input_field.pretty_name.decode('utf-8')) @@ -67,7 +65,7 @@ class TransposeOperatorBase(object): super(TransposeOperatorBase, self).__init__( input_fields=input_fields, - output_fields=output_fields, + output_fields=output_fields, name=name, pretty_name=pname, **kwds) @@ -76,24 +74,24 @@ class TransposeOperatorBase(object): self.nb_components = nb_components self.dim = dim self.axes = axes - + @debug def get_node_requirements(self): from hysop.core.graph.node_requirements import OperatorRequirements reqs = super(TransposeOperatorBase, self).get_node_requirements() reqs.enforce_unique_transposition_state=False return reqs - + def output_topology_state(self, output_field, input_topology_states): ostate = super(TransposeOperatorBase,self).output_topology_state( - output_field=output_field, + output_field=output_field, input_topology_states=input_topology_states) assert len(input_topology_states)==1 istate = input_topology_states.values()[0] axes = self.axes ostate.axes = tuple( istate.axes[i] for i in axes ) return ostate - + @debug def discretize(self): if self.discretized: @@ -102,17 +100,17 @@ class TransposeOperatorBase(object): self.din = self.get_input_discrete_field(self.input_field) self.dout = self.get_output_discrete_field(self.output_field) self.is_inplace = (self.din.dfield is self.dout.dfield) - + @debug def get_work_properties(self): requests = super(TransposeOperatorBase,self).get_work_properties() - + if self.is_inplace: request = MemoryRequest.empty_like(a=self.dout, nb_components=1) requests.push_mem_request('tmp', request) - + return requests - + @debug def setup(self, work): super(TransposeOperatorBase,self).setup(work) @@ -120,16 +118,16 @@ class TransposeOperatorBase(object): raise ValueError('work is None.') if self.is_inplace: self.dtmp, = work.get_buffer(self, 'tmp') - - + + @staticmethod def get_preferred_axes(src_topo, dst_topo, candidate_axes): """ - Return preferred transposition scheme (performance-wise) + Return preferred transposition scheme (performance-wise) given source and destination topology and possible candidate transposition schemes. - - Candidate_axes is a dictionnary containing permutation + + Candidate_axes is a dictionnary containing permutation as keys (tuple of ints), and target transposition state (hysop.constants.TranspositionState) as values. @@ -144,13 +142,13 @@ class TransposeOperatorBase(object): assert candidate_axes, 'candidate axes is None or empty.' dim = len(candidate_axes.keys()[0]) tstates = TranspositionState[dim] - check_instance(candidate_axes, dict, keys=tuple, + check_instance(candidate_axes, dict, keys=tuple, values=(tstates, type(None))) - + if tstates.default in candidate_axes.values(): idx = candidate_axes.values().index(tstates.default) else: idx = 0 - + axes = candidate_axes.keys()[idx] return axes diff --git a/hysop/operator/directional/directional.py b/hysop/operator/directional/directional.py index c3b7af6853dfd11fdcb7b3070e8459a15c88395b..6ef83b3a38c52509832534266acfc2ff75f32bba 100644 --- a/hysop/operator/directional/directional.py +++ b/hysop/operator/directional/directional.py @@ -123,12 +123,11 @@ class DirectionalOperatorGeneratorI(object): return self.generate_direction(i, dt_coeff) -class DirectionalOperatorGenerator(DirectionalOperatorGeneratorI): +class DirectionalOperatorGenerator(DirectionalOperatorGeneratorI, metaclass=ABCMeta): """ Simple ComputationalGraphNodeGenerator to generate an operator in multiple directions. """ - __metaclass__ = ABCMeta @debug def __init__(self, operator, base_kwds, @@ -286,14 +285,12 @@ class DirectionalOperatorGenerator(DirectionalOperatorGeneratorI): return {} -class DirectionalOperatorFrontend(DirectionalOperatorGenerator): +class DirectionalOperatorFrontend(DirectionalOperatorGenerator, metaclass=ABCMeta): """ Frontend facility for directional operators that provide multiple implementations. """ - __metaclass__ = ABCMeta - @debug def __init__(self, implementation=None, base_kwds=None, **op_kwds): """ diff --git a/hysop/operator/hdf_io.py b/hysop/operator/hdf_io.py index 0334725246d1f8cb1b7d16956bed31b2b6b34639..a62bd7a8e38401a7f53e2cef8573f62ff11a8977 100755 --- a/hysop/operator/hdf_io.py +++ b/hysop/operator/hdf_io.py @@ -28,14 +28,12 @@ from hysop.core.memory.memory_request import MemoryRequest from hysop.topology.topology_descriptor import TopologyDescriptor -class HDF_IO(ComputationalGraphOperator): +class HDF_IO(ComputationalGraphOperator, metaclass=ABCMeta): """ Abstract interface to read/write from/to hdf files, for hysop fields. """ - __metaclass__ = ABCMeta - @classmethod def supported_backends(cls): """ diff --git a/hysop/parameters/parameter.py b/hysop/parameters/parameter.py index f09da60d8fd6dba4678cd57cf60cee96ed3925b0..33c42064a1913ee15a10da4d32e1c65fa5c3db1a 100644 --- a/hysop/parameters/parameter.py +++ b/hysop/parameters/parameter.py @@ -10,12 +10,11 @@ from hysop.tools.handle import TaggedObject from hysop.tools.variable import Variable, VariableTag -class Parameter(TaggedObject, VariableTag): +class Parameter(TaggedObject, VariableTag, metaclass=ABCMeta): """ A parameter is a value of a given type that may change value as simulation advances. Parameters are only available on the host backend. """ - __metaclass__ = ABCMeta def __new__(cls, name, parameter_types, initial_value=None, allow_None=False, diff --git a/hysop/tools/enum.py b/hysop/tools/enum.py index 6ca539c068034cf7f4ae55b306b3ee8baef347b6..5dabce0925a19249c9f8bb50d657f4de9f6a216d 100644 --- a/hysop/tools/enum.py +++ b/hysop/tools/enum.py @@ -205,8 +205,7 @@ class EnumFactory(object): '__repr__':__repr__} mcls = type(name+'MetaEnum', (EnumFactory.MetaEnum,), mcls_dic) - class Enum(base_cls): - __metaclass__=mcls + class Enum(base_cls, metaclass=mcls): def __init__(self, field=sorted(fields.keys())[0]): assert isinstance(field, str) and len(field)>0 self._field = field diff --git a/hysop/tools/handle.py b/hysop/tools/handle.py index c51ea8c81d901657e91de5ce17b9237edd906078..5a51e25e39c3497e605fa6c43f7d04a7d755ef01 100644 --- a/hysop/tools/handle.py +++ b/hysop/tools/handle.py @@ -6,11 +6,9 @@ from hysop.tools.types import to_tuple, first_not_None from hysop.tools.sympy_utils import subscript from hysop.core.mpi import MPI -class TaggedObjectView(object): +class TaggedObjectView(object, metaclass=ABCMeta): """View on a TaggedObject, just forwards tag and id for views.""" - __metaclass__ = ABCMeta - @debug def __new__(cls, obj_view=None, **kwds): obj = super(TaggedObjectView, cls).__new__(cls, **kwds) @@ -76,7 +74,7 @@ class TaggedObjectView(object): return self.full_tag -class TaggedObject(object): +class TaggedObject(object, metaclass=ABCMeta): """ Generic class to count object instances and associate a tag to it. A tag is basically the id of the object instance formatted to a string. @@ -84,8 +82,6 @@ class TaggedObject(object): object id (for logging or debug purposes). """ - __metaclass__ = ABCMeta - # Counter of instances to set a unique id for each object. __ids = {} @@ -376,13 +372,11 @@ class RegisteredObject(TaggedObject): obj_initialized = property(__get_obj_initialized) -class Handle(object): +class Handle(object, metaclass=ABCMeta): """ Generic class to encapsulate various objects ('handles'). """ - __metaclass__ = ABCMeta - @classmethod @not_implemented def handle_cls(cls): diff --git a/hysop/tools/interface.py b/hysop/tools/interface.py index 3722820a076d08c4f7c5e21308d84799474fcba5..d49604237fbe4ea0ecf133829678ae600af2eb17 100644 --- a/hysop/tools/interface.py +++ b/hysop/tools/interface.py @@ -4,9 +4,8 @@ from hysop.tools.types import check_instance, first_not_None, to_tuple from hysop.tools.numpywrappers import npw -class SymbolContainerI(object): - __metaclass__ = ABCMeta - +class SymbolContainerI(object, metaclass=ABCMeta): + def _get_symbol(self): """ Return a Symbol that can be used to compute symbolic expressions @@ -14,14 +13,13 @@ class SymbolContainerI(object): """ assert hasattr(self, '_symbol'), 'Symbol has not been defined.' return self._symbol - + symbol = property(_get_symbol) s = property(_get_symbol) -class NamedObjectI(object): - __metaclass__ = ABCMeta - +class NamedObjectI(object, metaclass=ABCMeta): + def __new__(cls, name, pretty_name=None, latex_name=None, var_name=None, **kwds): """ Create an abstract named object that contains a symbolic value. @@ -33,21 +31,21 @@ class NamedObjectI(object): kwds: dict Keywords arguments for base class. """ - + obj = super(NamedObjectI, cls).__new__(cls, **kwds) - obj.rename(name=name, pretty_name=pretty_name, + obj.rename(name=name, pretty_name=pretty_name, latex_name=latex_name, var_name=var_name) return obj - + def rename(self, name, pretty_name=None, latex_name=None, var_name=None): """Change the names of this object.""" check_instance(name, str) check_instance(pretty_name, (str,unicode), allow_none=True) check_instance(latex_name, str, allow_none=True) - + pretty_name = first_not_None(pretty_name, name) latex_name = first_not_None(latex_name, name) - + if isinstance(pretty_name, unicode): pretty_name = pretty_name.encode('utf-8') check_instance(pretty_name, str) @@ -55,7 +53,7 @@ class NamedObjectI(object): self._name = name self._pretty_name = pretty_name self._latex_name = latex_name - + def _get_name(self): """Return the name of this field.""" return self._name @@ -65,10 +63,10 @@ class NamedObjectI(object): def _get_latex_name(self): """Return the latex name of this field.""" return self._latex_name - + def __str__(self): return self.long_description() - + @abstractmethod def short_description(self): """Short description of this field as a string.""" @@ -89,18 +87,18 @@ class NamedScalarContainerI(NamedObjectI, SymbolContainerI): def ndim(self): """Number of dimensions of this this tensor.""" return 0 - + def _get_var_name(self): """Return the variable name of this field.""" return self._var_name - - def rename(self, name, pretty_name=None, + + def rename(self, name, pretty_name=None, latex_name=None, var_name=None): """Change the names of this object.""" - super(NamedScalarContainerI, self).rename(name=name, + super(NamedScalarContainerI, self).rename(name=name, pretty_name=pretty_name, latex_name=latex_name) self.check_and_set_varname(first_not_None(var_name, self._name)) - + def check_and_set_varname(self, var_name): check_instance(var_name, str, allow_none=True) @@ -111,15 +109,15 @@ class NamedScalarContainerI(NamedObjectI, SymbolContainerI): if c in var_name: raise RuntimeError(msg) self._var_name = var_name - + def nd_iter(self): """Return an nd-indexed iterator of contained objects.""" yield ((1,), self) - + def __iter__(self): """Return an iterator on unique scalar objects.""" return (self,).__iter__() - + def __tuple__(self): """ Fix hysop.tools/type.to_tuple for FieldContainers, @@ -130,10 +128,10 @@ class NamedScalarContainerI(NamedObjectI, SymbolContainerI): def __contains__(self, obj): """Check if a scalar object is contained in self.""" return (obj is self) - + def __getitem__(self, slc): return self - + var_name = property(_get_var_name) @@ -143,14 +141,14 @@ class NamedTensorContainerI(NamedObjectI, SymbolContainerI): obj = super(NamedTensorContainerI, cls).__new__(cls, **kwds) obj._contained_objects = contained_objects return obj - - def rename(self, name, pretty_name=None, + + def rename(self, name, pretty_name=None, latex_name=None, var_name=None): """Change the names of this object.""" assert (var_name is None), 'Tensor do not have variable names.' - super(NamedTensorContainerI, self).rename(name=name, + super(NamedTensorContainerI, self).rename(name=name, pretty_name=pretty_name, latex_name=latex_name) - + @property def size(self): """Full size of this container as if it was a 1D tensor.""" @@ -160,7 +158,7 @@ class NamedTensorContainerI(NamedObjectI, SymbolContainerI): def shape(self): """Shape of this tensor.""" return self._contained_objects.shape - + @property def ndim(self): """Number of dimensions of this this tensor.""" @@ -184,11 +182,11 @@ class NamedTensorContainerI(NamedObjectI, SymbolContainerI): """Return an nd-indexed iterator of contained objects.""" for idx in npw.ndindex(*self._contained_objects.shape): yield (idx, self._contained_objects[idx]) - + def __iter__(self): """Return an iterator on unique scalar objects.""" return self._contained_objects.ravel().__iter__() - + def __tuple__(self): """ Fix hysop.tools/type.to_tuple for FieldContainers, @@ -199,7 +197,7 @@ class NamedTensorContainerI(NamedObjectI, SymbolContainerI): def __contains__(self, obj): """Check if a scalar object is contained in self.""" return obj in self._contained_objects - + @abstractmethod def __getitem__(self, slc): pass diff --git a/hysop/tools/transposition_states.py b/hysop/tools/transposition_states.py index 68db50f99cca745ca1b0fcb022923a4886313fe0..2222e6366ae538310a841e691e312e5b0c23dc7b 100644 --- a/hysop/tools/transposition_states.py +++ b/hysop/tools/transposition_states.py @@ -114,9 +114,8 @@ class TranspositionStateEnum(object): """TranspositionStateEnum base class.""" pass -class TranspositionState(object): +class TranspositionState(object, metaclass=TranspositionStateType): """TranspositionState base class.""" - __metaclass__ = TranspositionStateType __slots__ = ('_axes',) diff --git a/hysop/tools/variable.py b/hysop/tools/variable.py index 3773fca139a17b016256dfad29f3aa6aea2149a1..5e411dac8fe436d30395d0aea540e80cba78e0b2 100644 --- a/hysop/tools/variable.py +++ b/hysop/tools/variable.py @@ -6,9 +6,8 @@ from hysop.tools.decorators import debug Variable = EnumFactory.create('Variable', ['DISCRETE_FIELD', 'PARAMETER']) -class VariableTag(object): +class VariableTag(object, metaclass=ABCMeta): """Tag for HySoP variables.""" - __metaclass__ = ABCMeta @debug def __new__(cls, variable_kind=None, **kwds): @@ -16,7 +15,7 @@ class VariableTag(object): obj = super(VariableTag, cls).__new__(cls, **kwds) obj.__variable_kind = variable_kind return obj - + @debug def __init__(self, variable_kind=None, **kwds): check_instance(variable_kind, Variable, allow_none=True) diff --git a/hysop/topology/topology.py b/hysop/topology/topology.py index 398ad19a199203e526b60b8b2e6013efb7785217..9c972ea714ee5917d89a5293671aa0abc04e879f 100644 --- a/hysop/topology/topology.py +++ b/hysop/topology/topology.py @@ -16,7 +16,7 @@ from hysop.core.arrays.array_backend import ArrayBackend from hysop.tools.types import check_instance, to_tuple, first_not_None from hysop.tools.parameters import MPIParams from hysop.tools.misc import Utils -from hysop.tools.decorators import debug +from hysop.tools.decorators import debug from hysop.tools.numpywrappers import npw from hysop.tools.string_utils import prepend from hysop.tools.handle import RegisteredObject, TaggedObject, TaggedObjectView @@ -28,20 +28,19 @@ class TopologyWarning(HysopWarning): """ pass -class TopologyState(TaggedObject): +class TopologyState(TaggedObject, metaclass=ABCMeta): """ Abstract base to define TopologyStates. - + A TopologyState is a topology dependent state, and acts as a virtual state - that determines how we should perceive raw mesh data. - - A TopologyState may for example include a transposition state like for + that determines how we should perceive raw mesh data. + + A TopologyState may for example include a transposition state like for CartesianTopology topologies. """ - __metaclass__ = ABCMeta __slots__ = ('_is_read_only',) - + @debug def __init__(self, is_read_only, **kwds): """Initialize a topology state.""" @@ -54,13 +53,13 @@ class TopologyState(TaggedObject): return self._is_read_only is_read_only = property(_get_is_read_only) - + @abstractmethod def match(self, other, invert=False): """Check if this topology state does match the other one.""" res = (self._is_read_only == other._is_read_only) return (not res) if invert else res - + @abstractmethod def __hash__(self): return hash(self._is_read_only) @@ -69,7 +68,7 @@ class TopologyState(TaggedObject): def copy(self, is_read_only=None, **kwds): """Return a copy of self, some properties may be alterted in kwds.""" pass - + @abstractmethod def short_description(self): """Short description of this topology state.""" @@ -78,7 +77,7 @@ class TopologyState(TaggedObject): def long_description(self): """Long description of this topology state.""" pass - + def __eq__(self, other): return self.match(other) def __ne__(self, other): @@ -88,31 +87,30 @@ class TopologyState(TaggedObject): return self.long_description() -class TopologyView(TaggedObjectView): +class TopologyView(TaggedObjectView, metaclass=ABCMeta): """ Abstract base to define views on a Topology dependening on a TopologyState. - + A TopologyView is a view on a Topology altered by a TopologyState. It is a lightweight object that keeps a reference on a Topology and a TopologyState. - A CartesianTopologyState may for example include a transposition state - for CartesianTopology topologies, - resulting in the automatic permutation of attributes when fetching the view attributes + A CartesianTopologyState may for example include a transposition state + for CartesianTopology topologies, + resulting in the automatic permutation of attributes when fetching the view attributes (global_resolution and ghosts will be transposed). """ - __metaclass__ = ABCMeta __slots__ = ('_mesh_view', '_domain_view', '_topology', '_topology_state') - + @debug def __new__(cls, topology_state, topology=None, **kwds): """ Create and initialize a TopologyView on given topology. - + Parameters ---------- topology_state: :class:`~hysop.topology.topology.TopologyState` - State that charaterizes the given view. + State that charaterizes the given view. topology: :class:`~hysop.topology.topology.Topology` Original topology on which the view is. kwds: dict @@ -123,17 +121,17 @@ class TopologyView(TaggedObjectView): topology: :class:`~hysop.topology.topology.Topology` Original topology on which the view is. topology_state: :class:`~hysop.topology.topology.TopologyState` - State that charaterizes the given view. - + State that charaterizes the given view. + domain : :class:`~hysop.domain.domain.Domain` The geometry on which the topology is defined. backend: :class:`~hysop.core.arrays.array_backend.ArrayBackend` - ArrayBackend of this topology. + ArrayBackend of this topology. mpi_params: :class:`~hysop.tools.parameters.MPIParams` The parent MPI parameters of this topology. - /!\ Topologies may define a sub communicator + /!\ Topologies may define a sub communicator (CartesianTopology topologies will define a MPI.Cartcomm for example). - + parent: :class:`~hysop.core.mpi.IntraComm` Return the parent communicator used to build this topology. task_id: int @@ -182,7 +180,7 @@ class TopologyView(TaggedObjectView): return self._topology._backend def _get_mpi_params(self): """The parent MPI parameters of this topology. - /!\ Topologies may define a sub communicator + /!\ Topologies may define a sub communicator CartesianTopology topologies will define a MPI.Cartcomm for example. """ return self._topology._mpi_params @@ -205,7 +203,7 @@ class TopologyView(TaggedObjectView): def _get_task_id(self): """Returns id of the task that owns this topology.""" return self._topology._mpi_params.task_id - + @abstractmethod def default_state(self): """Return the default topology state of this topology.""" @@ -215,12 +213,12 @@ class TopologyView(TaggedObjectView): def short_description(self, topo): """Short description of this topology.""" pass - + @abstractmethod def long_description(self, topo): """Long description of this topology.""" pass - + def match(self, other, invert=False): """Check if two TopologyViews are equivalent.""" if not isinstance(other, TopologyView): @@ -231,7 +229,7 @@ class TopologyView(TaggedObjectView): return not eq else: return eq - + def __eq__(self, other): return self.match(other) def __ne__(self, other): @@ -246,21 +244,21 @@ class TopologyView(TaggedObjectView): topology = property(_get_topology) topology_state = property(_get_topology_state) - + backend = property(_get_backend) mpi_params = property(_get_mpi_params) domain = property(_get_domain) mesh = property(_get_mesh) domain_dim = property(_get_domain_dim) - + parent = property(_get_parent) task_id = property(_get_task_id) -class Topology(RegisteredObject): +class Topology(RegisteredObject, metaclass=ABCMeta): """ Abstract base class for hysop Topologies. - + In hysop, a topology is defined as the association of a mpi process distribution (mpi topology) and of a set of local meshes (one per process). @@ -271,10 +269,8 @@ class Topology(RegisteredObject): You can also find examples of topologies instanciation in test_topology.py. """ - __metaclass__ = ABCMeta - @debug - def __new__(cls, domain, mpi_params=None, backend=Backend.HOST, + def __new__(cls, domain, mpi_params=None, backend=Backend.HOST, cl_env=None, allocator=None, queue=None, **kwds): """ Creates or get an existing topology. @@ -287,7 +283,7 @@ class Topology(RegisteredObject): MPI parameters (comm, task ...). If not specified, comm = domain.task_comm, task = domain.curent_task() backend: :class:`~hysop.constants.Backend` or `~hysop.core.arrays.ArrayBackend`, optional - Backend or backend kind for this topology. + Backend or backend kind for this topology. By default a topology will use Backend.HOST. allocator: :class:`~hysop.core.memory.allocator.Allocator`, optional Allocated used on HOST backends instead of the default host memory pool allocator. @@ -307,10 +303,10 @@ class Topology(RegisteredObject): Topologies can be uniquely identified by their id as they are hysop RegisteredObjects. See :class:`~hysop.tools.handle.RegisteredObject` for more information. """ - + # Create MPI parameters if necessary mpi_params = cls._create_mpi_params(mpi_params, domain, cl_env) - + # Create backend if necessary backend = cls._create_backend(backend, mpi_params, allocator, cl_env, queue) @@ -318,18 +314,18 @@ class Topology(RegisteredObject): check_instance(mpi_params, MPIParams) check_instance(backend, ArrayBackend) check_instance(domain, Domain) - - obj = super(Topology, cls).__new__(cls, backend=backend, + + obj = super(Topology, cls).__new__(cls, backend=backend, mpi_params=mpi_params, domain=domain, tag_prefix='t', **kwds) if not obj.obj_initialized: obj._backend = backend obj._mpi_params = mpi_params obj._domain = domain - + obj._domain.register_topology(obj) return obj - + @classmethod def _create_mpi_params(cls, mpi_params, domain, cl_env): if (mpi_params is None): @@ -339,7 +335,7 @@ class Topology(RegisteredObject): else: mpi_params = cl_env.mpi_params task_id = mpi_params.task_id - + msg = 'MPI task_id contained in mpi_params parameter is None.' assert (task_id is not None), msg diff --git a/hysop/topology/topology_descriptor.py b/hysop/topology/topology_descriptor.py index 81ac7454fd27d6deb451e0e1ee69f0ecbcacc677..13334ce338cca41817c8534601141f63c4d0afdb 100644 --- a/hysop/topology/topology_descriptor.py +++ b/hysop/topology/topology_descriptor.py @@ -4,7 +4,7 @@ from hysop.tools.types import check_instance from hysop.topology.topology import Topology, TopologyView from hysop.mesh.mesh import Mesh -class TopologyDescriptor(object): +class TopologyDescriptor(object, metaclass=ABCMeta): """ Describes how a topology should be built. @@ -12,14 +12,13 @@ class TopologyDescriptor(object): operator graph building and are replaced by a single unique topology upon initialization. """ - __metaclass__ = ABCMeta __slots__ = ('_mpi_params', '_domain', '_backend', '_extra_kwds') def __init__(self, mpi_params, domain, backend, **kwds): """ Initialize a TopologyDescriptor. - + Notes ----- kwds allows for backend specific variables. @@ -49,13 +48,13 @@ class TopologyDescriptor(object): def _get_extra_kwds(self): """Get extra keyword arguments.""" return dict(self._extra_kwds) - + mpi_params = property(_get_mpi_params) domain = property(_get_domain) backend = property(_get_backend) extra_kwds = property(_get_extra_kwds) dim = property(_get_dim) - + @staticmethod def build_descriptor(backend, operator, field, handle, **kwds): """ @@ -79,7 +78,7 @@ class TopologyDescriptor(object): # handle is already a TopologyDescriptor, so we return it. return handle elif isinstance(handle, CartesianTopologyDescriptors): - return CartesianTopologyDescriptor.build_descriptor(backend, operator, + return CartesianTopologyDescriptor.build_descriptor(backend, operator, field, handle, **kwds) elif (handle is None): # this topology will be determined later @@ -88,7 +87,7 @@ class TopologyDescriptor(object): msg='Unknown handle of class {} to build a TopologyDescriptor.' msg=msg.format(handle.__class__) raise TypeError(msg) - + def choose_or_create_topology(self, known_topologies, **kwds): """ Returns a topology that is either taken from known_topologies, a set @@ -101,7 +100,7 @@ class TopologyDescriptor(object): if (topo is None): topo = self.create_topology(**kwds) return topo - + @abstractmethod def choose_topology(self, known_topologies, **kwds): """ @@ -140,8 +139,8 @@ class TopologyDescriptor(object): @abstractmethod def __hash__(self): - h = id(self.domain) - h ^= hash(self.mpi_params) + h = id(self.domain) + h ^= hash(self.mpi_params) h ^= hash(self.backend) h ^= hash(self._extra_kwds) return h