def get_fractional_positions_from_neighbor_list( structure: Structure, neighbor_list: List) -> List[Vector]: """ Returns the fractional positions of the lattice sites in structure from a neighbor list. Parameters ---------- structure input atomic structure neighbor_list list of lattice neighbors of the input structure """ neighbor_positions = [] fractional_positions = [] lattice_site = LatticeSite(0, [0, 0, 0]) for i in range(len(neighbor_list)): lattice_site.index = i position = structure.get_position(lattice_site) neighbor_positions.append(position) for neighbor in neighbor_list[i]: position = structure.get_position(neighbor) neighbor_positions.append(position) if len(neighbor_positions) > 0: fractional_positions = get_scaled_positions( np.array(neighbor_positions), structure.cell, wrap=False, pbc=structure.pbc) return fractional_positions
def setUp(self): """Setup before each test.""" self.icet_structure = Structure(positions=self.positions, chemical_symbols=self.chemical_symbols, cell=self.cell, pbc=[True, True, True]) random.seed(113)
def _get_lattice_site_matrix_of_equivalent_positions( structure: Structure, matrix_of_equivalent_positions: MatrixOfEquivalentPositions, fractional_position_tolerance: float, prune: bool = True) -> np.ndarray: """ Returns a transformed permutation matrix with lattice sites as entries instead of fractional coordinates. Parameters ---------- structure primitive atomic icet structure matrix_of_equivalent_positions permutation matrix with fractional coordinates format entries fractional_position_tolerance tolerance applied when evaluating distances in fractional coordinates prune if True the permutation matrix will be pruned Returns ------- permutation matrix in a row major order with lattice site format entries """ pm_frac = matrix_of_equivalent_positions.get_equivalent_positions() pm_lattice_sites = [] for row in pm_frac: positions = _fractional_to_cartesian(row, structure.cell) lattice_sites = [] if np.all(structure.pbc): lattice_sites = structure.find_lattice_sites_by_positions( positions=positions, fractional_position_tolerance=fractional_position_tolerance) else: for pos in positions: try: lattice_site = structure.find_lattice_site_by_position( position=pos, fractional_position_tolerance= fractional_position_tolerance) except RuntimeError: continue lattice_sites.append(lattice_site) if lattice_sites is not None: pm_lattice_sites.append(lattice_sites) else: logger.warning('Unable to transform any element in a column of the' ' fractional permutation matrix to lattice site') if prune: logger.debug('Size of columns of the permutation matrix before' ' pruning {}'.format(len(pm_lattice_sites))) pm_lattice_sites = _prune_matrix_of_equivalent_positions( pm_lattice_sites) logger.debug('Size of columns of the permutation matrix after' ' pruning {}'.format(len(pm_lattice_sites))) return pm_lattice_sites
def get_cluster_vector(self, structure: Atoms) -> np.ndarray: """ Returns the cluster vector for a structure. Parameters ---------- structure atomic configuration Returns ------- the cluster vector """ if not isinstance(structure, Atoms): raise TypeError('Input structure must be an ASE Atoms object') try: cv = _ClusterSpace.get_cluster_vector( self, structure=Structure.from_atoms(structure), fractional_position_tolerance=self.fractional_position_tolerance) except Exception as e: self.assert_structure_compatibility(structure) raise(e) return cv
def __init__(self, *args, **kwargs): super(TestGroundStateFinderZeroParameter, self).__init__(*args, **kwargs) self.chemical_symbols = ['Ag', 'Au'] self.cutoffs = [4.3] self.structure_prim = bulk(self.chemical_symbols[1], a=4.0) self.cs = ClusterSpace(self.structure_prim, self.cutoffs, self.chemical_symbols) nonzero_ce = ClusterExpansion(self.cs, [0, 0, 0.1, -0.02]) lolg = LocalOrbitListGenerator( self.cs.orbit_list, Structure.from_atoms(self.structure_prim), self.cs.fractional_position_tolerance) full_orbit_list = lolg.generate_full_orbit_list() binary_parameters_zero = transform_parameters(self.structure_prim, full_orbit_list, nonzero_ce.parameters) binary_parameters_zero[1] = 0 A = get_transformation_matrix(self.structure_prim, full_orbit_list) Ainv = np.linalg.inv(A) zero_parameters = np.dot(Ainv, binary_parameters_zero) self.ce = ClusterExpansion(self.cs, zero_parameters) self.all_possible_structures = [] self.supercell = self.structure_prim.repeat(2) for i in range(len(self.supercell)): structure = self.supercell.copy() structure.symbols[i] = self.chemical_symbols[0] self.all_possible_structures.append(structure)
def matrix_of_equivalent_positions_from_structure(structure: Atoms, cutoff: float, position_tolerance: float, symprec: float, find_primitive: bool = True) \ -> Tuple[np.ndarray, Structure, List]: """Sets up a list of permutation maps from an Atoms object. Parameters ---------- structure input structure cutoff cutoff radius find_primitive if True the symmetries of the primitive structure will be employed symprec tolerance imposed when analyzing the symmetry using spglib position_tolerance tolerance applied when comparing positions in Cartesian coordinates Returns ------- The tuple that is returned comprises the permutation matrix, the primitive structure, and the neighbor list. """ structure = structure.copy() structure_prim = structure if find_primitive: structure_prim = get_primitive_structure(structure, symprec=symprec) logger.debug('Size of primitive structure: {}'.format(len(structure_prim))) # get symmetry information structure_as_tuple = ase_atoms_to_spglib_cell(structure_prim) symmetry = spglib.get_symmetry(structure_as_tuple, symprec=symprec) translations = symmetry['translations'] rotations = symmetry['rotations'] # set up a permutation map object matrix_of_equivalent_positions = MatrixOfEquivalentPositions( translations, rotations) # create neighbor_lists from the different cutoffs prim_icet_structure = Structure.from_atoms(structure_prim) neighbor_list = get_neighbor_lists( prim_icet_structure, [cutoff], position_tolerance=position_tolerance)[0] # get fractional positions for neighbor_list frac_positions = get_fractional_positions_from_neighbor_list( prim_icet_structure, neighbor_list) logger.debug('Number of fractional positions: {}'.format( len(frac_positions))) if frac_positions is not None: matrix_of_equivalent_positions.build(frac_positions) return matrix_of_equivalent_positions, prim_icet_structure, neighbor_list
def test_neighbors_non_pbc(self): """ Tests indices and offset of neighborlist for a non-pbc structure under the same cutoff as above. """ structure = self.structure.copy() structure.pbc = [True, True, False] structure.center(4.0, axis=[2]) neighbors = get_neighbor_lists( structure=Structure.from_atoms(structure), cutoffs=[self.cutoff]) indices = [ngb.index for ngb in neighbors[0][0]] offsets = [ngb.unitcell_offset for ngb in neighbors[0][0]] self.ase_nl.update(structure) ase_indices, ase_offsets = self.ase_nl.get_neighbors(0) for index in indices: self.assertIn(index, ase_indices) for offset in offsets: self.assertIn(offset, ase_offsets) self.assertLess(len(indices), len(self.indices)) self.assertLess(len(offsets), len(self.offsets))
def __init__(self, cluster_space, structure: Atoms, interval: int = None, max_orbit: int = None) -> None: super().__init__(interval=interval, return_type=dict, tag='ClusterCountObserver') self._cluster_space = cluster_space local_orbit_list_generator = LocalOrbitListGenerator( orbit_list=cluster_space.orbit_list, structure=Structure.from_atoms(structure), fractional_position_tolerance=cluster_space. fractional_position_tolerance) self._full_orbit_list = local_orbit_list_generator.generate_full_orbit_list( ) self._cluster_counts_cpp = _ClusterCounts() if max_orbit is None: self._max_orbit = len(self._full_orbit_list) else: self._max_orbit = max_orbit self._cluster_keys = [] # type: List[Orbit] for i, orbit in enumerate(self._full_orbit_list.orbits): cluster = orbit.representative_cluster cluster.tag = i self._cluster_keys.append(cluster) self._empty_counts = self._get_empty_counts()
def __init__(self, orbit_list: OrbitList, structure: Atoms, fractional_position_tolerance: float): self._orbit_list = orbit_list self._structure = Structure.from_atoms(structure) # call (base) C++ constructor _ClusterCounts.__init__(self) self.cluster_counts = self._count_clusters( fractional_position_tolerance=fractional_position_tolerance)
def test_structure_to_atoms(self): """Tests icet Structure-to-ASE Atoms conversion.""" ase_structure = Structure.to_atoms(self.icet_structure) for ase_pos, icet_pos in zip(ase_structure.positions, self.icet_structure.positions): self.assertListEqual(ase_pos.tolist(), icet_pos.tolist()) chem_symbols = ase_structure.get_chemical_symbols() self.assertListEqual(chem_symbols, ['Ag', 'Ag'])
def test_structure_from_atoms(self): """Tests ASE Atoms-to-icet Structure conversion.""" icet_structure = Structure.from_atoms(self.ase_atoms) for icet_pos, ase_pos in zip(icet_structure.positions, self.ase_atoms.positions): self.assertListEqual(icet_pos.tolist(), ase_pos.tolist()) chem_symbols = icet_structure.get_chemical_symbols() self.assertListEqual(chem_symbols, ['Ag', 'Ag'])
def __init__(self, *args, **kwargs): super(TestOrbitList, self).__init__(*args, **kwargs) self.cutoffs = [4.2] self.symprec = 1e-5 self.position_tolerance = 1e-5 self.fractional_position_tolerance = 1e-6 self.structure = bulk('Ag', 'sc', a=4.09) # representative clusters for testing # for singlet self.cluster_singlet = Cluster( Structure.from_atoms(self.structure), [LatticeSite(0, [0, 0, 0])]) # for pair lattice_sites = [LatticeSite(0, [i, 0, 0]) for i in range(3)] self.cluster_pair = Cluster(Structure.from_atoms(self.structure), [lattice_sites[0], lattice_sites[1]], True)
def __init__(self, *args, **kwargs): super(TestNeighborList, self).__init__(*args, **kwargs) self.structure = bulk('Ni', 'hcp', a=1.0).repeat([3, 3, 1]) self.cutoff = 1.4 self.position_tolerance = 1e-5 self.icet_structure = Structure.from_atoms(self.structure) self.ase_nl = ASENeighborList(len(self.structure) * [self.cutoff / 2], skin=1e-8, bothways=True, self_interaction=False) self.ase_nl.update(self.structure) self.ase_indices, self.ase_offsets = self.ase_nl.get_neighbors(0)
def __init__(self, allowed_species: List[List[str]], primitive_structure: Atoms, structure: Atoms, fractional_position_tolerance: float): self._structure = structure # sorted unique sites, this basically decides A, B, C... sublattices active_lattices = sorted( set([ tuple(sorted(symbols)) for symbols in allowed_species if len(symbols) > 1 ])) inactive_lattices = sorted( set([ tuple(sorted(symbols)) for symbols in allowed_species if len(symbols) == 1 ])) self._allowed_species = active_lattices + inactive_lattices n = int(np.sqrt(len(self._allowed_species))) + 1 symbols = [ ''.join(p) for r in range(1, n + 1) for p in product(ascii_uppercase, repeat=r) ] cpp_prim_structure = Structure.from_atoms(primitive_structure) self._sublattices = [] sublattice_to_indices = [[] for _ in range(len(self._allowed_species))] for index, position in enumerate(structure.positions): lattice_site = cpp_prim_structure.find_lattice_site_by_position( position=position, fractional_position_tolerance=fractional_position_tolerance) # Get allowed species on this site species = allowed_species[lattice_site.index] # Get what sublattice those species correspond to sublattice = self._allowed_species.index(tuple(sorted(species))) sublattice_to_indices[sublattice].append(index) for symbol, species, indices in zip(symbols, self._allowed_species, sublattice_to_indices): sublattice = Sublattice(chemical_symbols=species, indices=indices, symbol=symbol) self._sublattices.append(sublattice) # Map lattice index to sublattice index self._index_to_sublattice = {} for k, sublattice in enumerate(self): for index in sublattice.indices: self._index_to_sublattice[index] = k
def test_find_lattice_site_by_position_with_tolerance(self): """Tests the find lattice site by position method by varying the tolerance """ atoms = bulk('Al', crystalstructure='hcp', a=3).repeat(2) icet_structure = Structure.from_atoms(atoms) def _test_lattice_site_find(tol, noise, index): for i in range(3): position = atoms.positions[index] position[i] += noise ls = icet_structure.find_lattice_site_by_position( position, tol) self.assertEqual(ls.index, index) position = atoms.positions[index] position[i] -= noise ls = icet_structure.find_lattice_site_by_position( position, tol) self.assertEqual(ls.index, index) # First with noise smaller thant tol tol = 1e-5 noise = 5e-6 for index in range(len(atoms)): _test_lattice_site_find(tol, noise, index) # Increase tolerance and force a fail tol = 1e-5 noise = 5e-5 for index in range(len(atoms)): with self.assertRaises(Exception) as context: _test_lattice_site_find(tol, noise, index) self.assertIn('Failed to find site by position', str(context.exception)) # Large noise but larger tol tol = 1e-3 noise = 5e-4 for index in range(len(atoms)): _test_lattice_site_find(tol, noise, index) # Large tol but larger noise tol = 1e-3 noise = 5e-3 for index in range(len(atoms)): with self.assertRaises(Exception) as context: _test_lattice_site_find(tol, noise, index) self.assertIn('Failed to find site by position', str(context.exception))
def __init__(self, *args, **kwargs): super(TestClusterCounts, self).__init__(*args, **kwargs) self.structure = bulk('Ni', 'hcp', a=2.0).repeat([2, 1, 1]) self.structure_prim = bulk('Ni', 'hcp', a=2.0) self.structure.set_chemical_symbols('NiFeNi2') self.icet_structure = Structure.from_atoms(self.structure) self.cutoffs = [2.2] self.symprec = 1e-5 self.position_tolerance = 1e-5 self.fractional_position_tolerance = 1e-6 self.orbit_list = OrbitList(self.structure_prim, self.cutoffs, self.symprec, self.position_tolerance, self.fractional_position_tolerance) self.orbit_list.sort(self.position_tolerance)
def _set_allowed_species(self, cluster_space: ClusterSpace, structure: Atoms): """ Set the allowed species for the selected sites in the Atoms object Parameters ---------- cluster_space Cluster space implicitly defining allowed species structure Specific supercell (consistent with cluster_space) whose allowed species are to be determined """ primitive_structure = Structure.from_atoms( cluster_space.primitive_structure) chemical_symbols = cluster_space.get_chemical_symbols() if len(chemical_symbols) == 1: # If the allowed species are the same for all sites no loop is # required allowed_species = { site: chemical_symbols[0] for site in self._sites.keys() } else: # Loop over the lattice sites to find the allowed species allowed_species = {} for site, indices in self._sites.items(): allowed_species[site] = None positions = structure.positions[np.array(indices)] lattice_sites = primitive_structure.find_lattice_sites_by_positions( positions=positions, fractional_position_tolerance=cluster_space. fractional_position_tolerance) for l, lattice_site in enumerate(lattice_sites): species = chemical_symbols[lattice_site.index] # check that the allowed species are equal for all sites if allowed_species[site] is not None and \ species != allowed_species[site]: raise Exception("The allowed species {} for the site" " with index {} differs from the" " result {} for the previous index" " ({})!".format( species, indices[l], allowed_species[site], indices[l - 1])) allowed_species[site] = species self._allowed_species = allowed_species
def test_get_local_cluster_vector(self): """Tests the get local clustervector method.""" cpp_calc = _ClusterExpansionCalculator( self.cs, Structure.from_atoms(self.structure), self.cs.fractional_position_tolerance) index = 4 cpp_calc.get_local_cluster_vector(self.structure.get_atomic_numbers(), index, []) self.structure[index].symbol = 'Ge' cpp_calc.get_local_cluster_vector(self.structure.get_atomic_numbers(), index, [])
def setUp(self): """Instantiates class before each test.""" structure = Structure.from_atoms(bulk('Al')) lattice_site_for_cluster = [ LatticeSite(0, [i, 0, 0]) for i in range(3) ] self.pair_cluster = Cluster( structure, [lattice_site_for_cluster[0], lattice_site_for_cluster[1]], True) self.triplet_cluster = Cluster(structure, lattice_site_for_cluster, True) self.orbit_pair = Orbit(self.pair_cluster) self.orbit_triplet = Orbit(self.triplet_cluster)
def __init__(self, *args, **kwargs): super(TestMatrixOfEquivalentPositions, self).__init__(*args, **kwargs) self.position_tolerance = 1e-6 self.symprec = 1e-6 self.fractional_position_tolerance = 1e-7 self.structure = bulk('Ni', 'hcp', a=3.0).repeat([2, 2, 1]) self.cutoff = 5.0 self.structure_prim = get_primitive_structure(self.structure) icet_structure_prim = Structure.from_atoms(self.structure_prim) neighbor_list = get_neighbor_lists( self.structure_prim, cutoffs=[self.cutoff], position_tolerance=self.position_tolerance)[0] self.frac_positions = get_fractional_positions_from_neighbor_list( icet_structure_prim, neighbor_list)
def get_supercell_orbit_list(self, structure: Atoms, fractional_position_tolerance: float): """ Returns an orbit list for a supercell structure. Parameters ---------- structure supercell atomic structure fractional_position_tolerance : float tolerance applied when comparing positions in fractional coordinates """ lolg = LocalOrbitListGenerator( self, structure=Structure.from_atoms(structure), fractional_position_tolerance=fractional_position_tolerance) supercell_orbit_list = lolg.generate_full_orbit_list() return supercell_orbit_list
def _generate_counts(self, structure: Atoms) -> None: """Counts the occurrence of different clusters and stores this information in a pandas dataframe. Parameters ---------- structure input atomic structure. """ self._cluster_counts_cpp.count_orbit_list( Structure.from_atoms(structure), self._full_orbit_list, True, True, self._max_orbit) # Getting the empty counts sometimes constitutes a large part of the total time. # Thus copy a previously constructed dictionary. # Since Cluster is not picklable, we need to do a slightly awkward manual copy. empty_counts = { cluster: copy.deepcopy(item) for cluster, item in self._empty_counts.items() } pandas_rows = [] # std::unordered_map<Cluster, std::map<std::vector<int>, int>> cluster_counts = self._cluster_counts_cpp.get_cluster_counts() for cluster_key, chemical_number_counts_dict in cluster_counts.items(): for chemical_symbols in empty_counts[cluster_key].keys(): count = chemical_number_counts_dict.get(chemical_symbols, 0) pandas_row = {} pandas_row['dc_tag'] = '{}_{}'.format( cluster_key.tag, '_'.join(chemical_symbols)) pandas_row['occupation'] = chemical_symbols pandas_row['cluster_count'] = count pandas_row['orbit_index'] = cluster_key.tag pandas_row['order'] = len(cluster_key) pandas_row['radius'] = cluster_key.radius pandas_rows.append(pandas_row) self.count_frame = pd.DataFrame(pandas_rows) self._cluster_counts_cpp.reset()
def test_find_lattice_site_by_position_hard(self): """ Tests finding lattice site by position, hard version tests against hcp, many atoms in the basis AND pbc = [True, True, False] ! 1. Create a bunch of lattice sites all with index 0 and integer unitcell offsets 2. convert these to x,y,z positions. Nothing strange so far 3. Find lattice site from the position and assert that it should be equivalent to the original lattice site. """ ase_atoms = self.ase_atoms.repeat([3, 5, 5]) # Set pbc false in Z-direction and add vacuum ase_atoms.pbc = [True, True, False] ase_atoms.center(30, axis=[2]) icet_structure = Structure.from_atoms(ase_atoms) noise_position = [] lattice_sites = [] unit_cell_range = 100 for j in range(500): offset = [ random.randint(-unit_cell_range, unit_cell_range) for i in range(3) ] offset[2] = 0 index = random.randint(0, len(ase_atoms) - 1) noise_position.append( [self.noise * random.uniform(-1, 1) for i in range(3)]) lattice_sites.append(LatticeSite(index, offset)) positions = [] for i, site in enumerate(lattice_sites): pos = icet_structure.get_position(site) pos += np.array(noise_position[i]) positions.append(pos) for site, pos in zip(lattice_sites, positions): found_site = icet_structure.find_lattice_site_by_position( pos, self.fractional_position_tolerance) self.assertEqual(site, found_site)
def test_mbnl_cubic_non_pbc(self): """ Tests that corners sites in a large cubic cell have only three neighbors in many-body neighbor list. """ structure = bulk('Al', 'sc', a=4.0).repeat(4) structure.set_pbc(False) neighbor_lists = get_neighbor_lists(Structure.from_atoms(structure), self.cutoffs, self.position_tolerance) mbnl = ManyBodyNeighborList() # atomic indices located at the corner of structure corner_sites = [0, 3, 12, 15, 48, 51, 60, 63] for index in corner_sites: lattice_neighbor = mbnl.build(neighbor_lists, index, True) # check pairs self.assertEqual(len(lattice_neighbor[1][1]), 3) # not neighbors besides above pairs with self.assertRaises(IndexError): lattice_neighbor[2]
def test_mbnl_non_pbc(self): """Tests many-body neighbor list for non-pbc structure.""" structure = self.structure.copy() structure.set_pbc([False]) neighbor_lists = get_neighbor_lists(Structure.from_atoms(structure), self.cutoffs, self.position_tolerance) mbnl = ManyBodyNeighborList() target = [([LatticeSite(0, [0, 0, 0])], []), ([LatticeSite(0, [0, 0, 0])], [ LatticeSite(1, [0, 0, 0]), LatticeSite(2, [0, 0, 0]), LatticeSite(4, [0, 0, 0]), LatticeSite(5, [0, 0, 0]), LatticeSite(6, [0, 0, 0]) ]), ([LatticeSite(0, [0, 0, 0]), LatticeSite(1, [0, 0, 0])], [ LatticeSite(2, [0, 0, 0]), LatticeSite(4, [0, 0, 0]), LatticeSite(5, [0, 0, 0]), LatticeSite(6, [0, 0, 0]) ]), ([LatticeSite(0, [0, 0, 0]), LatticeSite(2, [0, 0, 0])], [LatticeSite(6, [0, 0, 0])]), ([LatticeSite(0, [0, 0, 0]), LatticeSite(4, [0, 0, 0])], [LatticeSite(5, [0, 0, 0]), LatticeSite(6, [0, 0, 0])]), ([LatticeSite(0, [0, 0, 0]), LatticeSite(5, [0, 0, 0])], [LatticeSite(6, [0, 0, 0])])] neighbors_non_pbc = mbnl.build(neighbor_lists, 0, False) for k, latt_neighbors in enumerate(neighbors_non_pbc): self.assertEqual(target[k], latt_neighbors)
def _from_python(ase_structure: Atoms, lattice_sites: List[LatticeSite], cluster_index: int = -1): """ Constructs a cluster from an ASE Atoms object and Python lattice sites. Parameters ---------- ase_structure structure as ASE Atoms object lattice_sites lattice site objects cluster_index index used to identify cluster """ structure = Structure.from_atoms(ase_structure) lattice_sites_cpp = [ LatticeSite(ls.index, ls.unitcell_offset) for ls in lattice_sites ] return Cluster(structure, lattice_sites_cpp, cluster_index)
def test_find_lattice_site_by_position_medium(self): """ Tests finding lattice site by position, medium version tests against hcp and user more than one atom in the basis 1. Create a bunch of lattice sites all with index 0 and integer unitcell offsets 2. convert these to x,y,z positions. Nothing strange so far 3. Find lattice site from the position and assert that it should be equivalent to the original lattice site. """ ase_atoms = self.ase_atoms.repeat([3, 2, 5]) icet_structure = Structure.from_atoms(ase_atoms) lattice_sites = [] unit_cell_range = 1000 noise_position = [] for j in range(5000): offset = [ random.randint(-unit_cell_range, unit_cell_range) for i in range(3) ] index = random.randint(0, len(ase_atoms) - 1) noise_position.append( [self.noise * random.uniform(-1, 1) for i in range(3)]) lattice_sites.append(LatticeSite(index, offset)) positions = [] for i, site in enumerate(lattice_sites): pos = icet_structure.get_position(site) pos = pos + np.array(noise_position[i]) positions.append(pos) for site, pos in zip(lattice_sites, positions): found_site = icet_structure.find_lattice_site_by_position( pos, self.fractional_position_tolerance) self.assertEqual(site, found_site)
import numpy as np from icet.core.structure import Structure from ase.db import connect from ase.neighborlist import NeighborList as ASENeighborList """ Testing the calculation of distances with offsets TODO: Delete this after edit unittest/test_structure """ """ Fetch structures from database """ db = connect('structures_for_testing.db') for row in db.select(): structure = row.toatoms() nl = ASENeighborList(len(structure) * [2.6], self_interaction=False) nl.update(structure) for index in range(len(structure)): indices, offsets = nl.get_neighbors(index) for i, offset in zip(indices, offsets): dvec = structure.positions[index] - structure.positions[i] dvec -= np.dot(offset, structure.cell) dist_ase = np.linalg.norm(dvec) dist_struct = Structure.from_atoms(structure).get_distance( index, i, [0, 0, 0], offset) msg = 'Testing distance calculator failed' msg += ' for structure {}'.format(row.tag) assert dist_ase - dist_struct < 1e-8, msg
class TestStructure(unittest.TestCase): """Container for test of the module functionality.""" def __init__(self, *args, **kwargs): super(TestStructure, self).__init__(*args, **kwargs) self.ase_atoms = bulk('Ag', 'hcp', a=2.0) self.noise = 1e-6 self.fractional_position_tolerance = 2e-6 self.positions = [[0., 0., 0.], [0., 1.15470054, 1.63299316]] self.chemical_symbols = ['Ag', 'Ag'] self.cell = [[2., 0., 0.], [-1., 1.73205081, 0.], [0., 0., 3.26598632]] def shortDescription(self): """Silences unittest from printing the docstrings in test cases.""" return None def setUp(self): """Setup before each test.""" self.icet_structure = Structure(positions=self.positions, chemical_symbols=self.chemical_symbols, cell=self.cell, pbc=[True, True, True]) random.seed(113) def test_positions(self): """Tests positions of atoms in structure.""" for i, vec in enumerate(self.icet_structure.positions): self.assertListEqual(vec.tolist(), self.positions[i]) new_positions = [[0., 0., 0.001], [0., 1.15470054, 1.63299316]] self.icet_structure.positions = new_positions retval = self.icet_structure.positions for i, vec in enumerate(retval): self.assertListEqual(vec.tolist(), new_positions[i]) def test_chemical_symbols(self): """Tests chemical symbols of atoms in structure.""" self.assertListEqual(self.icet_structure.chemical_symbols, self.chemical_symbols) def test_atomic_numbers(self): """Tests atomic numbers.""" self.assertListEqual(self.icet_structure.atomic_numbers, [47, 47]) def test_cell(self): """Tests cell.""" for i, vec in enumerate(self.icet_structure.cell): self.assertListEqual(vec.tolist(), self.cell[i]) new_cell = [[2., 0., 0.], [-1., 2., 0.], [0., 0., 4.]] self.icet_structure.cell = new_cell retval = self.icet_structure.cell for i, vec in enumerate(retval): self.assertListEqual(vec.tolist(), new_cell[i]) def test_pbc(self): """Tests periodic boundary conditions.""" self.assertListEqual(self.icet_structure.pbc, [True, True, True]) self.icet_structure.pbc = [True, True, False] retval = self.icet_structure.pbc self.assertListEqual(retval, [True, True, False]) def test_unique_sites(self): """Tests unique sites.""" self.assertListEqual(self.icet_structure.unique_sites, [0, 0]) def test_set_and_get_chemical_symbols(self): """Tests set and get chemical symbols.""" new_chemical_symbols = ['Au', 'Au'] self.icet_structure.set_chemical_symbols(new_chemical_symbols) retval = self.icet_structure.get_chemical_symbols() self.assertListEqual(retval, new_chemical_symbols) def test_set_and_get_atomic_numbers(self): """Tests set and get atomic numbers.""" self.icet_structure.set_atomic_numbers([48, 47]) retval = self.icet_structure.get_atomic_numbers() self.assertListEqual(retval, [48, 47]) def test_set_and_get_unique_sites(self): """Tests set and get unique sites.""" self.icet_structure.set_unique_sites([0, 1]) retval = self.icet_structure.get_unique_sites() self.assertListEqual(retval, [0, 1]) def test_get_position(self): """Tests get_position functionality.""" retval = self.icet_structure.get_position(LatticeSite(1, [0, 0, 0])) self.assertListEqual(retval.tolist(), self.positions[1]) def test_get_distance(self): """Tests get_distance functionality.""" retval = self.icet_structure.get_distance(0, 1, [0., 0., 0.], [0., 0., 0.]) target = self.icet_structure.get_distance(0, 1) self.assertAlmostEqual(retval, target) def test_find_lattice_site_by_position_with_tolerance(self): """Tests the find lattice site by position method by varying the tolerance """ atoms = bulk('Al', crystalstructure='hcp', a=3).repeat(2) icet_structure = Structure.from_atoms(atoms) def _test_lattice_site_find(tol, noise, index): for i in range(3): position = atoms.positions[index] position[i] += noise ls = icet_structure.find_lattice_site_by_position( position, tol) self.assertEqual(ls.index, index) position = atoms.positions[index] position[i] -= noise ls = icet_structure.find_lattice_site_by_position( position, tol) self.assertEqual(ls.index, index) # First with noise smaller thant tol tol = 1e-5 noise = 5e-6 for index in range(len(atoms)): _test_lattice_site_find(tol, noise, index) # Increase tolerance and force a fail tol = 1e-5 noise = 5e-5 for index in range(len(atoms)): with self.assertRaises(Exception) as context: _test_lattice_site_find(tol, noise, index) self.assertIn('Failed to find site by position', str(context.exception)) # Large noise but larger tol tol = 1e-3 noise = 5e-4 for index in range(len(atoms)): _test_lattice_site_find(tol, noise, index) # Large tol but larger noise tol = 1e-3 noise = 5e-3 for index in range(len(atoms)): with self.assertRaises(Exception) as context: _test_lattice_site_find(tol, noise, index) self.assertIn('Failed to find site by position', str(context.exception)) def test_find_lattice_site_by_position_simple(self): """ Tests finding lattice site by position, simple version using only one atom cell. 1. Create a bunch of lattice sites all with index 0 and integer unitcell offsets 2. convert these to x,y,z positions. Nothing strange so far 3. Find lattice site from the position and assert that it should be equivalent to the original lattice site. """ lattice_sites = [] noise_position = [] unit_cell_range = 1000 for j in range(5000): offset = [ random.randint(-unit_cell_range, unit_cell_range) for i in range(3) ] noise_position.append( [self.noise * random.uniform(-1, 1) for i in range(3)]) lattice_sites.append(LatticeSite(0, offset)) positions = [] for i, site in enumerate(lattice_sites): # Get position with a little noise pos = self.icet_structure.get_position(site) pos = pos + np.array(noise_position[i]) positions.append(pos) for site, pos in zip(lattice_sites, positions): found_site = self.icet_structure.find_lattice_site_by_position( pos, self.fractional_position_tolerance) self.assertEqual(site, found_site) def test_find_lattice_site_by_position_medium(self): """ Tests finding lattice site by position, medium version tests against hcp and user more than one atom in the basis 1. Create a bunch of lattice sites all with index 0 and integer unitcell offsets 2. convert these to x,y,z positions. Nothing strange so far 3. Find lattice site from the position and assert that it should be equivalent to the original lattice site. """ ase_atoms = self.ase_atoms.repeat([3, 2, 5]) icet_structure = Structure.from_atoms(ase_atoms) lattice_sites = [] unit_cell_range = 1000 noise_position = [] for j in range(5000): offset = [ random.randint(-unit_cell_range, unit_cell_range) for i in range(3) ] index = random.randint(0, len(ase_atoms) - 1) noise_position.append( [self.noise * random.uniform(-1, 1) for i in range(3)]) lattice_sites.append(LatticeSite(index, offset)) positions = [] for i, site in enumerate(lattice_sites): pos = icet_structure.get_position(site) pos = pos + np.array(noise_position[i]) positions.append(pos) for site, pos in zip(lattice_sites, positions): found_site = icet_structure.find_lattice_site_by_position( pos, self.fractional_position_tolerance) self.assertEqual(site, found_site) def test_find_lattice_site_by_position_hard(self): """ Tests finding lattice site by position, hard version tests against hcp, many atoms in the basis AND pbc = [True, True, False] ! 1. Create a bunch of lattice sites all with index 0 and integer unitcell offsets 2. convert these to x,y,z positions. Nothing strange so far 3. Find lattice site from the position and assert that it should be equivalent to the original lattice site. """ ase_atoms = self.ase_atoms.repeat([3, 5, 5]) # Set pbc false in Z-direction and add vacuum ase_atoms.pbc = [True, True, False] ase_atoms.center(30, axis=[2]) icet_structure = Structure.from_atoms(ase_atoms) noise_position = [] lattice_sites = [] unit_cell_range = 100 for j in range(500): offset = [ random.randint(-unit_cell_range, unit_cell_range) for i in range(3) ] offset[2] = 0 index = random.randint(0, len(ase_atoms) - 1) noise_position.append( [self.noise * random.uniform(-1, 1) for i in range(3)]) lattice_sites.append(LatticeSite(index, offset)) positions = [] for i, site in enumerate(lattice_sites): pos = icet_structure.get_position(site) pos += np.array(noise_position[i]) positions.append(pos) for site, pos in zip(lattice_sites, positions): found_site = icet_structure.find_lattice_site_by_position( pos, self.fractional_position_tolerance) self.assertEqual(site, found_site) def test_structure_from_atoms(self): """Tests ASE Atoms-to-icet Structure conversion.""" icet_structure = Structure.from_atoms(self.ase_atoms) for icet_pos, ase_pos in zip(icet_structure.positions, self.ase_atoms.positions): self.assertListEqual(icet_pos.tolist(), ase_pos.tolist()) chem_symbols = icet_structure.get_chemical_symbols() self.assertListEqual(chem_symbols, ['Ag', 'Ag']) def test_structure_to_atoms(self): """Tests icet Structure-to-ASE Atoms conversion.""" ase_structure = Structure.to_atoms(self.icet_structure) for ase_pos, icet_pos in zip(ase_structure.positions, self.icet_structure.positions): self.assertListEqual(ase_pos.tolist(), icet_pos.tolist()) chem_symbols = ase_structure.get_chemical_symbols() self.assertListEqual(chem_symbols, ['Ag', 'Ag']) def test_repr_function(self): """Tests string representation.""" retval = self.icet_structure.__repr__() target = """ Cell: [[ 2. 0. 0. ] [-1. 1.73205081 0. ] [ 0. 0. 3.26598632]] Element and positions: Ag [0.0 0.0 0.0] Ag [0.0 1.15470054 1.63299316] """ self.assertEqual(strip_surrounding_spaces(retval), strip_surrounding_spaces(target))
def setUp(self): """Instantiates class before each test.""" self.mbnl = ManyBodyNeighborList() structure = Structure.from_atoms(self.structure) self.neighbor_lists = get_neighbor_lists(structure, self.cutoffs, self.position_tolerance)