From 8b8922dc250888cc53560f37840cc0054a029860 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Keck <Jean-Baptiste.Keck@imag.fr> Date: Thu, 29 Oct 2020 23:30:26 +0100 Subject: [PATCH] metaclasses --- hysop/backend/device/autotunable_kernel.py | 3 +- .../device/codegen/functions/complex.py | 4 +- .../device/codegen/kernels/custom_symbolic.py | 4 +- hysop/backend/device/device_platform.py | 10 +- hysop/backend/device/kernel_autotuner.py | 3 +- .../backend/device/kernel_autotuner_config.py | 4 +- hysop/backend/device/kernel_config.py | 8 +- hysop/backend/device/logical_device.py | 42 +++-- .../backend/device/opencl/opencl_allocator.py | 13 +- .../device/opencl/opencl_kernel_launcher.py | 7 +- .../backend/device/opencl/opencl_operator.py | 3 +- hysop/backend/hardware/hardware_backend.py | 8 +- hysop/backend/hardware/hwinfo.py | 7 +- .../backend/host/fortran/fortran_operator.py | 9 +- .../backend/host/host_directional_operator.py | 8 +- hysop/backend/host/host_operator.py | 3 +- hysop/core/arrays/array.py | 156 +++++++++--------- hysop/core/arrays/array_backend.py | 4 +- hysop/core/graph/allocator.py | 3 +- hysop/core/graph/computational_graph.py | 4 +- hysop/core/graph/computational_node.py | 4 +- hysop/core/graph/computational_operator.py | 4 +- hysop/core/graph/continuous.py | 25 ++- hysop/core/graph/node_generator.py | 4 +- hysop/core/memory/allocator.py | 4 +- hysop/core/memory/mempool.py | 4 +- hysop/domain/domain.py | 6 +- hysop/fields/discrete_field.py | 20 +-- hysop/fields/ghost_exchangers.py | 3 +- hysop/mesh/mesh.py | 22 ++- hysop/numerics/fft/fft.py | 104 ++++++------ hysop/operator/adapt_timestep.py | 3 +- .../operator/base/custom_symbolic_operator.py | 3 +- hysop/operator/base/derivative.py | 4 +- hysop/operator/base/enstrophy.py | 4 +- hysop/operator/base/external_force.py | 3 +- hysop/operator/base/integrate.py | 4 +- hysop/operator/base/memory_reordering.py | 4 +- hysop/operator/base/redistribute_operator.py | 38 ++--- hysop/operator/base/transpose_operator.py | 46 +++--- hysop/operator/directional/directional.py | 7 +- hysop/operator/hdf_io.py | 4 +- hysop/parameters/parameter.py | 3 +- hysop/tools/enum.py | 3 +- hysop/tools/handle.py | 12 +- hysop/tools/interface.py | 64 ++++--- hysop/tools/transposition_states.py | 3 +- hysop/tools/variable.py | 5 +- hysop/topology/topology.py | 88 +++++----- hysop/topology/topology_descriptor.py | 19 +-- 50 files changed, 362 insertions(+), 458 deletions(-) diff --git a/hysop/backend/device/autotunable_kernel.py b/hysop/backend/device/autotunable_kernel.py index 170f3348e..0bec40f40 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 5b29dd56f..3f2e68faf 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 066f9382e..df977b761 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 096eec384..63df8bdde 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 7bb7792d9..f4a6e9096 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 ee4483e49..b8ed3d302 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 334ab9e34..6db662404 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 d9e74c926..44682869e 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 44d8f8b0c..e44b2a0f4 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 0d203fc63..b96823f27 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 b88796c7b..535925701 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 21fa5ab05..7c8031911 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 227203cac..5510a00d8 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 a7a1d68c5..fbf4d5af2 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 dc4e9816f..f43dccf69 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 bf818f257..8c86415ab 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 27a8f7b48..a837463bd 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 82b3812be..07fb46176 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 c8d3f7a24..08cbe0b85 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 7f5226ffb..d8a5eba08 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 160db9b45..bd394fa06 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 b71d3bc4c..afccabccc 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 ae2e358bc..75a4d5ec9 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 070e8902f..402f61fac 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 098af43f7..4ef44783f 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 b3dcd7c9e..7b0ffa361 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 277c95371..a7f696db4 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 1490a15a8..aafa62d99 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 75087be12..014c3e6ba 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 4f30aa600..2b7993d73 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 494fb6550..c54495669 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 b547e6538..65e91de33 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 408432613..bba8012df 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 834416f58..5b860814b 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 1305461ca..f2c401b05 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 7cfb01798..59d9cb5ad 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 bfbda8177..2a9533557 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 1d6c4c901..c07791e5c 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 aa5ad03f3..0a7ebdf3a 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 bd91896df..e08f3fcd4 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 c3b7af685..6ef83b3a3 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 033472524..a62bd7a8e 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 f09da60d8..33c42064a 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 6ca539c06..5dabce092 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 c51ea8c81..5a51e25e3 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 3722820a0..d49604237 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 68db50f99..2222e6366 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 3773fca13..5e411dac8 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 398ad19a1..9c972ea71 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 81ac7454f..13334ce33 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 -- GitLab