Ejemplo n.º 1
0
    def random_point(self):
        """Generate the random point belonging to the mesh.

        The use of this function is mostly limited for writing tests
        for packages based on `discretisedfield`.

        Returns
        -------
        tuple (3,)
            Coordinates of a random point inside that belongs to the mesh
            :math:`(x_\\text{rand}, y_\\text{rand}, z_\\text{rand})`.

        Examples
        --------
        1. Generating a random mesh point.

        >>> import discretisedfield as df
        ...
        >>> p1 = (0, 0, 0)
        >>> p2 = (200e-9, 200e-9, 1e-9)
        >>> cell = (1e-9, 1e-9, 0.5e-9)
        >>> mesh = df.Mesh(p1=p1, p2=p2, cell=cell)
        >>> mesh.random_point()
        (...)

        .. note::

           In the example, ellipsis is used instead of an exact tuple
           because the result differs each time ``random_point``
           method is called.

        """
        res = np.add(self.pmin, np.multiply(np.random.random(3), self.l))
        return dfu.array2tuple(res)
Ejemplo n.º 2
0
    def centre(self):
        """Mesh domain centre point.

        It is computed as the middle point between minimum and maximum
        coordinates :math:`\\mathbf{p}_\\text{c} = \\frac{1}{2}
        (\\mathbf{p}_\\text{min} + \\mathbf{p}_\\text{max})` This
        point does not necessarily coincide with the discretisation
        cell centre.

        Returns
        -------
        tuple (3,)
            Mesh domain centre point :math:`(p_{c}^{x}, p_{c}^{y},
            p_{c}^{z})`.

        Examples
        --------
        1. Getting the mesh centre point.

        >>> import discretisedfield as df
        ...
        >>> p1 = (0, 0, 0)
        >>> p2 = (5, 15, 20)
        >>> n = (5, 15, 20)
        >>> mesh = df.Mesh(p1=p1, p2=p2, n=n)
        >>> mesh.centre
        (2.5, 7.5, 10.0)

        """
        res = np.multiply(np.add(self.pmin, self.pmax), 0.5)
        return dfu.array2tuple(res)
Ejemplo n.º 3
0
    def l(self):
        """Mesh domain edge lengths.

        Edge length in any direction :math:`i` is computed from the
        points between which the mesh domain spans :math:`l^{i} =
        |p_{2}^{i} - p_{1}^{i}|`.

        Returns
        -------
        tuple (3,)
            Lengths of mesh domain edges :math:`(l_{x}, l_{y}, l_{z})`.

        Examples
        --------
        1. Getting the mesh domain edge lengths.

        >>> import discretisedfield as df
        ...
        >>> p1 = (0, 0, -5)
        >>> p2 = (5, 15, 15)
        >>> n = (5, 15, 20)
        >>> mesh = df.Mesh(p1=p1, p2=p2, n=n)
        >>> mesh.l
        (5, 15, 20)

        .. seealso:: :py:func:`~discretisedfield.Mesh.n`

        """
        res = np.abs(np.subtract(self.p1, self.p2))
        return dfu.array2tuple(res)
Ejemplo n.º 4
0
    def pmax(self):
        """Mesh point with maximum coordinates.

        The :math:`i`-th component of :math:`\\mathbf{p}_\\text{max}`
        is computed from points :math:`p_{1}` and :math:`p_{2}`
        between which the mesh domain spans: :math:`p_\\text{min}^{i}
        = \\text{max}(p_{1}^{i}, p_{2}^{i})`.

        Returns
        -------
        tuple (3,)
            Point with maximum mesh coordinates :math:`(p_{x}^\\text{max},
            p_{y}^\\text{max}, p_{z}^\\text{max})`.

        Examples
        --------
        1. Getting the maximum mesh point.

        >>> import discretisedfield as df
        ...
        >>> p1 = (-1.1, 2.9, 0)
        >>> p2 = (5, 0, -0.1)
        >>> cell = (0.1, 0.1, 0.005)
        >>> mesh = df.Mesh(p1=p1, p2=p2, cell=cell)
        >>> mesh.pmax
        (5.0, 2.9, 0.0)

        .. seealso:: :py:func:`~discretisedfield.Mesh.pmin`

        """
        res = np.maximum(self.p1, self.p2)
        return dfu.array2tuple(res)
Ejemplo n.º 5
0
    def line(self, *, p1, p2, n):
        """Line generator.

        Given two points ``p1`` and ``p2`` line is defined and ``n`` points on
        that line are generated and yielded in ``n`` iterations:

        .. math::

           \\mathbf{r}_{i} = i\\frac{\\mathbf{p}_{2} - \\mathbf{p}_{1}}{n-1},
           \\text{for}\\, i = 0, ..., n-1

        Parameters
        ----------
        p1 / p2 : (3,) array_like

            Points between which the line is defined :math:`\\mathbf{p} =
            (p_{x}, p_{y}, p_{z})`.

        n : int

            Number of points on the line.

        Yields
        ------
        tuple (3,)

            :math:`\\mathbf{r}_{i}`

        Raises
        ------
        ValueError

            If ``p1`` or ``p2`` is outside the mesh region.

        Examples
        --------
        1. Creating line generator.

        >>> import discretisedfield as df
        ...
        >>> p1 = (0, 0, 0)
        >>> p2 = (2, 2, 2)
        >>> cell = (1, 1, 1)
        >>> mesh = df.Mesh(p1=p1, p2=p2, cell=cell)
        ...
        >>> line = mesh.line(p1=(0, 0, 0), p2=(2, 0, 0), n=2)
        >>> list(line)
        [(0.0, 0.0, 0.0), (2.0, 0.0, 0.0)]

        .. seealso:: :py:func:`~discretisedfield.Region.plane`

        """
        if p1 not in self.region or p2 not in self.region:
            msg = f'Point {p1=} or point {p2=} is outside the mesh region.'
            raise ValueError(msg)

        dl = np.subtract(p2, p1) / (n - 1)
        for i in range(n):
            yield dfu.array2tuple(np.add(p1, i * dl))
Ejemplo n.º 6
0
    def __init__(self,
                 *,
                 region=None,
                 p1=None,
                 p2=None,
                 n=None,
                 cell=None,
                 bc='',
                 subregions=dict()):
        if region is not None and p1 is None and p2 is None:
            self.region = region
        elif region is None and p1 is not None and p2 is not None:
            self.region = df.Region(p1=p1, p2=p2)
        else:
            msg = 'Either region or p1 and p2 can be passed, not both.'
            raise ValueError(msg)

        if cell is not None and n is None:
            self.cell = tuple(cell)
            n = np.divide(self.region.edges, self.cell).round().astype(int)
            self.n = dfu.array2tuple(n)
        elif n is not None and cell is None:
            self.n = tuple(n)
            cell = np.divide(self.region.edges, self.n).astype(float)
            self.cell = dfu.array2tuple(cell)
        else:
            msg = 'Either n or cell can be passed, not both.'
            raise ValueError(msg)

        # Check if the mesh region is an aggregate of the discretisation cell.
        tol = 1e-12  # picometre tolerance
        rem = np.remainder(self.region.edges, self.cell)
        if np.logical_and(np.greater(rem, tol),
                          np.less(rem, np.subtract(self.cell, tol))).any():
            msg = (f'Region cannot be divided into '
                   f'discretisation cells of size {self.cell=}.')
            raise ValueError(msg)

        self.bc = bc.lower()

        self.subregions = subregions
Ejemplo n.º 7
0
    def __init__(self,
                 p1,
                 p2,
                 n=None,
                 cell=None,
                 pbc=set(),
                 regions={},
                 name='mesh'):
        self.p1 = tuple(p1)
        self.p2 = tuple(p2)
        self.pbc = pbc
        self.name = name
        self.regions = regions

        # Is any edge length of the domain equal to zero?
        if np.equal(self.l, 0).any():
            msg = 'The length of one of the domain edges is zero.'
            raise ValueError(msg)

        # Determine whether cell or n was passed and define them both.
        if cell is not None and n is None:
            self.cell = tuple(cell)
            n = np.divide(self.l, self.cell).round().astype(int)
            self.n = dfu.array2tuple(n)
        elif n is not None and cell is None:
            self.n = tuple(n)
            cell = np.divide(self.l, self.n).astype(float)
            self.cell = dfu.array2tuple(cell)
        else:
            msg = ('One and only one of the parameters '
                   '(n or cell) should be defined.')
            raise ValueError(msg)

        # Is the mesh domain not an aggregate of discretisation cells?
        tol = 1e-12  # picometre tolerance
        rem = np.remainder(self.l, self.cell)
        if np.logical_and(np.greater(rem, tol),
                          np.less(rem, np.subtract(self.cell, tol))).any():
            msg = 'Mesh domain is not an aggregate of the discretisation cell.'
            raise ValueError(msg)
Ejemplo n.º 8
0
    def line(self, p1, p2, n=100):
        """Line generator.

        Given two points :math:`p_{1}` and :math:`p_{2}`, :math:`n`
        position coordinates are generated.

        .. math::

           \\mathbf{r}_{i} = i\\frac{\\mathbf{p}_{2} -
           \\mathbf{p}_{1}}{n-1}

        and this method yields :math:`\\mathbf{r}_{i}` in :math:`n`
        iterations.

        Parameters
        ----------
        p1, p2 : (3,) array_like
            Two points between which the line is generated.
        n : int
            Number of points on the line.

        Yields
        ------
        tuple
            :math:`\\mathbf{r}_{i}`

        Raises
        ------
        ValueError
            If `p1` or `p2` is outside the mesh domain.

        Examples
        --------
        1. Creating a line generator.

        >>> import discretisedfield as df
        ...
        >>> p1 = (0, 0, 0)
        >>> p2 = (2, 2, 2)
        >>> cell = (1, 1, 1)
        >>> mesh = df.Mesh(p1=p1, p2=p2, cell=cell)
        >>> tuple(mesh.line(p1=(0, 0, 0), p2=(2, 0, 0), n=2))
        ((0.0, 0.0, 0.0), (2.0, 0.0, 0.0))

        """
        if p1 not in self or p2 not in self:
            msg = f'Point {p1} or point {p2} is outside the mesh.'
            raise ValueError(msg)

        dl = np.subtract(p2, p1) / (n - 1)
        for i in range(n):
            yield dfu.array2tuple(np.add(p1, i * dl))
Ejemplo n.º 9
0
    def point2index(self, point, /):
        """Convert point to the index of a cell which contains that point.

        Parameters
        ----------
        point : (3,) array_like

            Point :math:`\\mathbf{p} = (p_{x}, p_{y}, p_{z})`.

        Returns
        -------
        (3,) tuple

            The cell's index :math:`(i_{x}, i_{y}, i_{z})`.

        Raises
        ------
        ValueError

            If ``point`` is outside the mesh.

        Examples
        --------
        1. Converting point to the cell's index.

        >>> import discretisedfield as df
        ...
        >>> p1 = (0, 0, 0)
        >>> p2 = (2, 2, 1)
        >>> cell = (1, 1, 1)
        >>> mesh = df.Mesh(region=df.Region(p1=p1, p2=p2), cell=cell)
        >>> mesh.point2index((0.2, 1.7, 0.3))
        (0, 1, 0)

        .. seealso:: :py:func:`~discretisedfield.Mesh.index2point`

        """
        if point not in self.region:
            msg = f'Point {point=} is outside the mesh region.'
            raise ValueError(msg)

        index = np.subtract(
            np.divide(np.subtract(point, self.region.pmin), self.cell),
            0.5).round().astype(int)
        # If index is rounded to the out-of-range values.
        index = np.clip(index, 0, np.subtract(self.n, 1))

        return dfu.array2tuple(index)
Ejemplo n.º 10
0
    def index2point(self, index, /):
        """Convert cell's index to its coordinate.

        Parameters
        ----------
        index : (3,) array_like

            The cell's index :math:`(i_{x}, i_{y}, i_{z})`.

        Returns
        -------
        (3,) tuple

            The cell's coordinate :math:`\\mathbf{p} = (p_{x}, p_{y}, p_{z})`.

        Raises
        ------
        ValueError

            If ``index`` is out of range.

        Examples
        --------
        1. Converting cell's index to its centre point coordinate.

        >>> import discretisedfield as df
        ...
        >>> p1 = (0, 0, 0)
        >>> p2 = (2, 2, 1)
        >>> cell = (1, 1, 1)
        >>> mesh = df.Mesh(p1=p1, p2=p2, cell=cell)
        >>> mesh.index2point((0, 0, 0))
        (0.5, 0.5, 0.5)
        >>> mesh.index2point((0, 1, 0))
        (0.5, 1.5, 0.5)

        .. seealso:: :py:func:`~discretisedfield.Mesh.point2index`

        """
        if np.logical_or(np.less(index, 0), np.greater_equal(index,
                                                             self.n)).any():
            msg = f'Index {index=} out of range.'
            raise ValueError(msg)

        point = np.add(self.region.pmin,
                       np.multiply(np.add(index, 0.5), self.cell))
        return dfu.array2tuple(point)
Ejemplo n.º 11
0
    def point2index(self, point):
        """Compute the index of a cell to which the point belongs to.

        Parameters
        ----------
        p : (3,) array_like
            The point's coordinate :math:`(p_{x}, p_{y}, p_{z})`.

        Returns
        -------
            The cell's index :math:`(i_{x}, i_{y}, i_{z})`.

        Raises
        ------
        ValueError
            If `point` is outside the mesh domain.

        Examples
        --------
        1. Converting point's coordinate to the cell index.

        >>> import discretisedfield as df
        ...
        >>> p1 = (0, 0, 0)
        >>> p2 = (2, 2, 1)
        >>> cell = (1, 1, 1)
        >>> mesh = df.Mesh(p1=p1, p2=p2, cell=cell)
        >>> mesh.point2index((0.2, 1.7, 0.3))
        (0, 1, 0)

        .. seealso:: :py:func:`~discretisedfield.Mesh.index2point`

        """
        if point not in self:
            msg = f'Point {point} is outside the mesh.'
            raise ValueError(msg)

        index = np.subtract(
            np.divide(np.subtract(point, self.pmin), self.cell),
            0.5).round().astype(int)
        # If index is rounded to the out-of-range values.
        index = np.clip(index, 0, np.subtract(self.n, 1))

        return dfu.array2tuple(index)
Ejemplo n.º 12
0
    def index2point(self, index):
        """Convert cell's index to the cell's centre coordinate.

        Parameters
        ----------
        index : (3,) array_like
            The cell's index :math:`(i_{x}, i_{y}, i_{z})`.

        Returns
        -------
            The cell's centre point :math:`(p_{x}, p_{y}, p_{z})`.

        Raises
        ------
        ValueError
            If the cell's index is out of range.

        Examples
        --------
        1. Converting cell's index to its coordinate.

        >>> import discretisedfield as df
        ...
        >>> p1 = (0, 0, 0)
        >>> p2 = (2, 2, 1)
        >>> cell = (1, 1, 1)
        >>> mesh = df.Mesh(p1=p1, p2=p2, cell=cell)
        >>> mesh.index2point((0, 1, 0))
        (0.5, 1.5, 0.5)

        .. seealso:: :py:func:`~discretisedfield.Mesh.point2index`

        """
        # Does index refer to a cell outside the mesh?
        if np.logical_or(np.less(index, 0), np.greater_equal(index,
                                                             self.n)).any():
            msg = 'Index out of range.'
            raise ValueError(msg)

        res = np.add(self.pmin, np.multiply(np.add(index, 0.5), self.cell))
        return dfu.array2tuple(res)
Ejemplo n.º 13
0
def test_array2tuple():
    dfu.array2tuple(np.array([1, 2, 3])) == (1, 2, 3)
Ejemplo n.º 14
0
    def plane(self, *args, n=None, **kwargs):
        """Extracts plane mesh.

        If one of the axes (``'x'``, ``'y'``, or ``'z'``) is passed as a
        string, a plane mesh perpendicular to that axis is extracted,
        intersecting the mesh region at its centre. Alternatively, if a keyword
        argument is passed (e.g. ``x=1e-9``), a plane perpendicular to the
        x-axis (parallel to yz-plane) and intersecting it at ``x=1e-9`` is
        extracted. The number of points in two dimensions on the plane can be
        defined using ``n`` tuple (e.g. ``n=(10, 15)``).

        The resulting mesh has an attribute ``info``, which is a dictionary
        containing basic information about the plane mesh.

        Parameters
        ----------
        n : (2,) tuple

            The number of points on the plane in two dimensions.

        Returns
        ------
        discretisedfield.Mesh

            An extracted mesh.

        Examples
        --------
        1. Extracting the plane mesh at a specific point (``y=1``).

        >>> import discretisedfield as df
        ...
        >>> p1 = (0, 0, 0)
        >>> p2 = (5, 5, 5)
        >>> cell = (1, 1, 1)
        >>> mesh = df.Mesh(p1=p1, p2=p2, cell=cell)
        ...
        >>> plane_mesh = mesh.plane(y=1)

        2. Extracting the xy-plane mesh at the mesh region centre.

        >>> plane_mesh = mesh.plane('z')

        3. Specifying the number of points on the plane.

        >>> plane_mesh = mesh.plane('z', n=(3, 3))

        .. seealso:: :py:func:`~discretisedfield.Region.line`

        """
        if args and not kwargs:
            if len(args) != 1:
                msg = f'Multiple args ({args}) passed.'
                raise ValueError(msg)

            # Only planeaxis is provided via args and the point is defined as
            # the centre of the sample.
            planeaxis = dfu.axesdict[args[0]]
            point = self.region.centre[planeaxis]
        elif kwargs and not args:
            if len(kwargs) != 1:
                msg = f'Multiple kwargs ({kwargs}) passed.'
                raise ValueError(msg)

            planeaxis, point = list(kwargs.items())[0]
            planeaxis = dfu.axesdict[planeaxis]

            # Check if point is outside the mesh region.
            test_point = list(self.region.centre)  # make it mutable
            test_point[planeaxis] = point
            if test_point not in self.region:
                msg = f'Point {test_point} is outside the mesh region.'
                raise ValueError(msg)
        else:
            msg = 'Either one arg or one kwarg can be passed, not both.'
            raise ValueError(msg)

        # Get indices of in-plane axes.
        axis1, axis2 = tuple(
            filter(lambda val: val != planeaxis, dfu.axesdict.values()))

        if n is None:
            n = (self.n[axis1], self.n[axis2])

        # Build plane-mesh.
        p1pm, p2pm, npm = np.zeros(3), np.zeros(3), np.zeros(3, dtype=int)
        ilist = [axis1, axis2, planeaxis]
        p1pm[ilist] = (self.region.pmin[axis1], self.region.pmin[axis2],
                       point - self.cell[planeaxis] / 2)
        p2pm[ilist] = (self.region.pmax[axis1], self.region.pmax[axis2],
                       point + self.cell[planeaxis] / 2)
        npm[ilist] = (*n, 1)

        plane_mesh = self.__class__(p1=p1pm, p2=p2pm, n=dfu.array2tuple(npm))

        # Add info dictionary, so that the mesh can be interpreted easier.
        info = dict()
        info['planeaxis'] = planeaxis
        info['point'] = point
        info['axis1'], info['axis2'] = axis1, axis2
        plane_mesh.info = info

        return plane_mesh
Ejemplo n.º 15
0
    def plane(self, *args, n=None, **kwargs):
        """Slices the mesh with a plane.

        If one of the axes (`'x'`, `'y'`, or `'z'`) is passed as a
        string, a plane perpendicular to that axis is generated which
        intersects the mesh at its centre. Alternatively, if a keyword
        argument is passed (e.g. `x=1`), a plane perpendicular to the
        x-axis and intersecting it at x=1 is generated. The number of
        points in two dimensions on the plane can be defined using `n`
        (e.g. `n=(10, 15)`). Using the generated plane, a new
        "two-dimensional" mesh is created and returned.

        Parameters
        ----------
        n : tuple of length 2
            The number of points on the plane in two dimensions

        Returns
        ------
        discretisedfield.Mesh
            A mesh obtained as an intersection of mesh and the plane.

        Examples
        --------
        1. Intersecting the mesh with a plane.

        >>> import discretisedfield as df
        ...
        >>> p1 = (0, 0, 0)
        >>> p2 = (2, 2, 2)
        >>> cell = (1, 1, 1)
        >>> mesh = df.Mesh(p1=p1, p2=p2, cell=cell)
        >>> mesh.plane(y=1)
        Mesh(p1=(0.0, 0.5, 0.0), p2=(2.0, 1.5, 2.0), ...)

        """
        info = dfu.plane_info(*args, **kwargs)

        if info['point'] is None:
            # Plane info was extracted from args.
            info['point'] = self.centre[info['planeaxis']]
        else:
            # Plane info was extracted from kwargs and should be
            # tested whether it is inside the mesh.
            test_point = list(self.centre)
            test_point[info['planeaxis']] = info['point']
            if test_point not in self:
                msg = f'Point {test_point} is outside the mesh.'
                raise ValueError(msg)

        # Determine the n tuple.
        if n is None:
            n = (self.n[info['axis1']], self.n[info['axis2']])

        # Build a mesh.
        p1s, p2s, ns = np.zeros(3), np.zeros(3), np.zeros(3)
        ilist = [info['axis1'], info['axis2'], info['planeaxis']]
        p1s[ilist] = (self.pmin[info['axis1']], self.pmin[info['axis2']],
                      info['point'] - self.cell[info['planeaxis']] / 2)
        p2s[ilist] = (self.pmax[info['axis1']], self.pmax[info['axis2']],
                      info['point'] + self.cell[info['planeaxis']] / 2)
        ns[ilist] = n + (1, )
        ns = dfu.array2tuple(ns.astype(int))

        plane_mesh = self.__class__(p1=p1s, p2=p2s, n=ns)
        plane_mesh.info = info  # Add info so it can be interpreted easier
        return plane_mesh