Ejemplo n.º 1
0
    def get_basis_indices(self):
        """Returns the indices of the atoms that were found to belong to a unit
        cell basis in the LinkedUnits in this collection as a single list.

        Returns:
            np.ndarray: Indices of the atoms in the original system that belong
            to this collection of LinkedUnits.
        """
        if self._basis_indices is None:
            with chronic.Timer("chemical_environment_translations"):
                translations, translations_reduced = self.get_chem_env_translations(
                )

            # For each atom in the basis check the chemical environment
            with chronic.Timer("chemical_environment_basis"):
                neighbour_map = self.get_basis_atom_neighbourhood()

            with chronic.Timer("chemical_environment_units"):
                indices = set()
                for unit in self.values():

                    # Compare the chemical environment near this atom to the one
                    # that is present in the prototype cell. If these
                    # neighbourhoods are too different, then the atom is not
                    # counted as being a part of the region.
                    for i_index, index in enumerate(unit.basis_indices):
                        if index is not None:
                            real_environment = self.get_chemical_environment(
                                self.system, index, self.disp_tensor_finite,
                                translations, translations_reduced)
                            ideal_environment = neighbour_map[i_index]
                            chem_similarity = self.get_chemical_similarity(
                                ideal_environment, real_environment)
                            if chem_similarity >= self.chem_similarity_threshold:
                                indices.add(index)

            # Ensure that all the basis atoms belong to the same cluster.
            # clusters = self.get_clusters()
            self._basis_indices = np.array(list(indices))

        return self._basis_indices
    def test_concurrent(self):
        w = GeventWorker(self.create_queue_with_two_jobs(), pool_size=2)
        with chronic.Timer('default'):
            w.work(burst=True)

        self.assertLess(chronic.timings['default']['total_elapsed'], 1.5)
    def test_sequence(self):
        w = GeventWorker(self.create_queue_with_two_jobs(), pool_size=1)
        with chronic.Timer('default'):
            w.work(burst=True)

        self.assertGreater(chronic.timings['default']['total_elapsed'], 2.0)
Ejemplo n.º 4
0
    def classify(self, input_system):
        """A function that analyzes the system and breaks it into different
        components.

        Args:
            system(ASE.Atoms or System): Atomic system to classify.

        Returns:
            Classification: One of the subclasses of the Classification base
            class that represents a classification.

        Raises:
            ValueError: If the system has more atoms than self.max_n_atoms
        """
        # We wrap the positions to to be inside the cell.
        system = input_system.copy()
        system.wrap()
        self.system = system
        classification = None

        n_atoms = len(system)
        if n_atoms > self.max_n_atoms:
            raise ValueError(
                "The system contains more atoms ({}) than the current allowed "
                "limit of {}. If you wish you can increase this limit with the "
                "max_n_atoms attribute.".format(n_atoms, self.max_n_atoms)
            )

        # Calculate the displacement tensor for the original system. It will be
        # reused in multiple sections.
        pos = system.get_positions()
        cell = system.get_cell()
        pbc = system.get_pbc()

        with chronic.Timer("displacement_tensor"):
            disp_tensor = matid.geometry.get_displacement_tensor(pos, pos)
            if pbc.any():
                disp_tensor_pbc, disp_factors = matid.geometry.get_displacement_tensor(
                    pos,
                    pos,
                    cell,
                    pbc,
                    mic=True,
                    return_factors=True
                )
            else:
                disp_tensor_pbc = disp_tensor
                disp_factors = np.zeros(disp_tensor.shape)
            dist_matrix_pbc = np.linalg.norm(disp_tensor_pbc, axis=2)

        # Calculate the distance matrix where the periodicity and the covalent
        # radii have been taken into account
        dist_matrix_radii_pbc = np.array(dist_matrix_pbc)
        num = system.get_atomic_numbers()
        radii = covalent_radii[num]
        radii_matrix = radii[:, None] + radii[None, :]
        dist_matrix_radii_pbc -= radii_matrix

        # If pos_tol_mode or delaunay_threshold_mode is relative, get the
        # average distance to closest neighbours
        if self.pos_tol_mode == "relative" or self.delaunay_threshold_mode == "relative":
            min_basis = np.linalg.norm(cell, axis=1).min()
            dist_matrix_mod = np.array(dist_matrix_pbc)
            np.fill_diagonal(dist_matrix_mod, min_basis)
            global_min_dist = dist_matrix_mod.min()
            min_dist = np.min(dist_matrix_mod, axis=1)
            mean_min_dist = min_dist.mean()

            if self.pos_tol_mode == "relative":
                self.abs_pos_tol = np.array(self.pos_tol)*global_min_dist
            elif self.pos_tol_mode == "absolute":
                self.abs_pos_tol = self.pos_tol

            if self.delaunay_threshold_mode == "relative":
                self.abs_delaunay_threshold = self.delaunay_threshold * mean_min_dist
            elif self.delaunay_threshold_mode == "absolute":
                self.abs_delaunay_threshold = self.delaunay_threshold

        # Get the system dimensionality
        with chronic.Timer("TSA"):
            dimensionality = matid.geometry.get_dimensionality(
                system,
                self.cluster_threshold,
                dist_matrix_radii_pbc
            )
            if dimensionality is None:
                return Unknown(input_system)

        # 0D structures
        if dimensionality == 0:
            classification = Class0D(input_system)

            # Systems with one atom have their own classification.
            n_atoms = len(system)
            if n_atoms == 1:
                classification = Atom(input_system)

        # 1D structures
        elif dimensionality == 1:
            classification = Class1D(input_system)

        # 2D structures
        elif dimensionality == 2:

            classification = Class2D(input_system)

            # Get the indices of the used seed atoms
            seed_indices = []
            test_sys = system.copy()
            cm = matid.geometry.get_center_of_mass(test_sys)

            # If center of mass defined, for each atomic element find the
            # occurrence closest to center of mass to use as seed point.
            num = self.system.get_atomic_numbers()
            elems = set(num)

            if self.seed_position == "cm":
                distances = np.linalg.norm(system.get_positions() - cm, axis=1)
                indices = np.argsort(distances)
                for i in indices:
                    i_elem = num[i]
                    if i_elem in elems:
                        seed_indices.append(i)
                        elems.remove(i_elem)
                    if len(elems) == 0:
                        break
            else:
                if type(self.seed_position) == int:
                    seed_indices = [self.seed_position]
                elif isinstance(self.seed_position, (tuple, list, np.ndarray)):
                    seed_indices = self.seed_position

            # Find the best region by trying out different parameters options
            with chronic.Timer("cross_validation"):
                best_region = self.cross_validate_region(
                    system,
                    seed_indices,
                    disp_tensor_pbc,
                    disp_factors,
                    disp_tensor,
                    dist_matrix_radii_pbc
                )

            if best_region is not None:

                with chronic.Timer("region_analysis"):

                    # Check that the region was connected cyclically in two
                    # directions. This ensures that finite systems or systems
                    # with a dislocation at the cell boundary are filtered.
                    region_conn = best_region.get_connected_directions()
                    n_region_conn = np.sum(region_conn)
                    region_is_periodic = n_region_conn == 2
                    # cell_statistically_valid = best_region.get_cell_statistically_valid()
                    # print(cell_statistically_valid)

                    # This might be unnecessary because the connectivity of the
                    # unit cell is already checked.
                    clusters = best_region.get_clusters()
                    basis_indices = set(list(best_region.get_basis_indices()))
                    split = True
                    for cluster in clusters:
                        if basis_indices.issubset(cluster):
                            split = False

                    # Check that the found region covers enough of the entire
                    # system. If not, then the region alone cannot be used to
                    # classify the entire structure. This happens e.g. when one
                    # 2D sheet is found from a 2D heterostructure, or a local
                    # pattern is found inside a structure.
                    n_atoms = len(system)
                    n_basis_atoms = len(basis_indices)
                    coverage = n_basis_atoms/n_atoms
                    covered = coverage >= self.min_coverage

                    if not split and covered and region_is_periodic:
                        if best_region.is_2d:
                            classification = Material2D(input_system, best_region)
                        else:
                            classification = Surface(input_system, best_region)

        # Bulk structures
        elif dimensionality == 3:

            classification = Class3D(input_system)

            # Check the number of symmetries
            # analyzer = SymmetryAnalyzer(system)
            # crystallinity = matid.geometry.get_crystallinity(analyzer)
            # is_crystal = crystallinity >= self.crystallinity_threshold

            # If the structure is connected but the symmetry criteria was
            # not fullfilled, check the number of atoms in the primitive
            # cell. If above a certain threshold, try to find periodic
            # region to see if it is a crystal containing a defect.
            # if not is_crystal:
                # pass

                # This section is currently disabled. Can be reenabled once
                # more extensive testing is carried out on the detection of
                # defects in crystals.

                # primitive_system = analyzer.get_primitive_system()
                # n_atoms_prim = len(primitive_system)
                # if n_atoms_prim >= 20:
                    # periodicfinder = PeriodicFinder(
                        # pos_tol=self.abs_pos_tol,
                        # angle_tol=self.angle_tol,
                        # max_cell_size=self.max_cell_size,
                        # pos_tol_factor=self.pos_tol_factor,
                        # cell_size_tol=self.cell_size_tol,
                    # )

                    # # Get the index of the seed atom
                    # if self.seed_position == "cm":
                        # seed_vec = self.system.get_center_of_mass()
                    # else:
                        # seed_vec = self.seed_position
                    # seed_index = matid.geometry.get_nearest_atom(self.system, seed_vec)

                    # region = periodicfinder.get_region(system, seed_index, disp_tensor_pbc, disp_tensor, self.abs_delaunay_threshold)
                    # if region is not None:
                        # region = region[1]

                        # # If all the regions cover at least 80% of the structure,
                        # # then we consider it to be a defected crystal
                        # n_region_atoms = len(region.get_basis_indices())
                        # n_atoms = len(system)
                        # coverage = n_region_atoms/n_atoms
                        # if coverage >= self.coverage_threshold:
                            # classification = Crystal(analyzer, region=region)

            # elif is_crystal:
                # classification = Crystal(analyzer)

        return classification