def test_gratoms(): edges = [(0, 1), (0, 2)] atoms = Gratoms(edges=edges) for n in atoms.edges: assert (n in edges) mol = molecule('H2O') atoms = to_gratoms(mol) atoms.graph.add_edges_from([(0, 1), (0, 2)]) sym_test = atoms.get_neighbor_symbols(0) assert (sym_test.tolist() == ['H', 'H']) test_tags = atoms.get_chemical_tags(rank=1) assert (test_tags == '2,0,0,0,0,0,0,1') test_comp, test_bonds = atoms.get_chemical_tags() assert (test_comp == '2,0,0,0,0,0,0,1') assert (test_bonds == '4,0,0,0,0,0,0,3') atoms.set_constraint(FixAtoms(indices=[0])) del atoms[2] assert (len(atoms) == 2) nx.set_node_attributes(atoms.graph, name='valence', values={0: 1, 1: 0}) test_nodes = atoms.get_unsaturated_nodes(screen=1) assert (test_nodes == [0])
def to_gratoms(atoms): """Convert and atom object to a gratoms object.""" gratoms = Gratoms(numbers=atoms.numbers, positions=atoms.positions, pbc=atoms.pbc, cell=atoms.cell) if atoms.constraints: gratoms.set_constraint(atoms.constraints) return gratoms
def get_slab(self, size=(1, 1), root=None, iterm=None, primitive=False): """Generate a slab object with a certain number of layers. Parameters ---------- size : tuple (2,) Repeat the x and y lattice vectors by the indicated dimensions root : int Produce a slab with a primitive a1 basis vector multiplied by the square root of a provided value. Uses primitive unit cell. iterm : int A termination index in reference to the list of possible terminations. primitive : bool Whether to reduce the unit cell to its primitive form. Returns ------- slab : atoms object The modified basis slab produced based on the layer specifications given. """ slab = self._basis.copy() if iterm: if self.unique_terminations is None: terminations = self.get_unique_terminations() else: terminations = self.unique_terminations zshift = terminations[iterm] slab.translate([0, 0, -zshift]) slab.wrap(pbc=True) # Get the minimum number of layers needed zlayers = utils.get_unique_coordinates(slab, direct=False, tol=self.tol) if self.min_width: width = slab.cell[2][2] z_repetitions = np.ceil(width / len(zlayers) * self.min_width) else: z_repetitions = np.ceil(self.layers / len(zlayers)) slab *= (1, 1, int(z_repetitions)) if primitive or root: if self.vacuum: slab.center(vacuum=self.vacuum, axis=2) else: raise ValueError('Primitive slab generation requires vacuum') nslab = utils.get_primitive_cell(slab) if nslab is not None: slab = nslab # spglib occasionally returns a split slab zpos = slab.get_scaled_positions() if zpos[:, 2].max() > 0.9 or zpos[:, 2].min() < 0.1: zpos[:, 2] -= 0.5 zpos[:, 2] %= 1 slab.set_scaled_positions(zpos) slab.center(vacuum=self.vacuum, axis=2) # For hcp(1, 1, 0), primitive alters z-axis d = norm(slab.cell, axis=0) maxd = np.argwhere(d == d.max())[0][0] if maxd != 2: slab.rotate(slab.cell[maxd], 'z', rotate_cell=True) slab.cell[[maxd, 2]] = slab.cell[[2, maxd]] slab.cell[maxd] = -slab.cell[maxd] slab.wrap(pbc=True) slab.rotate(slab.cell[0], 'x', rotate_cell=True) # Orthogonalize the z-coordinate # Warning: bulk symmetry is lost at this point a, b, c = slab.cell nab = np.cross(a, b) c = (nab * np.dot(c, nab) / norm(nab)**2) slab.cell[2] = c # Align the longest remaining basis vector with x vdist = norm(slab.cell[:2], axis=1) if vdist[1] > vdist[0]: slab.rotate(slab.cell[1], 'x', rotate_cell=True) slab.cell[0] *= -1 slab.cell[[0, 1]] = slab.cell[[1, 0]] # Enforce that the angle between basis vectors is acute. if slab.cell[1][0] < 0: slab.rotate(slab.cell[2], '-z') slab.cell *= [[1, 0, 0], [-1, 1, 0], [0, 0, 1]] if root: roots, vectors = root_surface_analysis(slab, return_vectors=True) if root not in roots: raise ValueError( 'Requested root structure unavailable for this system.' 'Try: {}'.format(roots)) vect = vectors[np.where(root == roots)][0] slab = self.root_surface(slab, root, vect) # Get the direct z-coordinate of the requested layer zlayers = utils.get_unique_coordinates(slab, direct=False, tag=True, tol=self.tol) if not self.fix_stoichiometry: reverse_sort = np.sort(zlayers)[::-1] if self.min_width: n = np.where(zlayers < self.min_width, 1, 0).sum() ncut = reverse_sort[n] else: ncut = reverse_sort[:self.layers][-1] zpos = slab.positions[:, 2] index = np.arange(len(slab)) del slab[index[zpos - ncut < -self.tol]] slab.cell[2][2] -= ncut slab.translate([0, 0, -ncut]) slab *= (size[0], size[1], 1) tags = slab.get_tags() m = np.where(tags == 1)[0][0] translation = slab[m].position.copy() translation[2] = 0 slab.translate(-translation) slab.wrap() ind = np.lexsort( (slab.positions[:, 0], slab.positions[:, 1], slab.positions[:, 2])) slab = Gratoms(positions=slab.positions[ind], numbers=slab.numbers[ind], cell=slab.cell, pbc=[1, 1, 0], tags=tags[ind]) fix = tags.max() - self.fixed constraints = FixAtoms(indices=[a.index for a in slab if a.tag > fix]) slab.set_constraint(constraints) if self.vacuum: slab.center(vacuum=self.vacuum, axis=2) self.slab = slab return slab