Example #1
0
 def test_exceptions(self):
     """Check exceptions, the new ones specific to PeriodicGrid."""
     # init
     with pytest.raises(ValueError):
         PeriodicGrid(self._ref_points, self._ref_weights, np.ones(
             (1, 100)))
     with pytest.raises(ValueError):
         if self._ref_points.ndim == 1:
             PeriodicGrid(self._ref_points, self._ref_weights, np.ones(100))
         else:
             PeriodicGrid(
                 self._ref_points,
                 self._ref_weights,
                 np.ones((100, self._ref_points.shape[1])),
             )
     if self._ref_realvecs is not None and self._ref_realvecs.shape[0] > 1:
         with pytest.raises(ValueError):
             realvecs = self._ref_realvecs.copy()
             realvecs[0] = realvecs[1]
             PeriodicGrid(self._ref_points, self._ref_weights, realvecs)
     # get_localgrid
     with pytest.raises(ValueError):
         self.grid.get_localgrid(np.zeros(100), 3.0)
     with pytest.raises(ValueError):
         self.grid.get_localgrid(self.grid.points[4], np.inf)
     with pytest.raises(ValueError):
         self.grid.get_localgrid(self.grid.points[4], -np.nan)
     with pytest.raises(ValueError):
         self.grid.get_localgrid(self.grid.points[4], -1.0)
Example #2
0
 def setup(self):
     """Initialize an unwrapped and a wrapped version of the grid."""
     self.define_reference_data()
     with pytest.warns(
             None if self._ref_realvecs is None else PeriodicGridWarning):
         self.grid = PeriodicGrid(self._ref_points, self._ref_weights,
                                  self._ref_realvecs)
     with pytest.warns(None) as record:
         self.wrapped_grid = PeriodicGrid(self._ref_points,
                                          self._ref_weights,
                                          self._ref_realvecs, True)
         assert len(record) == 0
Example #3
0
def test_tutorial_periodic_repetition(delta_p, delta_c):
    """Test example of the periodic repetition of a local non-periodic function.

    The 1D grid has a periodicity of a=0.5. On this grid, a periodic repetition
    of a Gaussian function is evaluated and tested for consistency.
    """
    a = 0.5276
    # Our grid covers multiple primitive cells, which is not great for
    # efficiency, but it still works. Due to the efficiency issue, a warning
    # is raised. To fix this issue, any may simply add the ``wrap=True``
    # argument to the constructor. (This modifies the grid points.)
    with pytest.warns(PeriodicGridWarning):
        grid = PeriodicGrid(
            np.linspace(-1, 1, 21) + delta_p, np.full(21, 0.1), np.array([a])
        )
    # The local grid is wider than one primitive cell, such that there will be
    # more points in the local grid than in the periodic grid. The test is repeated
    # with two displacements of the center by an integer multiple of the
    # periodic unit.
    center = 1.8362 - delta_c
    radius = 2.1876
    fn_old = None
    for _ in range(3):
        localgrid = grid.get_localgrid(center, radius)
        assert localgrid.size > grid.size
        # Compute one Gaussian, which is to be periodically repeated.
        localfn = np.exp(-20 * (localgrid.points - center) ** 2)
        # Construct the periodic model on grid points of one primitive cell.
        # Mind the ``np.add.at`` line. This is explained in the documentation of
        # PeriodicGrid.__init__.
        fn = np.zeros(grid.size)
        np.add.at(fn, localgrid.indices, localfn)
        # Compare the periodic function to the result with the previous value
        # of center. It should be the same because the center was translated by
        # exactly one periodic unit.
        if fn_old is not None:
            assert_allclose(fn, fn_old)
        fn_old = fn
        # The integral over one primitive cell should be the same as the integral
        # over one isolated Gaussian.
        assert_allclose(localgrid.integrate(localfn), grid.integrate(fn))
        # Manually construct the periodic repetition and compare.
        fn2 = np.zeros(grid.size)
        jmin = np.ceil((grid.points.min() - center - radius) / a).astype(int)
        jmax = np.floor((grid.points.max() - center + radius) / a).astype(int)
        assert jmax >= jmin
        for j in range(jmin, jmax + 1):
            center_translated = center + a * j
            fn2 += np.exp(-20 * (grid.points - center_translated) ** 2)
        assert_allclose(fn2, fn)
        # Modify the center for the next iteration.
        center += a
Example #4
0
 def test_get_item(self):
     """Test the __getitem__ method."""
     # test index
     grid_index = self.grid[10]
     ref_grid_index = PeriodicGrid(self._ref_points[10:11],
                                   self._ref_weights[10:11],
                                   self._ref_realvecs)
     assert_allclose(grid_index.points, ref_grid_index.points)
     assert_allclose(grid_index.weights, ref_grid_index.weights)
     assert_allclose(grid_index.realvecs, self.grid.realvecs)
     assert_allclose(grid_index.recivecs, self.grid.recivecs)
     assert_allclose(grid_index.spacings, self.grid.spacings)
     assert_allclose(grid_index.frac_intvls, ref_grid_index.frac_intvls)
     assert isinstance(grid_index, PeriodicGrid)
     # test slice
     with pytest.warns(
             None if self._ref_realvecs is None else PeriodicGridWarning):
         ref_grid_slice = PeriodicGrid(self._ref_points[:15],
                                       self._ref_weights[:15],
                                       self._ref_realvecs)
     with pytest.warns(
             None if self._ref_realvecs is None else PeriodicGridWarning):
         grid_slice = self.grid[:15]
     assert_allclose(grid_slice.points, ref_grid_slice.points)
     assert_allclose(grid_slice.weights, ref_grid_slice.weights)
     assert_allclose(grid_slice.realvecs, self.grid.realvecs)
     assert_allclose(grid_slice.recivecs, self.grid.recivecs)
     assert_allclose(grid_slice.spacings, self.grid.spacings)
     assert_allclose(grid_slice.frac_intvls, ref_grid_slice.frac_intvls)
     assert isinstance(grid_slice, PeriodicGrid)
     # test take with integers
     indices = np.array([1, 3, 5])
     grid_itake = self.grid[indices]
     assert_allclose(grid_itake.points, self._ref_points[indices])
     assert_allclose(grid_itake.weights, self._ref_weights[indices])
     assert_allclose(grid_itake.realvecs, self.grid.realvecs)
     assert_allclose(grid_itake.recivecs, self.grid.recivecs)
     assert_allclose(grid_itake.spacings, self.grid.spacings)
     assert isinstance(grid_itake, PeriodicGrid)
     # test take with boolean mask
     mask = np.zeros(self.grid.size, dtype=bool)
     mask[1] = True
     mask[3] = True
     mask[5] = True
     grid_mtake = self.grid[mask]
     assert_allclose(grid_mtake.points, grid_itake.points)
     assert_allclose(grid_mtake.weights, grid_itake.weights)
     assert_allclose(grid_mtake.realvecs, self.grid.realvecs)
     assert_allclose(grid_mtake.recivecs, self.grid.recivecs)
     assert_allclose(grid_mtake.spacings, self.grid.spacings)
     assert isinstance(grid_mtake, PeriodicGrid)
Example #5
0
def setup_equidistant_grid(origin, realvecs, npts):
    """Define a periodic grid with equally spaced grid points.

    The relative vectors between neigbouring grid points are constant and equal
    to the real-space lattice vectors divided by the number of grid points along
    one lattice vector.

    At the moment, this function is just used for testing, but this might be
    useful to generate grids for cube files and for other types of
    visualization.

    Parameters
    ----------
    origin: np.array(shape=(ndim, ), dtype=float)
        The position of the first grid point.
    realvecs: np.array(shape=(ndim, ndim), dtype=float)
        The real-space lattice vectors.
    ntps: np.array(shape=ndim, dtype=int)
        The number of grid points along each lattice vector.

    Returns
    -------
    PeriodicGrid
        The periodic grid with equidistant points.

    """
    # First build grid points in fractional coordinates and then transform
    # to real space.
    fractional_tuple = np.meshgrid(*[np.arange(npt) / npt for npt in npts])
    points = (
        sum(
            [
                np.outer(fractional.ravel(), realvec)
                for fractional, realvec in zip(fractional_tuple, realvecs)
            ]
        )
        + origin
    )
    # The weights take into account the Jacobian of the affine transformation
    # from fractional to Cartesian grid points.
    npt_total = np.product(npts)
    weights = np.full(npt_total, abs(np.linalg.det(realvecs)) / npt_total)
    return PeriodicGrid(points, weights, realvecs)
Example #6
0
class PeriodicGridTester:
    """Base class for PeriodicGrid test cases.

    Subclasses should override the define_reference_data method, which sets
    the following attributes:

    - ``self._ref_points``: grid points
    - ``self._ref_weights``: grid weights
    - ``self._ref_realvecs``: cell vectors
    - ``self._ref_recivecs``: the expected reciprocal cell vectors
    - ``self._ref_spacings``: the expected spacings between crystal planes
    - ``self._ref_frac_intvls``: the expected intervals of fractional
      coordinates.

    """
    def setup(self):
        """Initialize an unwrapped and a wrapped version of the grid."""
        self.define_reference_data()
        with pytest.warns(
                None if self._ref_realvecs is None else PeriodicGridWarning):
            self.grid = PeriodicGrid(self._ref_points, self._ref_weights,
                                     self._ref_realvecs)
        with pytest.warns(None) as record:
            self.wrapped_grid = PeriodicGrid(self._ref_points,
                                             self._ref_weights,
                                             self._ref_realvecs, True)
            assert len(record) == 0

    def define_reference_data(self):
        """Define reference data for the test."""
        raise NotImplementedError

    def test_init_grid(self):
        """Test PeriodicGrid init."""
        assert_allclose(self.grid.points, self._ref_points, atol=1e-7)
        assert_allclose(self.grid.frac_intvls, self._ref_frac_intvls)
        assert (self.wrapped_grid.frac_intvls[:, 0] >= 0).all()
        assert (self.wrapped_grid.frac_intvls[:, 1] <= 1).all()
        if self._ref_points.ndim == 2:
            frac_points = self.wrapped_grid.points @ self.wrapped_grid.recivecs.T
        else:
            frac_points = self.wrapped_grid.points * self.wrapped_grid.recivecs
        assert (frac_points >= 0).all
        assert (frac_points <= 1).all
        for grid in self.grid, self.wrapped_grid:
            assert isinstance(grid, PeriodicGrid)
            assert_allclose(grid.weights, self._ref_weights, atol=1e-7)
            if self._ref_realvecs is None:
                assert_allclose(grid.realvecs, self._ref_recivecs, atol=1e-10)
            else:
                assert_allclose(grid.realvecs, self._ref_realvecs, atol=1e-10)
            assert_allclose(grid.recivecs, self._ref_recivecs, atol=1e-10)
            assert_allclose(grid.spacings, self._ref_spacings, atol=1e-10)
            assert grid.size == self._ref_weights.size

    def test_get_item(self):
        """Test the __getitem__ method."""
        # test index
        grid_index = self.grid[10]
        ref_grid_index = PeriodicGrid(self._ref_points[10:11],
                                      self._ref_weights[10:11],
                                      self._ref_realvecs)
        assert_allclose(grid_index.points, ref_grid_index.points)
        assert_allclose(grid_index.weights, ref_grid_index.weights)
        assert_allclose(grid_index.realvecs, self.grid.realvecs)
        assert_allclose(grid_index.recivecs, self.grid.recivecs)
        assert_allclose(grid_index.spacings, self.grid.spacings)
        assert_allclose(grid_index.frac_intvls, ref_grid_index.frac_intvls)
        assert isinstance(grid_index, PeriodicGrid)
        # test slice
        with pytest.warns(
                None if self._ref_realvecs is None else PeriodicGridWarning):
            ref_grid_slice = PeriodicGrid(self._ref_points[:15],
                                          self._ref_weights[:15],
                                          self._ref_realvecs)
        with pytest.warns(
                None if self._ref_realvecs is None else PeriodicGridWarning):
            grid_slice = self.grid[:15]
        assert_allclose(grid_slice.points, ref_grid_slice.points)
        assert_allclose(grid_slice.weights, ref_grid_slice.weights)
        assert_allclose(grid_slice.realvecs, self.grid.realvecs)
        assert_allclose(grid_slice.recivecs, self.grid.recivecs)
        assert_allclose(grid_slice.spacings, self.grid.spacings)
        assert_allclose(grid_slice.frac_intvls, ref_grid_slice.frac_intvls)
        assert isinstance(grid_slice, PeriodicGrid)
        # test take with integers
        indices = np.array([1, 3, 5])
        grid_itake = self.grid[indices]
        assert_allclose(grid_itake.points, self._ref_points[indices])
        assert_allclose(grid_itake.weights, self._ref_weights[indices])
        assert_allclose(grid_itake.realvecs, self.grid.realvecs)
        assert_allclose(grid_itake.recivecs, self.grid.recivecs)
        assert_allclose(grid_itake.spacings, self.grid.spacings)
        assert isinstance(grid_itake, PeriodicGrid)
        # test take with boolean mask
        mask = np.zeros(self.grid.size, dtype=bool)
        mask[1] = True
        mask[3] = True
        mask[5] = True
        grid_mtake = self.grid[mask]
        assert_allclose(grid_mtake.points, grid_itake.points)
        assert_allclose(grid_mtake.weights, grid_itake.weights)
        assert_allclose(grid_mtake.realvecs, self.grid.realvecs)
        assert_allclose(grid_mtake.recivecs, self.grid.recivecs)
        assert_allclose(grid_mtake.spacings, self.grid.spacings)
        assert isinstance(grid_mtake, PeriodicGrid)

    def test_get_localgrid_small_radius(self):
        """Basic checks for the get_localgrid method.

        In this unit test, the cutoff sphere fits inside a primitive cell, such
        that each grid point from the parent periodic grid will at most appear
        once in the local grid.
        """
        center = self.grid.points[3]
        radius = 0.19475
        # Check that the sphere fits inside the primitive cell:
        assert (2 * radius < self.grid.spacings).all()
        # Build local grids.
        localgrids = [
            self.grid.get_localgrid(center, radius),
            self.wrapped_grid.get_localgrid(center, radius),
        ]
        # When there are no lattice vectors, the local grid from the base class
        # should also be the same.
        if self._ref_realvecs is None:
            aperiodic_grid = Grid(self._ref_points, self._ref_weights)
            localgrids.append(aperiodic_grid.get_localgrid(center, radius))
        # One should get the same local grid with or without wrapping, possibly
        # with a different ordering of the points. We can perform a relatively
        # simple check here because each point appears at most once.
        order0 = localgrids[0].indices.argsort()
        for localgrid in localgrids[1:]:
            assert_allclose(localgrids[0].center, localgrid.center)
            order = localgrid.indices.argsort()
            assert_allclose(localgrids[0].points[order0],
                            localgrid.points[order])
            assert_allclose(localgrids[0].weights[order0],
                            localgrid.weights[order])
        # Other sanity checks on the grids.
        for localgrid in localgrids:
            # Just make sure we are testing with an actual local grid with at least
            # some points.
            assert localgrid.size > 0
            assert localgrid.size <= self.grid.size
            assert len(localgrid.indices) == len(set(localgrid.indices))
            # Test that the localgrid contains sensible results.
            assert_allclose(localgrid.center, center)
            assert localgrid.points.ndim == self.grid.points.ndim
            assert localgrid.weights.ndim == self.grid.weights.ndim
            if self._ref_points.ndim == 2:
                assert (np.linalg.norm(localgrid.points - center, axis=1) <=
                        radius).all()
            else:
                assert (abs(localgrid.points - center) <= radius).all()

    def test_get_localgrid_large_radius(self):
        """Basic checks for the get_localgrid method.

        In this unit test, the cutoff sphere fits inside a primitive cell, such
        that each grid point from the parent periodic grid will at most appear
        once in the local grid.
        """
        center = self.grid.points[3]
        radius = 2.51235
        # Check that the sphere flows over the primitive cell, when there are
        # some lattice vectors.
        if self._ref_realvecs is not None:
            assert (2 * radius > self.grid.spacings).any()
        # Build local grids.
        localgrids = [
            self.grid.get_localgrid(center, radius),
            self.wrapped_grid.get_localgrid(center, radius),
        ]
        # When there are no lattice vectors, the local grid from the base class
        # should also be the same.
        if self._ref_realvecs is None:
            aperiodic_grid = Grid(self._ref_points, self._ref_weights)
            localgrids.append(aperiodic_grid.get_localgrid(center, radius))
        # One should get the same local grid with or without wrapping, possibly
        # with a different ordering of the points.
        for localgrid in localgrids[1:]:
            assert_equal_localgrids(localgrids[0], localgrid)
        # Other sanity checks.
        for localgrid in localgrids:
            # With a large radius there will never be less points in the local grid.
            if self._ref_realvecs is None:
                assert localgrid.size == self.grid.size
            else:
                assert localgrid.size > self.grid.size
            # Test that the local grid contains sensible results.
            assert_allclose(localgrid.center, center)
            assert localgrid.points.ndim == self.grid.points.ndim
            assert localgrid.weights.ndim == self.grid.weights.ndim
            if self._ref_points.ndim == 2:
                assert (np.linalg.norm(localgrid.points - center, axis=1) <=
                        radius).all()
            else:
                assert (abs(localgrid.points - center) <= radius).all()

    def test_exceptions(self):
        """Check exceptions, the new ones specific to PeriodicGrid."""
        # init
        with pytest.raises(ValueError):
            PeriodicGrid(self._ref_points, self._ref_weights, np.ones(
                (1, 100)))
        with pytest.raises(ValueError):
            if self._ref_points.ndim == 1:
                PeriodicGrid(self._ref_points, self._ref_weights, np.ones(100))
            else:
                PeriodicGrid(
                    self._ref_points,
                    self._ref_weights,
                    np.ones((100, self._ref_points.shape[1])),
                )
        if self._ref_realvecs is not None and self._ref_realvecs.shape[0] > 1:
            with pytest.raises(ValueError):
                realvecs = self._ref_realvecs.copy()
                realvecs[0] = realvecs[1]
                PeriodicGrid(self._ref_points, self._ref_weights, realvecs)
        # get_localgrid
        with pytest.raises(ValueError):
            self.grid.get_localgrid(np.zeros(100), 3.0)
        with pytest.raises(ValueError):
            self.grid.get_localgrid(self.grid.points[4], np.inf)
        with pytest.raises(ValueError):
            self.grid.get_localgrid(self.grid.points[4], -np.nan)
        with pytest.raises(ValueError):
            self.grid.get_localgrid(self.grid.points[4], -1.0)