Exemple #1
0
class Function(function.Function, Signer):

    from_YASK = True

    def __new__(cls, *args, **kwargs):
        if cls in _SymbolCache:
            newobj = sympy.Function.__new__(cls, *args,
                                            **kwargs.get('options', {}))
            newobj._cached_init()
        else:
            # If a Function has no SpaceDimension, than for sure it won't be
            # used by YASK. We then return a devito.Function, which employs
            # a standard row-major format for data values
            indices = cls.__indices_setup__(**kwargs)
            klass = cls if any(i.is_Space for i in indices) else cls.__base__
            newobj = cls.__base__.__new__(klass, *args, **kwargs)
        return newobj

    def _allocate_memory(func):
        """Allocate memory in terms of YASK grids."""
        def wrapper(self):
            if self._data is None:
                log("Allocating memory for %s%s" %
                    (self.name, self.shape_allocated))

                # Fetch the appropriate context
                context = contexts.fetch(self.dimensions, self.dtype)

                # Create a YASK grid; this allocates memory
                grid = context.make_grid(self)

                # /self._padding/ must be updated as (from the YASK docs):
                # "The value may be slightly larger [...] due to rounding"
                padding = []
                for i in self.dimensions:
                    if i.is_Space:
                        padding.append((grid.get_left_extra_pad_size(i.name),
                                        grid.get_right_extra_pad_size(i.name)))
                    else:
                        # time and misc dimensions
                        padding.append((0, 0))
                self._padding = tuple(padding)

                self._data = Data(grid, self.shape_allocated, self.indices,
                                  self.dtype)
                self._data.reset()
            return func(self)

        return wrapper

    def __del__(self):
        if self._data is not None:
            self._data.release_storage()

    @property
    @_allocate_memory
    def _data_buffer(self):
        ctype = numpy_to_ctypes(self.dtype)
        cpointer = ctypes.cast(int(self._data.grid.get_raw_storage_buffer()),
                               ctypes.POINTER(ctype))
        ndpointer = np.ctypeslib.ndpointer(dtype=self.dtype,
                                           shape=self.shape_allocated)
        casted = ctypes.cast(cpointer, ndpointer)
        ndarray = np.ctypeslib.as_array(casted, shape=self.shape_allocated)
        return ndarray

    @property
    def data(self):
        """
        The domain data values, as a :class:`Data`.

        The returned object, which behaves as a :class:`numpy.ndarray`, provides
        a *view* of the actual data, in row-major format. Internally, the data is
        stored in whatever layout adopted by YASK.

        Any read/write from/to the returned :class:`Data` should be performed
        assuming a row-major storage layout; behind the scenes, these accesses
        are automatically translated into whatever YASK expects, in order to pick
        the intended values.

        Abstracting away the internal storage layout adopted by YASK guarantees
        that user code works independently of the chosen Devito backend. This may
        introduce a little performance penalty when accessing data w.r.t. the
        default Devito backend. Such penalty should however be easily amortizable,
        as the time spent in running Operators is expected to be vastly greater
        than any user-level data manipulation.

        For further information, refer to ``Data.__doc__``.
        """
        return self.data_domain

    @cached_property
    @_allocate_memory
    def data_domain(self):
        """
        .. note::

            Alias to ``self.data``.
        """
        return Data(self._data.grid,
                    self.shape,
                    self.indices,
                    self.dtype,
                    offset=self._offset_domain.left)

    @cached_property
    @_allocate_memory
    def data_with_halo(self):
        return Data(self._data.grid,
                    self.shape_with_halo,
                    self.indices,
                    self.dtype,
                    offset=self._offset_halo.left)

    @cached_property
    @_allocate_memory
    def data_allocated(self):
        return Data(self._data.grid, self.shape_allocated, self.indices,
                    self.dtype)

    def _arg_defaults(self, alias=None):
        args = super(Function, self)._arg_defaults(alias=alias)

        key = alias or self
        args[namespace['code-grid-name'](key.name)] = self.data.rawpointer

        return args

    def _signature_items(self):
        return (self.name, ) + tuple(i.name for i in self.indices)
Exemple #2
0
class Function(function.Function):

    from_YASK = True

    def _allocate_memory(func):
        """Allocate memory in terms of YASK grids."""
        def wrapper(self):
            if self._data is None:
                log("Allocating memory for %s%s" % (self.name, self.shape_allocated))

                # Fetch the appropriate context
                context = contexts.fetch(self.grid, self.dtype)

                # TODO : the following will fail if not using a SteppingDimension,
                # eg with save=True one gets /time/ instead /t/
                grid = context.make_grid(self)

                # /self._padding/ must be updated as (from the YASK docs):
                # "The value may be slightly larger [...] due to rounding"
                pad = [(0, 0) if i.is_Time else (grid.get_left_extra_pad_size(i.name),
                                                 grid.get_right_extra_pad_size(i.name))
                       for i in self.indices]
                self._padding = pad

                self._data = Data(grid, self.shape_allocated, self.indices, self.dtype)
                self._data.reset()
            return func(self)
        return wrapper

    def __del__(self):
        if self._data is not None:
            self._data.release_storage()

    @property
    @_allocate_memory
    def _data_buffer(self):
        ctype = numpy_to_ctypes(self.dtype)
        cpointer = ctypes.cast(int(self._data.grid.get_raw_storage_buffer()),
                               ctypes.POINTER(ctype))
        ndpointer = np.ctypeslib.ndpointer(dtype=self.dtype, shape=self.shape_allocated)
        casted = ctypes.cast(cpointer, ndpointer)
        ndarray = np.ctypeslib.as_array(casted, shape=self.shape_allocated)
        return ndarray

    @property
    def data(self):
        """
        The domain data values, as a :class:`Data`.

        The returned object, which behaves as a :class:`numpy.ndarray`, provides
        a *view* of the actual data, in row-major format. Internally, the data is
        stored in whatever layout adopted by YASK.

        Any read/write from/to the returned :class:`Data` should be performed
        assuming a row-major storage layout; behind the scenes, these accesses
        are automatically translated into whatever YASK expects, in order to pick
        the intended values.

        Abstracting away the internal storage layout adopted by YASK guarantees
        that user code works independently of the chosen Devito backend. This may
        introduce a little performance penalty when accessing data w.r.t. the
        default Devito backend. Such penalty should however be easily amortizable,
        as the time spent in running Operators is expected to be vastly greater
        than any user-level data manipulation.

        For further information, refer to ``Data.__doc__``.
        """
        return self.data_domain

    @cached_property
    @_allocate_memory
    def data_domain(self):
        """
        .. note::

            Alias to ``self.data``.
        """
        return Data(self._data.grid, self.shape, self.indices, self.dtype,
                    offset=self._offset_domain.left)

    @cached_property
    @_allocate_memory
    def data_with_halo(self):
        return Data(self._data.grid, self.shape_with_halo, self.indices, self.dtype,
                    offset=self._offset_halo.left)

    @cached_property
    @_allocate_memory
    def data_allocated(self):
        return Data(self._data.grid, self.shape_allocated, self.indices, self.dtype)

    def initialize(self):
        raise NotImplementedError

    def _arg_defaults(self, alias=None):
        args = super(Function, self)._arg_defaults(alias=alias)

        key = alias or self
        args[namespace['code-grid-name'](key.name)] = self.data.rawpointer

        return args
Exemple #3
0
class Function(dense.Function, Signer):

    from_YASK = True

    def __new__(cls, *args, **kwargs):
        key = cls._cache_key(*args, **kwargs)
        obj = cls._cache_get(key)

        if obj is not None:
            newobj = sympy.Function.__new__(cls, *args,
                                            **kwargs.get('options', {}))
            newobj.__init_cached__(key)
            return newobj

        # Not in cache. Create a new Function via core.Function

        # If a Function has no SpaceDimension, than for sure it won't be
        # used by YASK. We then return a devito.Function, which employs
        # a standard row-major format for data values
        indices = cls.__indices_setup__(**kwargs)
        klass = cls if any(i.is_Space for i in indices) else cls.__base__
        newobj = cls.__base__.__new__(klass, *args, **kwargs)

        return newobj

    def __padding_setup__(self, **kwargs):
        # YASK calculates the padding, so we bypass the dense.Function's autopadding
        return tuple((0, 0) for i in range(self.ndim))

    def _allocate_memory(func):
        """Allocate memory in terms of YASK vars."""
        def wrapper(self):
            if self._data is None:
                log("Allocating memory for %s%s" %
                    (self.name, self.shape_allocated))

                # Free memory carried by stale symbolic objects
                # TODO: see issue #944
                # CacheManager.clear(dump_contexts=False, force=False)

                # Fetch the appropriate context
                context = contexts.fetch(self.dimensions, self.dtype)

                # Create a YASK var; this allocates memory
                var = context.make_var(self)

                # `self._padding` must be updated as (from the YASK docs):
                # "The value may be slightly larger [...] due to rounding"
                padding = []
                for i in self.dimensions:
                    if i.is_Space:
                        padding.append((var.get_left_extra_pad_size(i.name),
                                        var.get_right_extra_pad_size(i.name)))
                    else:
                        # time and misc dimensions
                        padding.append((0, 0))
                self._padding = tuple(padding)
                del self.shape_allocated  # Invalidate cached_property

                self._data = Data(var, self.shape_allocated, self.indices,
                                  self.dtype)
                self._data.reset()
            return func(self)

        return wrapper

    def __del__(self):
        if self._data is None:
            # Perhaps data had never been allocated
            return
        if self is self.function:
            # The original Function (e.g., f(x, y)) is in charge of freeing memory,
            # while this is a no-op for all other objects derived from it (e.g.,
            # (e.g., f(x+1, y), f(x, y-2))
            self._data.release_storage()

    @property
    @_allocate_memory
    def _data_buffer(self):
        num_elements = self._data.var.get_num_storage_elements()
        shape = self.shape_allocated
        ctype_1d = dtype_to_ctype(self.dtype) * reduce(mul, shape)

        if num_elements != reduce(mul, shape):
            warning("num_storage_elements(%d) != reduce(mul, %s)",
                    num_elements, str(shape))

        buf = ctypes.cast(int(self._data.var.get_raw_storage_buffer()),
                          ctypes.POINTER(ctype_1d)).contents

        return np.frombuffer(buf, dtype=self.dtype).reshape(shape)

    @property
    def data(self):
        """
        The domain data values, as a Data.

        The returned object, which behaves as a `numpy.ndarray`, provides
        a *view* of the actual data, in row-major format. Internally, the data is
        stored in whatever layout adopted by YASK.

        Any read/write from/to the returned Data should be performed
        assuming a row-major storage layout; behind the scenes, these accesses
        are automatically translated into whatever YASK expects, in order to pick
        the intended values.

        Abstracting away the internal storage layout adopted by YASK guarantees
        that user code works independently of the chosen Devito backend. This may
        introduce a little performance penalty when accessing data w.r.t. the
        default Devito backend. Such penalty should however be easily amortizable,
        as the time spent in running Operators is expected to be vastly greater
        than any user-level data manipulation.

        For further information, refer to ``Data.__doc__``.
        """
        return self.data_domain

    @cached_property
    @_allocate_memory
    def data_domain(self):
        """
        Notes
        -----
        Alias to ``self.data``.
        """
        return Data(self._data.var,
                    self.shape,
                    self.indices,
                    self.dtype,
                    offset=self._offset_domain)

    @cached_property
    @_allocate_memory
    def data_with_halo(self):
        return Data(self._data.var,
                    self.shape_with_halo,
                    self.indices,
                    self.dtype,
                    offset=self._offset_halo.left)

    @cached_property
    @_allocate_memory
    def _data_allocated(self):
        return Data(self._data.var, self.shape_allocated, self.indices,
                    self.dtype)

    def _arg_defaults(self, alias=None):
        args = super(Function, self)._arg_defaults(alias=alias)

        key = alias or self
        args[namespace['code-var-name'](key.name)] = self.data.rawpointer

        return args

    def _signature_items(self):
        return (self.name, ) + tuple(i.name for i in self.indices)
Exemple #4
0
class Function(dense.Function, Signer):

    from_YASK = True

    def __new__(cls, *args, **kwargs):
        if cls in basic._SymbolCache:
            newobj = sympy.Function.__new__(cls, *args, **kwargs.get('options', {}))
            newobj._cached_init()
        else:
            # If a Function has no SpaceDimension, than for sure it won't be
            # used by YASK. We then return a devito.Function, which employs
            # a standard row-major format for data values
            indices = cls.__indices_setup__(**kwargs)
            klass = cls if any(i.is_Space for i in indices) else cls.__base__
            newobj = cls.__base__.__new__(klass, *args, **kwargs)
        return newobj

    def _allocate_memory(func):
        """Allocate memory in terms of YASK grids."""
        def wrapper(self):
            if self._data is None:
                log("Allocating memory for %s%s" % (self.name, self.shape_allocated))

                # Fetch the appropriate context
                context = contexts.fetch(self.dimensions, self.dtype)

                # Create a YASK grid; this allocates memory
                grid = context.make_grid(self)

                # `self._padding` must be updated as (from the YASK docs):
                # "The value may be slightly larger [...] due to rounding"
                padding = []
                for i in self.dimensions:
                    if i.is_Space:
                        padding.append((grid.get_left_extra_pad_size(i.name),
                                        grid.get_right_extra_pad_size(i.name)))
                    else:
                        # time and misc dimensions
                        padding.append((0, 0))
                self._padding = tuple(padding)
                del self.shape_allocated  # Invalidate cached_property

                self._data = Data(grid, self.shape_allocated, self.indices, self.dtype)
                self._data.reset()
            return func(self)
        return wrapper

    def __del__(self):
        if self._data is not None:
            self._data.release_storage()

    @property
    @_allocate_memory
    def _data_buffer(self):
        num_elements = self._data.grid.get_num_storage_elements()
        shape = self.shape_allocated
        ctype_1d = dtype_to_ctype(self.dtype) * reduce(mul, shape)

        if num_elements != reduce(mul, shape):
            warning("num_storage_elements(%d) != reduce(mul, %s)",
                    num_elements, str(shape))

        buf = ctypes.cast(
            int(self._data.grid.get_raw_storage_buffer()),
            ctypes.POINTER(ctype_1d)).contents

        return np.frombuffer(buf, dtype=self.dtype).reshape(shape)

    @property
    def data(self):
        """
        The domain data values, as a :class:`Data`.

        The returned object, which behaves as a :class:`numpy.ndarray`, provides
        a *view* of the actual data, in row-major format. Internally, the data is
        stored in whatever layout adopted by YASK.

        Any read/write from/to the returned :class:`Data` should be performed
        assuming a row-major storage layout; behind the scenes, these accesses
        are automatically translated into whatever YASK expects, in order to pick
        the intended values.

        Abstracting away the internal storage layout adopted by YASK guarantees
        that user code works independently of the chosen Devito backend. This may
        introduce a little performance penalty when accessing data w.r.t. the
        default Devito backend. Such penalty should however be easily amortizable,
        as the time spent in running Operators is expected to be vastly greater
        than any user-level data manipulation.

        For further information, refer to ``Data.__doc__``.
        """
        return self.data_domain

    @cached_property
    @_allocate_memory
    def data_domain(self):
        """
        Notes
        -----
        Alias to ``self.data``.
        """
        return Data(self._data.grid, self.shape, self.indices, self.dtype,
                    offset=self._offset_domain)

    @cached_property
    @_allocate_memory
    def data_with_halo(self):
        return Data(self._data.grid, self.shape_with_halo, self.indices, self.dtype,
                    offset=self._offset_halo.left)

    @cached_property
    @_allocate_memory
    def _data_allocated(self):
        return Data(self._data.grid, self.shape_allocated, self.indices, self.dtype)

    def _arg_defaults(self, alias=None):
        args = super(Function, self)._arg_defaults(alias=alias)

        key = alias or self
        args[namespace['code-grid-name'](key.name)] = self.data.rawpointer

        return args

    def _signature_items(self):
        return (self.name,) + tuple(i.name for i in self.indices)
Exemple #5
0
class Function(function.Function):

    from_YASK = True

    def _allocate_memory(func):
        """Allocate memory in terms of YASK grids."""
        def wrapper(self):
            if self._data is None:
                log("Allocating memory for %s (%s)" % (self.name, self.shape))

                # Fetch the appropriate context
                context = contexts.fetch(self.grid, self.dtype)

                # TODO : the following will fail if not using a SteppingDimension,
                # eg with save=True one gets /time/ instead /t/
                grid = context.make_grid(self)

                # /self._padding/ must be updated as (from the YASK docs):
                # "The value may be slightly larger [...] due to rounding"
                padding = [
                    0 if i.is_Time else grid.get_extra_pad_size(i.name)
                    for i in self.indices
                ]
                self._padding = tuple((i, ) * 2 for i in padding)

                self._data = Data(grid, self.shape_allocated, self.indices,
                                  self.dtype)
                self._data.reset()
            return func(self)

        return wrapper

    def __del__(self):
        if self._data is not None:
            self._data.release_storage()

    @property
    def _data_buffer(self):
        data = self.data
        ctype = numpy_to_ctypes(data.dtype)
        cpointer = ctypes.cast(int(data.grid.get_raw_storage_buffer()),
                               ctypes.POINTER(ctype))
        ndpointer = np.ctypeslib.ndpointer(dtype=data.dtype, shape=data.shape)
        casted = ctypes.cast(cpointer, ndpointer)
        ndarray = np.ctypeslib.as_array(casted, shape=data.shape)
        return ndarray

    @property
    def shape_with_halo(self):
        """
        Shape of the domain plus the read-only stencil boundary associated
        with this :class:`Function`.
        """
        # TODO: Drop me after the domain-allocation switch, as this method
        # will be provided by the superclass
        return tuple(j + i + k
                     for i, (j, k) in zip(self.shape_domain, self._halo))

    @property
    def shape_allocated(self):
        """
        Shape of the allocated data associated with this :class:`Function`.
        It includes the domain and halo regions, as well as any additional
        padding outside of the halo.
        """
        # TODO: Drop me after the domain-allocation switch, as this method
        # will be provided by the superclass
        return tuple(j + i + k
                     for i, (j, k) in zip(self.shape_with_halo, self._padding))

    @property
    def data(self):
        """
        The domain data values, as a :class:`Data`.

        The returned object, which behaves as a :class:`numpy.ndarray`, provides
        a *view* of the actual data, in row-major format. Internally, the data is
        stored in whatever layout adopted by YASK.

        Any read/write from/to the returned :class:`Data` should be performed
        assuming a row-major storage layout; behind the scenes, these accesses
        are automatically translated into whatever YASK expects, in order to pick
        the intended values.

        Abstracting away the internal storage layout adopted by YASK guarantees
        that user code works independently of the chosen Devito backend. This may
        introduce a little performance penalty when accessing data w.r.t. the
        default Devito backend. Such penalty should however be easily amortizable,
        as the time spent in running Operators is expected to be vastly greater
        than any user-level data manipulation.

        For further information, refer to ``Data.__doc__``.
        """
        return self.data_domain

    @cached_property
    @_allocate_memory
    def data_domain(self):
        """
        .. note::

            Alias to ``self.data``.
        """
        return Data(self._data.grid,
                    self.shape,
                    self.indices,
                    self.dtype,
                    offset=self._offset_domain)

    @cached_property
    @_allocate_memory
    def data_with_halo(self):
        return Data(self._data.grid,
                    self.shape_with_halo,
                    self.indices,
                    self.dtype,
                    offset=self._offset_halo)

    def initialize(self):
        raise NotImplementedError