def primitive_from_conventional_cell(atoms, spacegroup=1, setting=1): """Returns primitive cell given an Atoms object for a conventional cell and it's spacegroup.""" from ase.lattice.spacegroup import Spacegroup from ase.utils.geometry import cut sg = Spacegroup(spacegroup, setting) prim_cell = sg.scaled_primitive_cell # Check if we need to transpose return cut(atoms, a=prim_cell[0], b=prim_cell[1], c=prim_cell[2])
def crystal(symbols=None, basis=None, spacegroup=1, setting=1, cell=None, cellpar=None, ab_normal=(0,0,1), a_direction=None, size=(1,1,1), ondublicates='warn', symprec=0.001, pbc=True, primitive_cell=False, **kwargs): """Create an Atoms instance for a conventional unit cell of a space group. Parameters: symbols : str | sequence of str | sequence of Atom | Atoms Element symbols of the unique sites. Can either be a string formula or a sequence of element symbols. E.g. ('Na', 'Cl') and 'NaCl' are equivalent. Can also be given as a sequence of Atom objects or an Atoms object. basis : list of scaled coordinates Positions of the unique sites corresponding to symbols given either as scaled positions or through an atoms instance. Not needed if *symbols* is a sequence of Atom objects or an Atoms object. spacegroup : int | string | Spacegroup instance Space group given either as its number in International Tables or as its Hermann-Mauguin symbol. setting : 1 | 2 Space group setting. cell : 3x3 matrix Unit cell vectors. cellpar : [a, b, c, alpha, beta, gamma] Cell parameters with angles in degree. Is not used when `cell` is given. ab_normal : vector Is used to define the orientation of the unit cell relative to the Cartesian system when `cell` is not given. It is the normal vector of the plane spanned by a and b. a_direction : vector Defines the orientation of the unit cell a vector. a will be parallel to the projection of `a_direction` onto the a-b plane. size : 3 positive integers How many times the conventional unit cell should be repeated in each direction. ondublicates : 'keep' | 'replace' | 'warn' | 'error' Action if `basis` contain symmetry-equivalent positions: 'keep' - ignore additional symmetry-equivalent positions 'replace' - replace 'warn' - like 'keep', but issue an UserWarning 'error' - raises a SpacegroupValueError symprec : float Minimum "distance" betweed two sites in scaled coordinates before they are counted as the same site. pbc : one or three bools Periodic boundary conditions flags. Examples: True, False, 0, 1, (1, 1, 0), (True, False, False). Default is True. primitive_cell : bool Wheter to return the primitive instead of the conventional unit cell. Keyword arguments: All additional keyword arguments are passed on to the Atoms constructor. Currently, probably the most useful additional keyword arguments are `info`, `constraint` and `calculator`. Examples: Two diamond unit cells (space group number 227) >>> diamond = crystal('C', [(0,0,0)], spacegroup=227, ... cellpar=[3.57, 3.57, 3.57, 90, 90, 90], size=(2,1,1)) >>> ase.view(diamond) # doctest: +SKIP A CoSb3 skutterudite unit cell containing 32 atoms >>> skutterudite = crystal(('Co', 'Sb'), ... basis=[(0.25,0.25,0.25), (0.0, 0.335, 0.158)], ... spacegroup=204, cellpar=[9.04, 9.04, 9.04, 90, 90, 90]) >>> len(skutterudite) 32 """ sg = Spacegroup(spacegroup, setting) if (not isinstance(symbols, str) and hasattr(symbols, '__getitem__') and len(symbols) > 0 and isinstance(symbols[0], ase.Atom)): symbols = ase.Atoms(symbols) if isinstance(symbols, ase.Atoms): basis = symbols symbols = basis.get_chemical_symbols() if isinstance(basis, ase.Atoms): basis_coords = basis.get_scaled_positions() if cell is None and cellpar is None: cell = basis.cell if symbols is None: symbols = basis.get_chemical_symbols() else: basis_coords = np.array(basis, dtype=float, copy=False, ndmin=2) sites, kinds = sg.equivalent_sites(basis_coords, ondublicates=ondublicates, symprec=symprec) symbols = parse_symbols(symbols) symbols = [symbols[i] for i in kinds] if cell is None: cell = cellpar_to_cell(cellpar, ab_normal, a_direction) info = dict(spacegroup=sg) if primitive_cell: info['unit_cell'] = 'primitive' else: info['unit_cell'] = 'conventional' if 'info' in kwargs: info.update(kwargs['info']) kwargs['info'] = info atoms = ase.Atoms(symbols, scaled_positions=sites, cell=cell, pbc=pbc, **kwargs) if isinstance(basis, ase.Atoms): for name in basis.arrays: if not atoms.has(name): array = basis.get_array(name) atoms.new_array(name, [array[i] for i in kinds], dtype=array.dtype, shape=array.shape[1:]) if primitive_cell: from ase.utils.geometry import cut prim_cell = sg.scaled_primitive_cell atoms = cut(atoms, a=prim_cell[0], b=prim_cell[1], c=prim_cell[2]) if size != (1, 1, 1): atoms = atoms.repeat(size) return atoms
"""Test the ase.utils.geometry module""" import numpy as np from ase.lattice.spacegroup import crystal from ase.utils.geometry import get_layers, cut, stack from ase.atoms import Atoms np.set_printoptions(suppress=True) al = crystal("Al", [(0, 0, 0)], spacegroup=225, cellpar=4.05) # Cut out slab of 5 Al(001) layers al001 = cut(al, nlayers=5) correct_pos = np.array( [ [0.0, 0.0, 0.0], [0.0, 0.5, 0.2], [0.5, 0.0, 0.2], [0.5, 0.5, 0.0], [0.0, 0.0, 0.4], [0.0, 0.5, 0.6], [0.5, 0.0, 0.6], [0.5, 0.5, 0.4], [0.0, 0.0, 0.8], [0.5, 0.5, 0.8], ] ) assert np.allclose(correct_pos, al001.get_scaled_positions())
def cut111(crystal,nlayers=6): """Cleave the 111 surface""" crystal111 = cut(crystal,[1,0,1],[1,1,0],nlayers=nlayers) return crystal111
def cut001(crystal,nlayers=6): """Cleave the 001 surface""" crystal001 = cut(crystal,[0,1,0],[0,0,1],nlayers=nlayers) return crystal001
def cut010(crystal,nlayers=6): """Cleave the 010 surface""" crystal010 = cut(crystal,[1,0,0],[0,0,1],nlayers=nlayers) return crystal010
def cut100(crystal,nlayers=6): """Cleave the 100 surface""" crystal100 = cut(crystal,[0,0,1],[0,1,0],nlayers=nlayers) return crystal100
import numpy as np import ase.utils.geometry as geometry import ase.io as io # Create a new atoms instance with Co at origo including all atoms on the # surface of the unit cell cosb3 = geometry.cut(skutterudite, origo=(0.25, 0.25, 0.25), extend=1.01) # Define the atomic bonds to show bondatoms = [] symbols = cosb3.get_chemical_symbols() for i in xrange(len(cosb3)): for j in xrange(i): if (symbols[i] == symbols[j] == 'Co' and cosb3.get_distance(i, j) < 4.53): bondatoms.append((i, j)) elif (symbols[i] == symbols[j] == 'Sb' and cosb3.get_distance(i, j) < 2.99): bondatoms.append((i, j)) # Create nice-looking image using povray io.write('spacegroup-cosb3.pov', cosb3, transparent=False, display=False, run_povray=True, camera_type='perspective', canvas_width=320, radii=0.4, rotation='90y', bondlinewidth=0.07, bondatoms=bondatoms,