コード例 #1
0
ファイル: mapping.py プロジェクト: svandenhaute/easymap
    def apply(self, positions, rvecs=None):
        """Computes cluster centers of mass

        For periodic systems, clusters are not allowed to contain relative
        vectors which are longer than half of the smallest diagonal box vector
        component in its reduced representation (i.e. a_x, b_y, or c_z).
        A ValueError is raised if this is the case.

        Parameters
        ----------

        positions : 2darray of shape (natom, 3) [angstrom]
            atomic positions.

        rvecs : 2darray of shape (3, 3) [angstrom] or None
            box vectors of the configuration, if applicable.

        """
        dvecs = self.deltas @ positions
        if rvecs is not None: # apply mic to dvecs
            apply_mic(dvecs, rvecs)
            rcut = determine_rcut(rvecs)
            if not np.all(np.linalg.norm(dvecs, axis=1) < rcut):
                raise ValueError('Maximum allowed size of distance vectors'
                        ' was exceeded (rcut = {}).'.format(rcut))
        positions_com = np.zeros((self.nclusters, 3))
        for i, group in enumerate(self):
            positions_com[i, :] = positions[group[0], :].copy()
        positions_com += self.transform @ dvecs
        return positions_com
コード例 #2
0
ファイル: test_utils.py プロジェクト: svandenhaute/easymap
def test_mic():
    length = 20
    rvecs = length * np.eye(3)
    rvecs[1, 0] = 2 * length + 0.2  # create strongly triclinic box
    vectors = np.random.uniform(-6, 6, size=(10000, 3))
    deltas = vectors.copy()
    apply_mic(deltas, rvecs)  # vectors already satisfy the mic
    assert np.allclose(vectors, deltas)
    deltas += np.random.randint(-3, 3, size=(1, 3)).dot(rvecs)
    apply_mic(deltas, rvecs)  # vectors already satisfy the mic
    assert np.allclose(vectors, deltas)
コード例 #3
0
ファイル: test_utils.py プロジェクト: svandenhaute/easymap
def test_distance_matrix():
    natoms = 10
    positions = np.random.uniform(-4, 4, size=(natoms, 3))
    distances = np.zeros((natoms, natoms))
    for i in range(natoms):
        for j in range(natoms):
            distances[i, j] = np.linalg.norm(positions[i, :] - positions[j, :])
    assert np.allclose(distances, compute_distance_matrix(positions))

    distances = np.zeros((natoms, natoms))
    rvecs = 6 * np.eye(3) + np.random.uniform(-1, 1, size=(3, 3))
    for i in range(natoms):
        for j in range(natoms):
            delta = positions[i, :] - positions[j, :]
            apply_mic(delta.reshape(1, 3), rvecs)
            distances[i, j] = np.linalg.norm(delta)
    assert np.allclose(distances, compute_distance_matrix(positions, rvecs))
コード例 #4
0
ファイル: mapping.py プロジェクト: svandenhaute/easymap
    def check_mic(self, positions, rvecs, translate_atoms=True):
        """Verifies whether clusters are sufficiently small as to apply the mic

        For each cluster, the largest relative vector between member atoms is
        determined and compared with the allowed cutoff of the unit cell.

        Parameters
        ----------

        positions : 2darray of shape (natom, 3) [angstrom]
            atomic positions.

        rvecs : 2darray of shape (3, 3) [angstrom]
            box vectors of the configuration

        translate_atoms : bool
            determines whether to translate atoms such that individual
            clusters do not require the box vectors for the computation of their
            center of mass.

        """
        rcut = determine_rcut(rvecs)
        for group in self:
            n = len(group)
            if n > 1:
                r = group[0] # take random central atom as reference
                deltas = np.zeros((n, self.natoms))
                for i in range(n):
                    deltas[i, r] -= 1
                    deltas[i, group[i]] += 1 # becomes zero for index == r
                dvecs = deltas @ positions
                apply_mic(dvecs, rvecs)
                group_pos = np.zeros((n, 3)) # construct pos with dvecs
                group_pos = positions[r, :].reshape((1, 3)) + dvecs
                if translate_atoms:
                    positions[group, :] = group_pos[:]
                # iterate over all relative vectors
                for k in range(n):
                    for l in range(n):
                        d = np.linalg.norm(group_pos[k, :] - group_pos[l, :])
                        if d > rcut:
                            return False
        return True
コード例 #5
0
def generate_environments(mapping, harmonic, cutoff, tol=1e-1):
    """Generates ``ClusterEnvironment`` instances based on cluster positions

    An ASE neighborlist object is used to identify clusters within a certain
    cutoff radius. Care is taken to ensure that no cluster resides close to
    the cutoff radius, as this may cause similar environments to contain
    a different number of atoms. To achieve this, a 'radius' is determined that
    is smaller than or equal to the cutoff but for which it is guaranteed that
    clusters are far enough from the boundary and do not cause any issues.
    The radius is determined per cluster_type.

    Parameters
    ----------

    mapping : easymap.Mapping
        mapping for which to generate a list of candidates

    harmonic : easymap.Harmonic
        contains the structure based on which the neighbor list is generated

    cutoff: float [angstrom]
        cutoff of the neighborlist used to construct the environment

    tol : float [angstrom]
        distance threshold above which atoms are considered distinguishable


    """
    logger.debug('\tgenerating neighbor list')
    cell = harmonic.atoms.get_cell()
    if np.allclose(cell.lengths(), np.zeros(3)):
        rvecs = None
    else:
        rvecs = cell
    positions = mapping.apply(harmonic.atoms.get_positions(), rvecs)
    nlist = get_nlist(positions, rvecs, cutoff=cutoff)

    # build lookup table of cluster_types
    table = {}
    for i, cluster_type in mapping.cluster_types.items():
        if cluster_type not in table.keys():
            table[cluster_type] = []
        table[cluster_type].append(i)

    environments = {}  # environments per cluster_type
    radii = {}  # determined environment radii per cluster_type
    for cluster_type, cluster_indices in table.items():
        logger.debug(
            '\tbuilding environments for cluster type {}'.format(cluster_type))
        environments[cluster_type] = []
        indices_distances = []
        for i in cluster_indices:  # parse nlist for every cluster
            indices, _ = nlist.get_neighbors(i)
            if len(indices) > 0:
                _dist_indices = np.array([(i, j) for j in indices])
                distances = get_distances(_dist_indices, positions, rvecs)
                indices_distances.append((i, indices, distances), )
            else:  # add empty entry
                indices_distances.append((i, np.array([]), np.array([])))

        # search for proper radius value based on all distances
        _all_distances = [d for (_, __, d) in indices_distances]
        _all_distances = np.concatenate(_all_distances)
        radius = cutoff - 2 * tol  # start safely below cutoff
        if len(_all_distances) > 0:
            while np.min(np.abs(_all_distances - radius)) < 2 * tol:
                radius -= 0.1 * tol
        radii[cluster_type] = radius
        logger.debug('\tdetermined safe radius at {} angstrom'.format(radius))
        for i, indices, distances in indices_distances:
            within_radius = indices[np.where(distances < radius)[0]].astype(
                np.int32)
            within_radius_all = np.concatenate((
                np.array([i]),
                within_radius,
            ))
            types = [mapping.cluster_types[j] for j in within_radius_all]
            pos = positions[within_radius_all]
            if rvecs is not None:  # apply minimum image convention
                deltas = pos - positions[i, :].reshape(1, -1)
                apply_mic(deltas, rvecs)
                assert np.allclose(np.zeros(3), deltas[0])
                pos = deltas + positions[i, :].reshape(1, -1)
            env = ClusterEnvironment(
                pos,
                types,
                within_radius_all,
            )
            environments[cluster_type].append(env)
    return environments, radii