Example #1
0
    def __init__(self, row, meta={}, subscript=None, prefix=''):
        self.row = row

        self.cell = [['{:.3f}'.format(a) for a in axis] for axis in row.cell]
        par = ['{:.3f}'.format(x) for x in cell_to_cellpar(row.cell)]
        self.lengths = par[:3]
        self.angles = par[3:]

        self.stress = row.get('stress')
        if self.stress is not None:
            self.stress = ', '.join('{0:.3f}'.format(s) for s in self.stress)

        self.formula = formula_metal(row.numbers)
        if subscript:
            self.formula = subscript.sub(r'<sub>\1</sub>', self.formula)

        kd = meta.get('key_descriptions', {})
        create_layout = meta.get('layout') or default_layout
        self.layout = create_layout(row, kd, prefix)

        self.dipole = row.get('dipole')
        if self.dipole is not None:
            self.dipole = ', '.join('{0:.3f}'.format(d) for d in self.dipole)

        self.data = row.get('data')
        if self.data:
            self.data = ', '.join(self.data.keys())

        self.constraints = row.get('constraints')
        if self.constraints:
            self.constraints = ', '.join(c.__class__.__name__
                                         for c in self.constraints)
Example #2
0
def generate_cif(structure, comment=None, symops=['+x,+y,+z']):
    parameters = cell_to_cellpar(structure.cell)
    cif_data = "# " + comment + "\n\n" if comment else ''
    cif_data += 'data_tilde_project\n'
    cif_data += '_cell_length_a    ' + "%2.6f" % parameters[0] + "\n"
    cif_data += '_cell_length_b    ' + "%2.6f" % parameters[1] + "\n"
    cif_data += '_cell_length_c    ' + "%2.6f" % parameters[2] + "\n"
    cif_data += '_cell_angle_alpha ' + "%2.6f" % parameters[3] + "\n"
    cif_data += '_cell_angle_beta  ' + "%2.6f" % parameters[4] + "\n"
    cif_data += '_cell_angle_gamma ' + "%2.6f" % parameters[5] + "\n"
    cif_data += "_symmetry_space_group_name_H-M 'P1'" + "\n"
    cif_data += 'loop_' + "\n"
    cif_data += '_symmetry_equiv_pos_as_xyz' + "\n"
    for i in symops:
        cif_data += i + "\n"
    cif_data += 'loop_' + "\n"
    cif_data += '_atom_site_type_symbol' + "\n"
    cif_data += '_atom_site_fract_x' + "\n"
    cif_data += '_atom_site_fract_y' + "\n"
    cif_data += '_atom_site_fract_z' + "\n"
    pos = structure.get_scaled_positions()
    for n, i in enumerate(structure):
        cif_data += "%s   % 1.8f   % 1.8f   % 1.8f\n" % (i.symbol, pos[n][0],
                                                         pos[n][1], pos[n][2])
    return cif_data
def _ngl_write_structure(elements, positions, cell):
    """
    Turns structure information into a NGLView-readable protein-database-formatted string.

    Args:
        elements (numpy.ndarray/list): Element symbol for each atom.
        positions (numpy.ndarray/list): Vector of Cartesian atom positions.
        cell (numpy.ndarray/list): Simulation cell Bravais matrix.

    Returns:
        (str): The PDB-formatted representation of the structure.
    """
    from ase.geometry import cell_to_cellpar, cellpar_to_cell
    if cell is None or any(np.max(cell, axis=0) < 1e-2):
        # Define a dummy cell if it doesn't exist (eg. for clusters)
        max_pos = np.max(positions, axis=0)
        max_pos[np.abs(max_pos) < 1e-2] = 10
        cell = np.eye(3) * max_pos
    cellpar = cell_to_cellpar(cell)
    exportedcell = cellpar_to_cell(cellpar)
    rotation = np.linalg.solve(cell, exportedcell)

    pdb_str = _ngl_write_cell(*cellpar)
    pdb_str += "MODEL     1\n"

    if rotation is not None:
        positions = np.array(positions).dot(rotation)

    for i, p in enumerate(positions):
        pdb_str += _ngl_write_atom(i, elements[i], *p)

    pdb_str += "ENDMDL \n"
    return pdb_str
Example #4
0
    def get_cell_parameters(self, atoms):
        cell = atoms.cell
        cell_parameters = {}

        parameters = cell_to_cellpar(cell)

        for i, param in enumerate(['a', 'b', 'c', 'alpha', 'beta', 'gamma']):

            cell_parameters.update({param: parameters[i]})

        if np.all(self.free_atoms == 0):
            return cell_parameters

        dir_map = ['x', 'y', 'z']

        relative_positions = np.dot(np.linalg.inv(cell.T), atoms.positions.T).T

        unique_atoms_w, indices = np.unique(self.atoms_wyckoffs,
                                            return_index=True)

        for ai in indices:
            if not self.free_atoms[ai]:
                continue
            aw = self.atoms_wyckoffs[ai]
            position = relative_positions[ai]
            w_position = self.get_parameter_from_position(position, aw[0])
            for i, w in enumerate(w_position):
                if np.isclose(w, 0):
                    continue
                w_p_name = dir_map[i] + aw
                cell_parameters.update({w_p_name: w})

        return cell_parameters
Example #5
0
def write_eon(fileobj, images):
    """Writes structure to EON reactant.con file
    Multiple snapshots are allowed."""
    if isinstance(images, Atoms):
        atoms = images
    elif len(images) == 1:
        atoms = images[0]
    else:
        raise ValueError('Can only write one configuration to EON '
                         'reactant.con file')

    out = []
    out.append(atoms.info.get('comment', 'Generated by ASE'))
    out.append('0.0000 TIME')  # ??

    a, b, c, alpha, beta, gamma = cell_to_cellpar(atoms.cell)
    out.append('%-10.6f  %-10.6f  %-10.6f' % (a, b, c))
    out.append('%-10.6f  %-10.6f  %-10.6f' % (gamma, beta, alpha))

    out.append('0 0')  # ??
    out.append('0 0 0')  # ??

    symbols = atoms.get_chemical_symbols()
    massdict = dict(list(zip(symbols, atoms.get_masses())))
    atomtypes = sorted(massdict.keys())
    atommasses = [massdict[at] for at in atomtypes]
    natoms = [symbols.count(at) for at in atomtypes]
    ntypes = len(atomtypes)

    out.append(str(ntypes))
    out.append(' '.join([str(n) for n in natoms]))
    out.append(' '.join([str(n) for n in atommasses]))

    atom_id = 0
    for n in range(ntypes):
        fixed = np.array([False] * len(atoms))
        out.append(atomtypes[n])
        out.append('Coordinates of Component %d' % (n + 1))
        indices = [i for i, at in enumerate(symbols) if at == atomtypes[n]]
        a = atoms[indices]
        coords = a.positions
        for c in a.constraints:
            if not isinstance(c, FixAtoms):
                warn(
                    'Only FixAtoms constraints are supported by con files. '
                    'Dropping %r', c)
                continue
            if c.index.dtype.kind == 'b':
                fixed = np.array(c.index, dtype=int)
            else:
                fixed = np.zeros((natoms[n], ), dtype=int)
                for i in c.index:
                    fixed[i] = 1
        for xyz, fix in zip(coords, fixed):
            out.append('%22.17f %22.17f %22.17f %d %4d' % (tuple(xyz) +
                                                           (fix, atom_id)))
            atom_id += 1
    fileobj.write('\n'.join(out))
    fileobj.write('\n')
Example #6
0
def row2dct(
        row,
        key_descriptions: Dict[str, Tuple[str, str,
                                          str]] = {}) -> Dict[str, Any]:
    """Convert row to dict of things for printing or a web-page."""

    from ase.db.core import float_to_time_string, now

    dct = {}

    atoms = Atoms(cell=row.cell, pbc=row.pbc)
    dct['size'] = kptdensity2monkhorstpack(atoms, kptdensity=1.8, even=False)

    dct['cell'] = [['{:.3f}'.format(a) for a in axis] for axis in row.cell]
    par = ['{:.3f}'.format(x) for x in cell_to_cellpar(row.cell)]
    dct['lengths'] = par[:3]
    dct['angles'] = par[3:]

    stress = row.get('stress')
    if stress is not None:
        dct['stress'] = ', '.join('{0:.3f}'.format(s) for s in stress)

    dct['formula'] = Formula(row.formula).format('abc')

    dipole = row.get('dipole')
    if dipole is not None:
        dct['dipole'] = ', '.join('{0:.3f}'.format(d) for d in dipole)

    data = row.get('data')
    if data:
        dct['data'] = ', '.join(data.keys())

    constraints = row.get('constraints')
    if constraints:
        dct['constraints'] = ', '.join(c.__class__.__name__
                                       for c in constraints)

    keys = ({'id', 'energy', 'fmax', 'smax', 'mass', 'age'}
            | set(key_descriptions) | set(row.key_value_pairs))
    dct['table'] = []
    for key in keys:
        if key == 'age':
            age = float_to_time_string(now() - row.ctime, True)
            dct['table'].append(('ctime', 'Age', age))
            continue
        value = row.get(key)
        if value is not None:
            if isinstance(value, float):
                value = '{:.3f}'.format(value)
            elif not isinstance(value, str):
                value = str(value)
            desc, unit = key_descriptions.get(key, ['', '', ''])[1:]
            if unit:
                value += ' ' + unit
            dct['table'].append((key, desc, value))

    return dct
Example #7
0
File: res.py Project: nateharms/ase
    def get_string(self, significant_figures=6, write_info=False):
        """
        Returns a string to be written as a Res file.

        Args:
            significant_figures (int): No. of significant figures to
                output all quantities. Defaults to 6.

            write_info (bool): if True, format TITL line using key-value pairs
               from atoms.info in addition to attributes stored in Res object

        Returns:
            String representation of Res.
        """

        # Title line
        if write_info:
            info = self.atoms.info.copy()
            for attribute in ['name', 'pressure', 'energy',
                              'spacegroup', 'times_found']:
                if getattr(self, attribute) and attribute not in info:
                    info[attribute] = getattr(self, attribute)
            lines = ['TITL ' + ' '.join(['{0}={1}'.format(k, v)
                                         for (k, v) in info.items()])]
        else:
            lines = ['TITL ' + self.print_title()]

        # Cell
        abc_ang = cell_to_cellpar(self.atoms.get_cell())
        fmt = '{{0:.{0}f}}'.format(significant_figures)
        cell = ' '.join([fmt.format(a) for a in abc_ang])
        lines.append('CELL 1.0 ' + cell)

        # Latt
        lines.append('LATT -1')

        # Atoms
        symbols = self.atoms.get_chemical_symbols()
        species_types = []
        for symbol in symbols:
            if symbol not in species_types:
                species_types.append(symbol)
        lines.append('SFAC ' + ' '.join(species_types))

        fmt = '{{0}} {{1}} {{2:.{0}f}} {{3:.{0}f}} {{4:.{0}f}} 1.0'
        fmtstr = fmt.format(significant_figures)
        for symbol, coords in zip(symbols,
                                  self.atoms_.get_scaled_positions()):
            lines.append(
                fmtstr.format(symbol,
                              species_types.index(symbol) + 1,
                              coords[0],
                              coords[1],
                              coords[2]))
        lines.append('END')
        return '\n'.join(lines)
Example #8
0
def diffMap(cif,
            Ro=1.1745,
            b=0.514,
            co=6.,
            dx=0.4,
            dy=0.4,
            dz=0.4,
            mesh=None,
            anion=None):

    a = ase.io.read(cif)
    formula = ChemFormula(cif)
    counter_ion_label = max_electronegativity(formula)
    counter_ions = a[a.numbers == chem.element(
        counter_ion_label).atomic_number]
    counter_ion_positions = counter_ions.positions

    lat = np.linalg.norm(a.cell, axis=1)
    shape = tuple(int(round(i)) for i in lat / np.array([dx, dy, dz]))
    V = np.ones(shape)

    counter_ion_scaled_positions = counter_ions.get_scaled_positions().copy()
    connectivity = [
        np.array(i) for i in itr.product([-1, 0, 1], repeat=len(V.shape))
    ]
    counter_ion_scaled_positions = np.concatenate(
        [counter_ion_scaled_positions + con for con in connectivity])

    ctIons = np.einsum('ij,kj->ik', a.cell.T, counter_ion_scaled_positions).T

    counter_ion_positions = counter_ions.positions

    for i in range(len(V.flatten())):
        ri = np.dot(a.cell.T, (2 * np.array(unrav(i, V.shape)) + 1.) /
                    (2 * np.array([V.shape]).astype(float))[0])
        cti = ctIons[np.all(((ctIons < ri + co) & (ctIons > ri - co)),
                            axis=1)]  # counter ions
        # put in & instaed of == ...
        Ri = np.linalg.norm(cti - ri, axis=1)
        V[unrav(i,
                V.shape)] = np.abs(np.sum(np.exp((Ro - Ri[Ri < co]) / b)) - 1)

    output_filename = cif.split('.')[0] + '.grd'
    with open(output_filename, "w") as savefile:
        savefile.write("Bond Valence Sum Difference\r")  # Title
        from ase.geometry import cell_to_cellpar
        cellParams = cell_to_cellpar(a.cell)  # get ABC alpha, beta, gamma
        savefile.write(" ".join([str(k) for k in cellParams]) + "\r")
        savefile.write(" ".join([str(k) for k in V.shape]) + "\r")
        for i in np.nditer(V.flatten()):
            savefile.write("%.6f  " %
                           (i))  # Write each valence difference value

    return V
Example #9
0
def write_proteindatabank(fileobj, images, write_arrays=True):
    """Write images to PDB-file."""
    if isinstance(fileobj, basestring):
        fileobj = paropen(fileobj, 'w')

    if hasattr(images, 'get_positions'):
        images = [images]

    rotation = None
    if images[0].get_pbc().any():
        from ase.geometry import cell_to_cellpar, cellpar_to_cell

        currentcell = images[0].get_cell()
        cellpar = cell_to_cellpar(currentcell)
        exportedcell = cellpar_to_cell(cellpar)
        rotation = np.linalg.solve(currentcell, exportedcell)
        # ignoring Z-value, using P1 since we have all atoms defined explicitly
        format = 'CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1\n'
        fileobj.write(format % (cellpar[0], cellpar[1], cellpar[2], cellpar[3],
                                cellpar[4], cellpar[5]))

    #     1234567 123 6789012345678901   89   67   456789012345678901234567 890
    format = ('ATOM  %5d %4s MOL     1    %8.3f%8.3f%8.3f%6.2f%6.2f'
              '          %2s  \n')

    # RasMol complains if the atom index exceeds 100000. There might
    # be a limit of 5 digit numbers in this field.
    MAXNUM = 100000

    symbols = images[0].get_chemical_symbols()
    natoms = len(symbols)

    for n, atoms in enumerate(images):
        fileobj.write('MODEL     ' + str(n + 1) + '\n')
        p = atoms.get_positions()
        occupancy = np.ones(len(atoms))
        bfactor = np.zeros(len(atoms))
        if write_arrays:
            if 'occupancy' in atoms.arrays:
                occupancy = atoms.get_array('occupancy')
            if 'bfactor' in atoms.arrays:
                bfactor = atoms.get_array('bfactor')
        if rotation is not None:
            p = p.dot(rotation)
        for a in range(natoms):
            x, y, z = p[a]
            occ = occupancy[a]
            bf = bfactor[a]
            fileobj.write(
                format %
                (a % MAXNUM, symbols[a], x, y, z, occ, bf, symbols[a].upper()))
        fileobj.write('ENDMDL\n')
Example #10
0
 def get_cell(ps):
     descr = ps.get_description().replace('\n', '\n%s' % (' ' * 8))
     add_line('Poisson solver: %s' % descr, 8)
     if hasattr(ps, 'gd'):
         gd = ps.gd
         par = cell_to_cellpar(gd.cell_cv * Bohr)
         h_eff = gd.dv**(1.0 / 3.0) * Bohr
         l1 = '{:8d} x {:8d} x {:8d} points'.format(*gd.N_c)
         l2 = '{:8.2f} x {:8.2f} x {:8.2f} AA'.format(*par[:3])
         l3 = 'Effective grid spacing dv^(1/3) = {:.4f}'.format(h_eff)
         add_line('Grid: %s' % l1, 8)
         add_line('      %s' % l2, 8)
         add_line('      %s' % l3, 8)
Example #11
0
 def __get_unitcell(self):
     """Wrapper to get the unit cell from different structure classes"""
     from javelin.unitcell import UnitCell
     try:  # javelin structure
         return self.structure.unitcell
     except AttributeError:
         try:  # diffpy structure
             return UnitCell(self.structure.lattice.abcABG())
         except AttributeError:
             try:  # ASE structure
                 from ase.geometry import cell_to_cellpar
                 return UnitCell(cell_to_cellpar(self.structure.cell))
             except (ImportError, AttributeError) as e:
                 print(e)
                 raise ValueError("Unable to get unit cell from structure")
Example #12
0
def set_substrate(atoms,
                  a=None,
                  b=None,
                  c=None,
                  angle_ab=90.0,
                  all_angle_90=False,
                  m=1,
                  fix_volume=False):
    """
    set atoms cellpars to fit the substrate cellpars.
    a,b , angle_ab are the  the inplane cellpars of the substrate
    all_angle_90: if True,all the angles will be set to 90
    m is the multiplier. a*m b*m
    fix_volume: whether to set c so that the volume is unchanged.
    Note that this is not always really the case if angles of a-b plane and c is changed.
    """
    cellpars = cell_to_cellpar(atoms.get_cell())
    print(a, b, c)

    a0, b0, c0 = cellpars[0:3]
    if a is not None:
        cellpars[0] = a * m
    if b is not None:
        cellpars[1] = b * m
    if c is not None:
        cellpars[2] = c * m
    if angle_ab is not None:
        cellpars[5] = angle_ab
    if all_angle_90:
        cellpars[3:] = [90, 90, 90]

    print(cellpars)
    if fix_volume:
        ab = cellpars[5]
        c = (a0 * b0 * c0 * math.sin(math.radians(ab)) /
             (a * b * m * m * math.sin(math.radians(angle_ab))))
        cellpars[2] = c

    cell = cellpar_to_cell(cellpars)
    print(cell)
    spos = atoms.get_scaled_positions()

    atoms.set_cell(cell)
    atoms.set_scaled_positions(spos)
    return atoms
Example #13
0
def print_cell(gd, pbc_c, log):

    log("""Unit cell:
           periodic     x           y           z      points  spacing""")
    h_c = gd.get_grid_spacings()
    for c in range(3):
        log('  %d. axis:    %s  %10.6f  %10.6f  %10.6f   %3d   %8.4f'
            % ((c + 1, ['no ', 'yes'][int(pbc_c[c])]) +
               tuple(Bohr * gd.cell_cv[c]) +
               (gd.N_c[c], Bohr * h_c[c])))

    par = cell_to_cellpar(gd.cell_cv * Bohr)
    log('\n  Lengths: {:10.6f} {:10.6f} {:10.6f}'.format(*par[:3]))
    log('  Angles:  {:10.6f} {:10.6f} {:10.6f}\n'.format(*par[3:]))

    h_eff = gd.dv**(1.0 / 3.0) * Bohr
    log('Effective grid spacing dv^(1/3) = {:.4f}'.format(h_eff))
    log()
Example #14
0
def write_jsv(f, atoms):
    """Writes JSV file."""
    f.write('asymmetric_unit_cell\n')

    f.write('[cell]')
    for v in cell_to_cellpar(atoms.cell):
        f.write('  %g' % v)
    f.write('\n')

    f.write('[natom]  %d\n' % len(atoms))
    f.write('[nbond]  0\n')  # FIXME
    f.write('[npoly]  0\n')  # FIXME

    if 'spacegroup' in atoms.info:
        sg = Spacegroup(atoms.info['spacegroup'])
        f.write('[space_group]  %d %d\n' % (sg.no, sg.setting))
    else:
        f.write('[space_group]  1  1\n')

    f.write('[title] %s\n' % atoms.info.get('title', 'untitled'))

    f.write('\n')
    f.write('[atoms]\n')
    if 'labels' in atoms.info:
        labels = atoms.info['labels']
    else:
        labels = [
            '%s%d' % (s, i + 1)
            for i, s in enumerate(atoms.get_chemical_symbols())
        ]
    numbers = atoms.get_atomic_numbers()
    scaled = atoms.get_scaled_positions()
    for l, n, p in zip(labels, numbers, scaled):
        f.write('%-4s  %2d  %9.6f  %9.6f  %9.6f\n' % (l, n, p[0], p[1], p[2]))

    f.write('Label  AtomicNumber  x y z (repeat natom times)\n')

    f.write('\n')
    f.write('[bonds]\n')

    f.write('\n')
    f.write('[poly]\n')

    f.write('\n')
Example #15
0
def test_monoclinic():
    """Test band structure from different variations of hexagonal cells."""
    import numpy as np
    from ase import Atoms
    from ase.calculators.test import FreeElectrons
    from ase.geometry import (crystal_structure_from_cell, cell_to_cellpar,
                              cellpar_to_cell)
    from ase.dft.kpoints import get_special_points

    mc1 = [[1, 0, 0], [0, 1, 0], [0, 0.2, 1]]
    par = cell_to_cellpar(mc1)
    mc2 = cellpar_to_cell(par)
    mc3 = [[1, 0, 0], [0, 1, 0], [-0.2, 0, 1]]
    mc4 = [[1, 0, 0], [-0.2, 1, 0], [0, 0, 1]]
    path = 'GYHCEM1AXH1'

    firsttime = True
    for cell in [mc1, mc2, mc3, mc4]:
        a = Atoms(cell=cell, pbc=True)
        a.cell *= 3
        a.calc = FreeElectrons(nvalence=1, kpts={'path': path})
        cs = crystal_structure_from_cell(a.cell)
        assert cs == 'monoclinic'
        r = a.cell.reciprocal()
        k = get_special_points(a.cell)['H']
        print(np.dot(k, r))
        a.get_potential_energy()
        bs = a.calc.band_structure()
        coords, labelcoords, labels = bs.get_labels()
        assert ''.join(labels) == path
        e_skn = bs.energies
        # bs.plot()
        if firsttime:
            coords1 = coords
            labelcoords1 = labelcoords
            e_skn1 = e_skn
            firsttime = False
        else:
            for d in [
                    coords - coords1, labelcoords - labelcoords1,
                    e_skn - e_skn1
            ]:
                print(abs(d).max())
                assert abs(d).max() < 1e-13, d
Example #16
0
def write_v_sim(filename, atoms):
    """Write V_Sim input file.

    Writes the atom positions and unit cell.
    """
    from ase.geometry import cellpar_to_cell, cell_to_cellpar

    if isinstance(filename, str):
        f = open(filename)
    else:  # Assume it's a file-like object
        f = filename

    # Convert the lattice vectors to triangular matrix by converting
    #   to and from a set of lengths and angles
    cell = cellpar_to_cell(cell_to_cellpar(atoms.cell))
    dxx = cell[0, 0]
    dyx, dyy = cell[1, 0:2]
    dzx, dzy, dzz = cell[2, 0:3]

    f.write('===== v_sim input file created using the'
            ' Atomic Simulation Environment (ASE) ====\n')
    f.write('{0} {1} {2}\n'.format(dxx, dyx, dyy))
    f.write('{0} {1} {2}\n'.format(dzx, dzy, dzz))

    # Use v_sim 3.5 keywords to indicate scaled positions, etc.
    f.write('#keyword: reduced\n')
    f.write('#keyword: angstroem\n')
    if np.alltrue(atoms.pbc):
        f.write('#keyword: periodic\n')
    elif not np.any(atoms.pbc):
        f.write('#keyword: freeBC\n')
    elif np.array_equiv(atoms.pbc, [True, False, True]):
        f.write('#keyword: surface\n')
    else:
        raise Exception(
            'Only supported boundary conditions are full PBC,'
            ' no periodic boundary, and surface which is free in y direction'
            ' (i.e. Atoms.pbc = [True, False, True]).')

    # Add atoms (scaled positions)
    for position, symbol in zip(atoms.get_scaled_positions(),
                                atoms.get_chemical_symbols()):
        f.write('{0} {1} {2} {3}\n'.format(position[0], position[1],
                                           position[2], symbol))
Example #17
0
def write_v_sim(filename, atoms):
    """Write V_Sim input file.

    Writes the atom positions and unit cell.
    """
    from ase.geometry import cellpar_to_cell, cell_to_cellpar

    if isinstance(filename, str):
        f = open(filename)
    else:  # Assume it's a file-like object
        f = filename

    # Convert the lattice vectors to triangular matrix by converting
    #   to and from a set of lengths and angles
    cell = cellpar_to_cell(cell_to_cellpar(atoms.cell))
    dxx = cell[0, 0]
    dyx, dyy = cell[1, 0:2]
    dzx, dzy, dzz = cell[2, 0:3]

    f.write('===== v_sim input file created using the'
            ' Atomic Simulation Environment (ASE) ====\n')
    f.write('{0} {1} {2}\n'.format(dxx, dyx, dyy))
    f.write('{0} {1} {2}\n'.format(dzx, dzy, dzz))

    # Use v_sim 3.5 keywords to indicate scaled positions, etc.
    f.write('#keyword: reduced\n')
    f.write('#keyword: angstroem\n')
    if np.alltrue(atoms.pbc):
        f.write('#keyword: periodic\n')
    elif not np.any(atoms.pbc):
        f.write('#keyword: freeBC\n')
    elif np.array_equiv(atoms.pbc, [True, False, True]):
        f.write('#keyword: surface\n')
    else:
        raise Exception('Only supported boundary conditions are full PBC,'
        ' no periodic boundary, and surface which is free in y direction'
        ' (i.e. Atoms.pbc = [True, False, True]).')

    # Add atoms (scaled positions)
    for position, symbol in zip(atoms.get_scaled_positions(),
                                atoms.get_chemical_symbols()):
        f.write('{0} {1} {2} {3}\n'.format(
            position[0], position[1], position[2], symbol))
Example #18
0
def write_proteindatabank(fileobj, images):
    """Write images to PDB-file."""
    if isinstance(fileobj, basestring):
        fileobj = paropen(fileobj, 'w')

    if hasattr(images, 'get_positions'):
        images = [images]


    rotation = None
    if images[0].get_pbc().any():
        from ase.geometry import cell_to_cellpar, cellpar_to_cell

        currentcell = images[0].get_cell()
        cellpar = cell_to_cellpar(currentcell)
        exportedcell = cellpar_to_cell(cellpar)
        rotation = np.linalg.solve(currentcell, exportedcell)
        # ignoring Z-value, using P1 since we have all atoms defined explicitly
        format = 'CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1\n'
        fileobj.write(format % (cellpar[0], cellpar[1], cellpar[2],
                                cellpar[3], cellpar[4], cellpar[5]))

    #     1234567 123 6789012345678901   89   67   456789012345678901234567 890
    format = ('ATOM  %5d %4s MOL     1    %8.3f%8.3f%8.3f  1.00  0.00'
              '          %2s  \n')

    # RasMol complains if the atom index exceeds 100000. There might
    # be a limit of 5 digit numbers in this field.
    MAXNUM = 100000

    symbols = images[0].get_chemical_symbols()
    natoms = len(symbols)

    for n, atoms in enumerate(images):
        fileobj.write('MODEL     ' + str(n + 1) + '\n')
        p = atoms.get_positions()
        if rotation is not None:
            p = p.dot(rotation)
        for a in range(natoms):
            x, y, z = p[a]
            fileobj.write(format % (a % MAXNUM, symbols[a],
                                    x, y, z, symbols[a].upper()))
        fileobj.write('ENDMDL\n')
Example #19
0
def test():
    myatoms = gen_STO()
    print(find_sym(myatoms))
    #print myatoms.get_chemical_symbols()
    #natoms,d=ref_atoms_mag(myatoms)
    ##print ref_atoms_mag(myatoms)[0].get_chemical_symbols()
    #old_atoms=rev_ref_atoms(natoms,d)
    #print old_atoms.get_chemical_symbols()
    #print get_prim_atoms(myatoms)
    new_atoms = find_primitive_mag(myatoms)
    #print new_atoms.get_chemical_symbols()
    print(new_atoms.get_cell())
    print(new_atoms.get_scaled_positions())
    print(new_atoms.get_volume())
    print(spglib.get_spacegroup(new_atoms))
    #write('POSCAR',new_atoms,sort=True,vasp5=True)
    natoms = normalize(new_atoms)
    print(natoms.get_positions())
    print(natoms.get_volume())
    print(cell_to_cellpar(natoms.get_cell()))
Example #20
0
def write_jsv(f, atoms):
    """Writes JSV file."""
    f.write("asymmetric_unit_cell\n")

    f.write("[cell]")
    for v in cell_to_cellpar(atoms.cell):
        f.write("  %g" % v)
    f.write("\n")

    f.write("[natom]  %d\n" % len(atoms))
    f.write("[nbond]  0\n")  # FIXME
    f.write("[npoly]  0\n")  # FIXME

    if "spacegroup" in atoms.info:
        sg = Spacegroup(atoms.info["spacegroup"])
        f.write("[space_group]  %d %d\n" % (sg.no, sg.setting))
    else:
        f.write("[space_group]  1  1\n")

    f.write("[title] %s\n" % atoms.info.get("title", "untitled"))

    f.write("\n")
    f.write("[atoms]\n")
    if "labels" in atoms.info:
        labels = atoms.info["labels"]
    else:
        labels = ["%s%d" % (s, i + 1) for i, s in enumerate(atoms.get_chemical_symbols())]
    numbers = atoms.get_atomic_numbers()
    scaled = atoms.get_scaled_positions()
    for l, n, p in zip(labels, numbers, scaled):
        f.write("%-4s  %2d  %9.6f  %9.6f  %9.6f\n" % (l, n, p[0], p[1], p[2]))

    f.write("Label  AtomicNumber  x y z (repeat natom times)\n")

    f.write("\n")
    f.write("[bonds]\n")

    f.write("\n")
    f.write("[poly]\n")

    f.write("\n")
Example #21
0
def normalize(atoms, set_origin=False):
    """
    set the most near 0 to 0
    make cell -> cellpar ->cell
    make all the scaled postion between 0 and 1
    """
    newatoms = atoms.copy()
    newatoms = force_near_0(newatoms)
    if set_origin:
        positions = newatoms.get_positions()
        s_positions = sorted(positions, key=np.linalg.norm)
        newatoms.translate(-s_positions[0])

    positions = newatoms.get_scaled_positions()
    newcell = cellpar_to_cell(cell_to_cellpar(newatoms.get_cell()))
    newatoms.set_cell(newcell)

    for i, pos in enumerate(positions):
        positions[i] = np.mod(pos, 1.0)

    newatoms.set_scaled_positions(positions)
    return newatoms
Example #22
0
 def get_lattice_type(self):
     cellpar = cell_to_cellpar(self.atoms.cell)
     abc = cellpar[:3]
     angles = cellpar[3:]
     min_lv = min(abc)
     if abc.ptp() < 0.01 * min_lv:
         if abs(angles - 90).max() < 1:
             return 'cubic'
         elif abs(angles - 60).max() < 1:
             return 'fcc'
         elif abs(angles - np.arccos(-1 / 3.) * 180 / np.pi).max < 1:
             return 'bcc'
     elif abs(angles - 90).max() < 1:
         if abs(abc[0] - abc[1]).min() < 0.01 * min_lv:
             return 'tetragonal'
         else:
             return 'orthorhombic'
     elif abs(abc[0] - abc[1]) < 0.01 * min_lv and \
             abs(angles[2] - 120) < 1 and abs(angles[:2] - 90).max() < 1:
         return 'hexagonal'
     else:
         return 'not special'
Example #23
0
def write_pdb(fileobj, images):
    """Write images to PDB-file.

    The format is assumed to follow the description given in
    http://www.wwpdb.org/documentation/format32/sect9.html."""
    if isinstance(fileobj, str):
        fileobj = paropen(fileobj, 'w')

    if not isinstance(images, (list, tuple)):
        images = [images]

    if images[0].get_pbc().any():
        from ase.geometry import cell_to_cellpar
        cellpar = cell_to_cellpar(images[0].get_cell())
        # ignoring Z-value, using P1 since we have all atoms defined explicitly
        format = 'CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1\n'
        fileobj.write(format % (cellpar[0], cellpar[1], cellpar[2], cellpar[3],
                                cellpar[4], cellpar[5]))

    #         1234567 123 6789012345678901   89   67   456789012345678901234567 890
    format = 'ATOM  %5d %4s MOL     1    %8.3f%8.3f%8.3f  1.00  0.00          %2s  \n'

    # RasMol complains if the atom index exceeds 100000. There might
    # be a limit of 5 digit numbers in this field.
    MAXNUM = 100000

    symbols = images[0].get_chemical_symbols()
    natoms = len(symbols)

    for n, atoms in enumerate(images):
        fileobj.write('MODEL     ' + str(n + 1) + '\n')
        p = atoms.get_positions()
        for a in range(natoms):
            x, y, z = p[a]
            fileobj.write(
                format %
                (a % MAXNUM, symbols[a], x, y, z, symbols[a].rjust(2)))
        fileobj.write('ENDMDL\n')
Example #24
0
def write_proteindatabank(fileobj, images):
    """Write images to PDB-file.

    The format is assumed to follow the description given in
    http://www.wwpdb.org/documentation/format32/sect9.html."""
    if isinstance(fileobj, str):
        fileobj = paropen(fileobj, 'w')

    if hasattr(images, 'get_positions'):
        images = [images]

    if images[0].get_pbc().any():
        from ase.geometry import cell_to_cellpar
        cellpar = cell_to_cellpar(images[0].get_cell())
        # ignoring Z-value, using P1 since we have all atoms defined explicitly
        format = 'CRYST1%9.3f%9.3f%9.3f%7.2f%7.2f%7.2f P 1\n'
        fileobj.write(format % (cellpar[0], cellpar[1], cellpar[2],
                                cellpar[3], cellpar[4], cellpar[5]))

    #     1234567 123 6789012345678901   89   67   456789012345678901234567 890
    format = ('ATOM  %5d %4s MOL     1    %8.3f%8.3f%8.3f  1.00  0.00'
              '          %2s  \n')

    # RasMol complains if the atom index exceeds 100000. There might
    # be a limit of 5 digit numbers in this field.
    MAXNUM = 100000

    symbols = images[0].get_chemical_symbols()
    natoms = len(symbols)

    for n, atoms in enumerate(images):
        fileobj.write('MODEL     ' + str(n + 1) + '\n')
        p = atoms.get_positions()
        for a in range(natoms):
            x, y, z = p[a]
            fileobj.write(format % (a % MAXNUM, symbols[a],
                                    x, y, z, symbols[a].rjust(2)))
        fileobj.write('ENDMDL\n')
Example #25
0
def ase_to_eq_cif(ase_obj, supply_sg=True):
    """
    From ASE object generate CIF
    with symmetry-equivalent atoms;
    mostly for debugging
    """
    if supply_sg:
        sg_hm = getattr(ase_obj.info.get('spacegroup', object), 'symbol', 'P1')
        sg_n = getattr(ase_obj.info.get('spacegroup', object), 'no', 1)
    else:
        sg_hm, sg_n = 'P1', 1

    parameters = cell_to_cellpar(ase_obj.cell)

    cif_data = 'data_tilde_labs\n'
    cif_data += '_cell_length_a    ' + "%2.6f" % parameters[0] + "\n"
    cif_data += '_cell_length_b    ' + "%2.6f" % parameters[1] + "\n"
    cif_data += '_cell_length_c    ' + "%2.6f" % parameters[2] + "\n"
    cif_data += '_cell_angle_alpha ' + "%2.6f" % parameters[3] + "\n"
    cif_data += '_cell_angle_beta  ' + "%2.6f" % parameters[4] + "\n"
    cif_data += '_cell_angle_gamma ' + "%2.6f" % parameters[5] + "\n"
    cif_data += "_symmetry_space_group_name_H-M '%s'" % sg_hm + "\n"
    cif_data += "_symmetry_Int_Tables_number %s" % sg_n + "\n"
    cif_data += 'loop_' + "\n"
    cif_data += ' _symmetry_equiv_pos_as_xyz' + "\n"
    cif_data += ' +x,+y,+z' + "\n"
    cif_data += 'loop_' + "\n"
    cif_data += ' _atom_site_type_symbol' + "\n"
    cif_data += ' _atom_site_fract_x' + "\n"
    cif_data += ' _atom_site_fract_y' + "\n"
    cif_data += ' _atom_site_fract_z' + "\n"

    pos = ase_obj.get_scaled_positions(wrap=False)
    for n, item in enumerate(ase_obj):
        cif_data += " {:3s} {: 6.5f} {: 6.5f} {: 6.5f}\n".format(
            item.symbol, pos[n][0], pos[n][1], pos[n][2])
    return cif_data
Example #26
0
def generate_cif(structure, comment=None, symops=['+x,+y,+z']):
    parameters = cell_to_cellpar(structure.cell)
    cif_data = "# " + comment + "\n\n" if comment else ''
    cif_data += 'data_tilde_project\n'
    cif_data += '_cell_length_a    ' + "%2.6f" % parameters[0] + "\n"
    cif_data += '_cell_length_b    ' + "%2.6f" % parameters[1] + "\n"
    cif_data += '_cell_length_c    ' + "%2.6f" % parameters[2] + "\n"
    cif_data += '_cell_angle_alpha ' + "%2.6f" % parameters[3] + "\n"
    cif_data += '_cell_angle_beta  ' + "%2.6f" % parameters[4] + "\n"
    cif_data += '_cell_angle_gamma ' + "%2.6f" % parameters[5] + "\n"
    cif_data += "_symmetry_space_group_name_H-M 'P1'" + "\n"
    cif_data += 'loop_' + "\n"
    cif_data += '_symmetry_equiv_pos_as_xyz' + "\n"
    for i in symops:
        cif_data += i + "\n"
    cif_data += 'loop_' + "\n"
    cif_data += '_atom_site_type_symbol' + "\n"
    cif_data += '_atom_site_fract_x' + "\n"
    cif_data += '_atom_site_fract_y' + "\n"
    cif_data += '_atom_site_fract_z' + "\n"
    pos = structure.get_scaled_positions()
    for n, i in enumerate(structure):
        cif_data += "%s   % 1.8f   % 1.8f   % 1.8f\n" % (i.symbol, pos[n][0], pos[n][1], pos[n][2])
    return cif_data
def atomsToPDB(fileobj, atoms):
    ''' ASE idiotically truncates to 3dp when writing .pdb 
        This is a simplification of ase write (note - code taken from ase/io/proteindatabank.py
        Keep as atoms rather than Crystal to fit with aseToRDKit stuff '''
    from ase.geometry import cell_to_cellpar
    # THIS CANNOT BE READ BY RDKIT SO IS NOT MUCH USE !!!
    if isinstance(fileobj, basestring):
        fileobj = paropen(fileobj, 'w')

    cellpar = cell_to_cellpar(atoms.get_cell())
    fileobj.write('CRYST1 ' + ' '.join(['%s' % x for x in cellpar]) + ' P 1\n')
    fileobj.write('MODEL 1\n')

    symbols = atoms.get_chemical_symbols()
    natoms = len(symbols)
    format = ('ATOM  %s %s MOL     1    %s %s %s 1.00 0.00'
              '          %2s  \n')
    p = atoms.get_positions()
    for a in range(natoms):
        x, y, z = p[a]
        fileobj.write(format % (a, symbols[a], x, y, z, symbols[a].upper()))
    fileobj.write('ENDMDL\n')

    return fileobj
Example #28
0
def write_eon(fileobj, images):
    """Writes structure to EON reactant.con file
    Multiple snapshots are not allowed."""
    if isinstance(fileobj, str):
        f = paropen(fileobj, 'w')
    else:
        f = fileobj

    if isinstance(images, Atoms):
        atoms = images
    elif len(images) == 1:
        atoms = images[0]
    else:
        raise ValueError('Can only write one configuration to EON '
                         'reactant.con file')

    out = []
    out.append(atoms.info.get('comment', 'Generated by ASE'))
    out.append('0.0000 TIME')  # ??

    a, b, c, alpha, beta, gamma = cell_to_cellpar(atoms.cell)
    out.append('%-10.6f  %-10.6f  %-10.6f' % (a, b, c))
    out.append('%-10.6f  %-10.6f  %-10.6f' % (gamma, beta, alpha))

    out.append('0 0')    # ??
    out.append('0 0 0')  # ??

    symbols = atoms.get_chemical_symbols()
    massdict = dict(list(zip(symbols, atoms.get_masses())))
    atomtypes = sorted(massdict.keys())
    atommasses = [massdict[at] for at in atomtypes]
    natoms = [symbols.count(at) for at in atomtypes]
    ntypes = len(atomtypes)

    out.append(str(ntypes))
    out.append(' '.join([str(n) for n in natoms]))
    out.append(' '.join([str(n) for n in atommasses]))

    atom_id = 0
    for n in range(ntypes):
        fixed = np.array([False] * len(atoms))
        out.append(atomtypes[n])
        out.append('Coordinates of Component %d' % (n + 1))
        indices = [i for i, at in enumerate(symbols) if at == atomtypes[n]]
        a = atoms[indices]
        coords = a.positions
        for c in a.constraints:
            if not isinstance(c, FixAtoms):
                warn('Only FixAtoms constraints are supported by con files. '
                     'Dropping %r', c)
                continue
            if c.index.dtype.kind == 'b':
                fixed = np.array(c.index, dtype=int)
            else:
                fixed = np.zeros((natoms[n], ), dtype=int)
                for i in c.index:
                    fixed[i] = 1
        for xyz, fix in zip(coords, fixed):
            out.append('%22.17f %22.17f %22.17f %d %4d' %
                       (tuple(xyz) + (fix, atom_id)))
            atom_id += 1
    f.write('\n'.join(out))
    f.write('\n')

    if isinstance(fileobj, str):
        f.close()
Example #29
0
            # -v option
            if args.convergence:
                if calc.convergence:
                    output_lines += str(calc.convergence) + "\n"
                if calc.tresholds:
                    for i in range(len(calc.tresholds)):
                        try: ncycles = calc.ncycles[i]
                        except IndexError: ncycles = ""
                        output_lines += "%1.2e" % calc.tresholds[i][0] + " "*2 + "%1.5f" % calc.tresholds[i][1] + " "*2 + "%1.4f" % calc.tresholds[i][2] + " "*2 + "%1.4f" % calc.tresholds[i][3] + " "*2 + "E=" + "%1.4f" % calc.tresholds[i][4] + " eV" + " "*2 + "(%s)" % ncycles + "\n"

            # -s option
            if args.structures:
                out = ''
                if len(calc.structures) > 1:
                    out += str(cell_to_cellpar(calc.structures[0].cell)) + " V=%2.2f" % (abs(det(calc.structures[0].cell))) + ' -> '
                out += str(cell_to_cellpar(calc.structures[-1].cell))
                out += " V=%2.2f\n" % calc.info['dims']
                for i in calc.structures[-1]:
                    out += " %s %s %s %s\n" % (i.symbol, i.x, i.y, i.z)
                output_lines += out

            # -c option
            if args.cif:
                try: calc.structures[ args.cif ]
                except IndexError: output_lines += "Warning! Structure "+args.cif+" not found!" + "\n"
                else:
                    N = args.cif if args.cif>0 else len(calc.structures) + 1 + args.cif
                    comment = calc.info['formula'] + " extracted from " + task + " (structure N " + str(N) + ")"
                    cif_file = os.path.realpath(os.path.abspath(DATA_DIR + os.sep + os.path.basename(task))) + '_' + str(args.cif) + '.cif'
                    if write_cif(cif_file, calc.structures[ args.cif ], comment):
Example #30
0
def get_special_points(lattice, cell, eps=1e-4):
    """Return dict of special points.
    
    The definitions are from a paper by Wahyu Setyawana and Stefano
    Curtarolo::
        
        http://dx.doi.org/10.1016/j.commatsci.2010.05.010
    
    lattice: str
        One of the following: cubic, fcc, bcc, orthorhombic, tetragonal,
        hexagonal or monoclinic.
    cell: 3x3 ndarray
        Unit cell.
    eps: float
        Tolerance for cell-check.
    """

    lattice = lattice.lower()

    cellpar = cell_to_cellpar(cell=cell)
    abc = cellpar[:3]
    angles = cellpar[3:] / 180 * pi
    a, b, c = abc
    alpha, beta, gamma = angles

    # Check that the unit-cells are as in the Setyawana-Curtarolo paper:
    if lattice == 'cubic':
        assert abc.ptp() < eps and abs(angles - pi / 2).max() < eps
    elif lattice == 'fcc':
        assert abc.ptp() < eps and abs(angles - pi / 3).max() < eps
    elif lattice == 'bcc':
        angle = np.arccos(-1 / 3)
        assert abc.ptp() < eps and abs(angles - angle).max() < eps
    elif lattice == 'tetragonal':
        assert abs(a - b) < eps and abs(angles - pi / 2).max() < eps
    elif lattice == 'orthorhombic':
        assert abs(angles - pi / 2).max() < eps
    elif lattice == 'hexagonal':
        assert abs(a - b) < eps
        assert abs(gamma - pi / 3 * 2) < eps
        assert abs(angles[:2] - pi / 2).max() < eps
    elif lattice == 'monoclinic':
        assert c >= a and c >= b
        assert alpha < pi / 2 and abs(angles[1:] - pi / 2).max() < eps

    if lattice != 'monoclinic':
        return special_points[lattice]

    # Here, we need the cell:
    eta = (1 - b * cos(alpha) / c) / (2 * sin(alpha)**2)
    nu = 1 / 2 - eta * c * cos(alpha) / b
    return {
        'G': [0, 0, 0],
        'A': [1 / 2, 1 / 2, 0],
        'C': [0, 1 / 2, 1 / 2],
        'D': [1 / 2, 0, 1 / 2],
        'D1': [1 / 2, 0, -1 / 2],
        'E': [1 / 2, 1 / 2, 1 / 2],
        'H': [0, eta, 1 - nu],
        'H1': [0, 1 - eta, nu],
        'H2': [0, eta, -nu],
        'M': [1 / 2, eta, 1 - nu],
        'M1': [1 / 2, 1 - eta, nu],
        'M2': [1 / 2, eta, -nu],
        'X': [0, 1 / 2, 0],
        'Y': [0, 0, 1 / 2],
        'Y1': [0, 0, -1 / 2],
        'Z': [1 / 2, 0, 0]
    }
Example #31
0
def get_special_points(lattice, cell, eps=2e-4):
    """Return dict of special points.

    The definitions are from a paper by Wahyu Setyawana and Stefano
    Curtarolo::

        http://dx.doi.org/10.1016/j.commatsci.2010.05.010

    lattice: str
        One of the following: cubic, fcc, bcc, orthorhombic, tetragonal,
        hexagonal or monoclinic.
    cell: 3x3 ndarray
        Unit cell.
    eps: float
        Tolerance for cell-check.
    """

    lattice = lattice.lower()

    cellpar = cell_to_cellpar(cell=cell)
    abc = cellpar[:3]
    angles = cellpar[3:] / 180 * pi
    a, b, c = abc
    alpha, beta, gamma = angles

    # Check that the unit-cells are as in the Setyawana-Curtarolo paper:
    if lattice == 'cubic':
        assert abc.ptp() < eps and abs(angles - pi / 2).max() < eps
    elif lattice == 'fcc':
        assert abc.ptp() < eps and abs(angles - pi / 3).max() < eps
    elif lattice == 'bcc':
        angle = np.arccos(-1 / 3)
        assert abc.ptp() < eps and abs(angles - angle).max() < eps
    elif lattice == 'tetragonal':
        assert abs(a - b) < eps and abs(angles - pi / 2).max() < eps
    elif lattice == 'orthorhombic':
        assert abs(angles - pi / 2).max() < eps
    elif lattice == 'hexagonal':
        assert abs(a - b) < eps
        assert abs(gamma - pi / 3 * 2) < eps
        assert abs(angles[:2] - pi / 2).max() < eps
    elif lattice == 'monoclinic':
        assert c >= a and c >= b
        assert alpha < pi / 2 and abs(angles[1:] - pi / 2).max() < eps
    elif lattice == 'rhombohedral type 1':
        assert abc.ptp() < eps and angles.ptp() < eps
        assert abs(alpha) < pi / 2

    if lattice == 'monoclinic':
        # Here, we need the cell:
        eta = (1 - b * cos(alpha) / c) / (2 * sin(alpha)**2)
        nu = 1 / 2 - eta * c * cos(alpha) / b
        return {'G': [0, 0, 0],
                'A': [1 / 2, 1 / 2, 0],
                'C': [0, 1 / 2, 1 / 2],
                'D': [1 / 2, 0, 1 / 2],
                'D1': [1 / 2, 0, -1 / 2],
                'E': [1 / 2, 1 / 2, 1 / 2],
                'H': [0, eta, 1 - nu],
                'H1': [0, 1 - eta, nu],
                'H2': [0, eta, -nu],
                'M': [1 / 2, eta, 1 - nu],
                'M1': [1 / 2, 1 - eta, nu],
                'M2': [1 / 2, eta, -nu],
                'X': [0, 1 / 2, 0],
                'Y': [0, 0, 1 / 2],
                'Y1': [0, 0, -1 / 2],
                'Z': [1 / 2, 0, 0]}
    elif lattice == 'rhombohedral type 1':
        eta = (1 + 4 * np.cos(alpha)) / (2 + 4 * np.cos(alpha))
        nu = 3 / 4 - eta / 2
        return {'G': [0, 0, 0],
                'B': [eta, 1 / 2, 1 - eta],
                'B1': [1 / 2, 1 - eta, eta - 1],
                'F': [1 / 2, 1 / 2, 0],
                'L': [1 / 2, 0, 0],
                'L1': [0, 0, - 1 / 2],
                'P': [eta, nu, nu],
                'P1': [1 - nu, 1 - nu, 1 - eta],
                'P2': [nu, nu, eta - 1],
                'Q': [1 - nu, nu, 0],
                'X': [nu, 0, -nu],
                'Z': [0.5, 0.5, 0.5]}
    else:
        return special_points[lattice]
Example #32
0
    def write(self, atoms=None, frame=None, arrays=None, time=None):
        """
        Write the atoms to the file.

        If the atoms argument is not given, the atoms object specified
        when creating the trajectory object is used.
        """
        self._open()
        self._call_observers(self.pre_observers)
        if atoms is None:
            atoms = self.atoms

        if hasattr(atoms, 'interpolate'):
            # seems to be a NEB
            neb = atoms
            assert not neb.parallel
            try:
                neb.get_energies_and_forces(all=True)
            except AttributeError:
                pass
            for image in neb.images:
                self.write(image)
            return

        if not self.has_header:
            self._define_file_structure(atoms)
        else:
            if len(atoms) != self.n_atoms:
                raise ValueError('Bad number of atoms!')

        if frame is None:
            i = self.frame
        else:
            i = frame

        # Number can be per file variable
        numbers = self._get_variable(self._numbers_var)
        if numbers.dimensions[0] == self._frame_dim:
            numbers[i] = atoms.get_atomic_numbers()
        else:
            if np.any(numbers != atoms.get_atomic_numbers()):
                raise ValueError('Atomic numbers do not match!')
        self._get_variable(self._positions_var)[i] = atoms.get_positions()
        if atoms.has('momenta'):
            self._add_velocities()
            self._get_variable(self._velocities_var)[i] = \
                atoms.get_momenta() / atoms.get_masses().reshape(-1, 1)
        a, b, c, alpha, beta, gamma = cell_to_cellpar(atoms.get_cell())
        cell_lengths = np.array([a, b, c]) * atoms.pbc
        self._get_variable(self._cell_lengths_var)[i] = cell_lengths
        self._get_variable(self._cell_angles_var)[i] = [alpha, beta, gamma]
        if arrays is not None:
            for array in arrays:
                data = atoms.get_array(array)
                if array in self.extra_per_file_vars:
                    # This field exists but is per file data. Check that the
                    # data remains consistent.
                    if np.any(self._get_variable(array) != data):
                        raise ValueError('Trying to write Atoms object with '
                                         'incompatible data for the {0} '
                                         'array.'.format(array))
                else:
                    self._add_array(atoms, array, data.dtype, data.shape)
                    self._get_variable(array)[i] = data
        if time is not None:
            self._add_time()
            self._get_variable(self._time_var)[i] = time

        self.sync()

        self._call_observers(self.post_observers)
        self.frame += 1
        self._close()
Example #33
0
    def classify(self, calc, symprec=None):
        '''
        Reasons on normalization, invokes hierarchy API and prepares calc for saving
        NB: this is the PUBLIC method
        @returns tilde_obj, error
        '''
        error = None
        symbols = calc.structures[-1].get_chemical_symbols()
        calc.info['formula'] = self.formula(symbols)
        calc.info['cellpar'] = cell_to_cellpar(
            calc.structures[-1].cell).tolist()
        if calc.info['input']:
            try:
                calc.info['input'] = str(calc.info['input'], errors='ignore')
            except:
                pass

        # applying filter: todo
        if (calc.info['finished'] == 0x1 and self.settings['skip_unfinished']) or \
           (not calc.info['energy'] and self.settings['skip_notenergy']):
            return None, 'data do not satisfy the active filter'

        # naive elements extraction
        fragments = re.findall(r'([A-Z][a-z]?)(\d*[?:.\d+]*)?',
                               calc.info['formula'])
        for i in fragments:
            if i[0] == 'X': continue
            calc.info['elements'].append(i[0])
            calc.info['contents'].append(int(
                i[1])) if i[1] else calc.info['contents'].append(1)

        # extend hierarchy with modules
        for C_obj in self.Classifiers:
            try:
                calc = C_obj['classify'](calc)
            except:
                exc_type, exc_value, exc_tb = sys.exc_info()
                error = "Fatal error during classification:\n %s" % "".join(
                    traceback.format_exception(exc_type, exc_value, exc_tb))
                return None, error

        # chemical ratios
        if not len(calc.info['standard']):
            if len(calc.info['elements']) == 1: calc.info['expanded'] = 1
            if not calc.info['expanded']:
                calc.info['expanded'] = reduce(gcd, calc.info['contents'])
            for n, i in enumerate(
                [x // calc.info['expanded'] for x in calc.info['contents']]):
                if i == 1: calc.info['standard'] += calc.info['elements'][n]
                else:
                    calc.info['standard'] += calc.info['elements'][n] + str(i)
        if not calc.info['expanded']: del calc.info['expanded']
        calc.info['nelem'] = len(calc.info['elements'])
        if calc.info['nelem'] > 13: calc.info['nelem'] = 13
        calc.info['natom'] = len(symbols)

        # periodicity
        if calc.info['periodicity'] == 0: calc.info['periodicity'] = 0x4
        elif calc.info['periodicity'] == -1: calc.info['periodicity'] = 0x5

        # general calculation type reasoning
        if (calc.structures[-1].get_initial_charges() != 0).sum():
            calc.info['calctypes'].append(
                0x4)  # numpy count_nonzero implementation
        if (calc.structures[-1].get_initial_magnetic_moments() != 0).sum():
            calc.info['calctypes'].append(0x5)
        if calc.phonons['modes']: calc.info['calctypes'].append(0x6)
        if calc.phonons['ph_k_degeneracy']: calc.info['calctypes'].append(0x7)
        if calc.phonons['dielectric_tensor']:
            calc.info['calctypes'].append(0x8)  # CRYSTAL-only!
        if len(calc.tresholds) > 1:
            calc.info['calctypes'].append(0x3)
            calc.info['optgeom'] = True
        if calc.electrons['dos'] or calc.electrons['bands']:
            calc.info['calctypes'].append(0x2)
        if calc.info['energy']: calc.info['calctypes'].append(0x1)
        calc.info['spin'] = 0x2 if calc.info['spin'] else 0x1

        # TODO: standardize
        if 'vac' in calc.info:
            if 'X' in symbols:
                calc.info['techs'].append('vacancy defect: ghost')
            else:
                calc.info['techs'].append('vacancy defect: void space')

        calc.info['lata'] = round(calc.info['cellpar'][0], 3)
        calc.info['latb'] = round(calc.info['cellpar'][1], 3)
        calc.info['latc'] = round(calc.info['cellpar'][2], 3)
        calc.info['latalpha'] = round(calc.info['cellpar'][3], 2)
        calc.info['latbeta'] = round(calc.info['cellpar'][4], 2)
        calc.info['latgamma'] = round(calc.info['cellpar'][5], 2)

        # invoke symmetry finder
        found = SymmetryHandler(calc, symprec)
        if found.error:
            return None, found.error
        calc.info['sg'] = found.i
        calc.info['ng'] = found.n
        calc.info['symmetry'] = found.symmetry
        calc.info['spg'] = "%s &mdash; %s" % (found.n, found.i)
        calc.info['pg'] = found.pg
        calc.info['dg'] = found.dg

        # phonons
        if calc.phonons['dfp_magnitude']:
            calc.info['dfp_magnitude'] = round(calc.phonons['dfp_magnitude'],
                                               3)
        if calc.phonons['dfp_disps']:
            calc.info['dfp_disps'] = len(calc.phonons['dfp_disps'])
        if calc.phonons['modes']:
            calc.info['n_ph_k'] = len(
                calc.phonons['ph_k_degeneracy']
            ) if calc.phonons['ph_k_degeneracy'] else 1

        #calc.info['rgkmax'] = calc.electrons['rgkmax'] # LAPW

        # electronic properties reasoning by bands
        if calc.electrons['bands']:
            if calc.electrons['bands'].is_conductor():
                calc.info['etype'] = 0x2
                calc.info['bandgap'] = 0.0
                calc.info['bandgaptype'] = 0x1
            else:
                try:
                    gap, is_direct = calc.electrons['bands'].get_bandgap()
                except ElectronStructureError as e:
                    calc.electrons['bands'] = None
                    calc.warning(e.value)
                else:
                    calc.info['etype'] = 0x1
                    calc.info['bandgap'] = round(gap, 2)
                    calc.info['bandgaptype'] = 0x2 if is_direct else 0x3

        # electronic properties reasoning by DOS
        if calc.electrons['dos']:
            try:
                gap = round(calc.electrons['dos'].get_bandgap(), 2)
            except ElectronStructureError as e:
                calc.electrons['dos'] = None
                calc.warning(e.value)
            else:
                if calc.electrons['bands']:  # check coincidence
                    if abs(calc.info['bandgap'] - gap) > 0.2:
                        calc.warning(
                            'Bans gaps in DOS and bands data differ considerably! The latter will be considered.'
                        )
                else:
                    calc.info['bandgap'] = gap
                    if gap: calc.info['etype'] = 0x1
                    else:
                        calc.info['etype'] = 0x2
                        calc.info['bandgaptype'] = 0x1

        # TODO: beware to add something new to an existing item!
        # TODO2: unknown or absent?
        for entity in self.hierarchy:
            if entity['creates_topic'] and not entity[
                    'optional'] and not calc.info.get(entity['source']):
                if entity['enumerated']:
                    calc.info[entity['source']] = [
                        0x0
                    ] if entity['multiple'] else 0x0
                else:
                    calc.info[entity['source']] = [
                        'none'
                    ] if entity['multiple'] else 'none'

        calc.benchmark()  # this call must be at the very end of parsing

        return calc, error
Example #34
0
    def save(self, calc, session):
        '''
        Saves tilde_obj into the database
        NB: this is the PUBLIC method
        @returns checksum, error
        '''
        checksum = calc.get_checksum()

        try:
            existing_calc = session.query(model.Calculation).filter(
                model.Calculation.checksum == checksum).one()
        except NoResultFound:
            pass
        else:
            del calc
            return None, "This calculation already exists!"

        if not calc.download_size:
            for f in calc.related_files:
                calc.download_size += os.stat(f).st_size

        ormcalc = model.Calculation(checksum=checksum)

        if calc._calcset:
            ormcalc.meta_data = model.Metadata(
                chemical_formula=calc.info['standard'],
                download_size=calc.download_size)

            for child in session.query(model.Calculation).filter(
                    model.Calculation.checksum.in_(calc._calcset)).all():
                ormcalc.children.append(child)
            ormcalc.siblings_count = len(ormcalc.children)
            ormcalc.nested_depth = calc._nested_depth

        else:
            # prepare phonon data for saving
            # this is actually a dict to list conversion TODO re-structure this
            if calc.phonons['modes']:
                phonons_json = []

                for bzpoint, frqset in calc.phonons['modes'].items():
                    # re-orientate eigenvectors
                    for i in range(0,
                                   len(calc.phonons['ph_eigvecs'][bzpoint])):
                        for j in range(
                                0,
                                len(calc.phonons['ph_eigvecs'][bzpoint][i]) //
                                3):
                            eigv = array([
                                calc.phonons['ph_eigvecs'][bzpoint][i][j * 3],
                                calc.phonons['ph_eigvecs'][bzpoint][i][j * 3 +
                                                                       1],
                                calc.phonons['ph_eigvecs'][bzpoint][i][j * 3 +
                                                                       2]
                            ])
                            R = dot(eigv, calc.structures[-1].cell).tolist()
                            calc.phonons['ph_eigvecs'][bzpoint][i][
                                j * 3], calc.phonons['ph_eigvecs'][bzpoint][i][
                                    j * 3 +
                                    1], calc.phonons['ph_eigvecs'][bzpoint][i][
                                        j * 3 + 2] = [round(x, 3) for x in R]

                    try:
                        irreps = calc.phonons['irreps'][bzpoint]
                    except KeyError:
                        empty = []
                        for i in range(len(frqset)):
                            empty.append('')
                        irreps = empty
                    phonons_json.append({
                        'bzpoint':
                        bzpoint,
                        'freqs':
                        frqset,
                        'irreps':
                        irreps,
                        'ph_eigvecs':
                        calc.phonons['ph_eigvecs'][bzpoint]
                    })
                    if bzpoint == '0 0 0':
                        phonons_json[-1]['ir_active'] = calc.phonons[
                            'ir_active']
                        phonons_json[-1]['raman_active'] = calc.phonons[
                            'raman_active']
                    if calc.phonons['ph_k_degeneracy']:
                        phonons_json[-1]['ph_k_degeneracy'] = calc.phonons[
                            'ph_k_degeneracy'][bzpoint]

                ormcalc.phonons = model.Phonons()
                ormcalc.spectra.append(
                    model.Spectra(kind=model.Spectra.PHONON,
                                  eigenvalues=json.dumps(phonons_json)))

            # prepare electron data for saving TODO re-structure this
            for i in ['dos', 'bands']:  # projected?
                if calc.electrons[i]:
                    calc.electrons[i] = calc.electrons[i].todict()

            if calc.electrons['dos'] or calc.electrons['bands']:
                ormcalc.electrons = model.Electrons(gap=calc.info['bandgap'])
                if 'bandgaptype' in calc.info:
                    ormcalc.electrons.is_direct = 1 if calc.info[
                        'bandgaptype'] == 'direct' else -1
                ormcalc.spectra.append(
                    model.Spectra(
                        kind=model.Spectra.ELECTRON,
                        dos=json.dumps(calc.electrons['dos']),
                        bands=json.dumps(calc.electrons['bands']),
                        projected=json.dumps(calc.electrons['projected']),
                        eigenvalues=json.dumps(calc.electrons['eigvals'])))

            # construct ORM for other props
            calc.related_files = list(map(virtualize_path, calc.related_files))
            ormcalc.meta_data = model.Metadata(
                location=calc.info['location'],
                finished=calc.info['finished'],
                raw_input=calc.info['input'],
                modeling_time=calc.info['duration'],
                chemical_formula=html_formula(calc.info['standard']),
                download_size=calc.download_size,
                filenames=json.dumps(calc.related_files))

            codefamily = model.Codefamily.as_unique(
                session, content=calc.info['framework'])
            codeversion = model.Codeversion.as_unique(
                session, content=calc.info['prog'])

            codeversion.instances.append(ormcalc.meta_data)
            codefamily.versions.append(codeversion)

            pot = model.Pottype.as_unique(session, name=calc.info['H'])
            pot.instances.append(ormcalc)
            ormcalc.recipinteg = model.Recipinteg(
                kgrid=calc.info['k'],
                kshift=calc.info['kshift'],
                smearing=calc.info['smear'],
                smeartype=calc.info['smeartype'])
            ormcalc.basis = model.Basis(
                kind=calc.info['ansatz'],
                content=json.dumps(calc.electrons['basis_set'])
                if calc.electrons['basis_set'] else None)
            ormcalc.energy = model.Energy(convergence=json.dumps(
                calc.convergence),
                                          total=calc.info['energy'])

            ormcalc.spacegroup = model.Spacegroup(n=calc.info['ng'])
            ormcalc.struct_ratios = model.Struct_ratios(
                chemical_formula=calc.info['standard'],
                formula_units=calc.info['expanded'],
                nelem=calc.info['nelem'],
                dimensions=calc.info['dims'])
            if len(calc.tresholds) > 1:
                ormcalc.struct_optimisation = model.Struct_optimisation(
                    tresholds=json.dumps(calc.tresholds),
                    ncycles=json.dumps(calc.ncycles))

            for n, ase_repr in enumerate(calc.structures):
                is_final = True if n == len(calc.structures) - 1 else False
                struct = model.Structure(step=n, final=is_final)

                s = cell_to_cellpar(ase_repr.cell)
                struct.lattice = model.Lattice(a=s[0],
                                               b=s[1],
                                               c=s[2],
                                               alpha=s[3],
                                               beta=s[4],
                                               gamma=s[5],
                                               a11=ase_repr.cell[0][0],
                                               a12=ase_repr.cell[0][1],
                                               a13=ase_repr.cell[0][2],
                                               a21=ase_repr.cell[1][0],
                                               a22=ase_repr.cell[1][1],
                                               a23=ase_repr.cell[1][2],
                                               a31=ase_repr.cell[2][0],
                                               a32=ase_repr.cell[2][1],
                                               a33=ase_repr.cell[2][2])

                #rmts =      ase_repr.get_array('rmts') if 'rmts' in ase_repr.arrays else [None for j in range(len(ase_repr))]
                charges = ase_repr.get_array(
                    'charges') if 'charges' in ase_repr.arrays else [
                        None for j in range(len(ase_repr))
                    ]
                magmoms = ase_repr.get_array(
                    'magmoms') if 'magmoms' in ase_repr.arrays else [
                        None for j in range(len(ase_repr))
                    ]
                for n, i in enumerate(ase_repr):
                    struct.atoms.append(
                        model.Atom(number=chemical_symbols.index(i.symbol),
                                   x=i.x,
                                   y=i.y,
                                   z=i.z,
                                   charge=charges[n],
                                   magmom=magmoms[n]))

                ormcalc.structures.append(struct)
            # TODO Forces

        ormcalc.uigrid = model.Grid(info=json.dumps(calc.info))

        # tags ORM
        uitopics = []
        for entity in self.hierarchy:

            if not entity['creates_topic']: continue

            if entity['multiple'] or calc._calcset:
                for item in calc.info.get(entity['source'], []):
                    uitopics.append(model.topic(cid=entity['cid'], topic=item))
            else:
                topic = calc.info.get(entity['source'])
                if topic or not entity['optional']:
                    uitopics.append(model.topic(cid=entity['cid'],
                                                topic=topic))

        uitopics = [
            model.Topic.as_unique(session, cid=x.cid, topic="%s" % x.topic)
            for x in uitopics
        ]

        ormcalc.uitopics.extend(uitopics)

        if calc._calcset: session.add(ormcalc)
        else: session.add_all([codefamily, codeversion, pot, ormcalc])

        session.commit()
        del calc, ormcalc
        return checksum, None
Example #35
0
    def __init__(self, tilde_obj, accuracy=None):
        self.i = None
        self.n = None
        self.symmetry = None
        self.pg = None
        self.dg = None

        SymmetryFinder.__init__(self, accuracy)
        SymmetryFinder.get_spacegroup(self, tilde_obj)

        # Data below are taken from Table 2.3 of the book
        # Robert A. Evarestov, Quantum Chemistry of Solids,
        # LCAO Treatment of Crystals and Nanostructures, 2nd Edition,
        # Springer, 2012, http://dx.doi.org/10.1007/978-3-642-30356-2
        # NB 7 crystal systems != 7 lattice systems

        # space group to crystal system conversion
        if   195 <= self.n <= 230: self.symmetry = 'cubic'
        elif 168 <= self.n <= 194: self.symmetry = 'hexagonal'
        elif 143 <= self.n <= 167: self.symmetry = 'trigonal'
        elif 75  <= self.n <= 142: self.symmetry = 'tetragonal'
        elif 16  <= self.n <= 74:  self.symmetry = 'orthorhombic'
        elif 3   <= self.n <= 15:  self.symmetry = 'monoclinic'
        elif 1   <= self.n <= 2:   self.symmetry = 'triclinic'

        # space group to point group conversion
        if   221 <= self.n <= 230: self.pg = 'O<sub>h</sub>'
        elif 215 <= self.n <= 220: self.pg = 'T<sub>d</sub>'
        elif 207 <= self.n <= 214: self.pg = 'O'
        elif 200 <= self.n <= 206: self.pg = 'T<sub>h</sub>'
        elif 195 <= self.n <= 199: self.pg = 'T'
        elif 191 <= self.n <= 194: self.pg = 'D<sub>6h</sub>'
        elif 187 <= self.n <= 190: self.pg = 'D<sub>3h</sub>'
        elif 183 <= self.n <= 186: self.pg = 'C<sub>6v</sub>'
        elif 177 <= self.n <= 182: self.pg = 'D<sub>6</sub>'
        elif 175 <= self.n <= 176: self.pg = 'C<sub>6h</sub>'
        elif        self.n == 174: self.pg = 'C<sub>3h</sub>'
        elif 168 <= self.n <= 173: self.pg = 'C<sub>6</sub>'
        elif 162 <= self.n <= 167: self.pg = 'D<sub>3d</sub>'
        elif 156 <= self.n <= 161: self.pg = 'C<sub>3v</sub>'
        elif 149 <= self.n <= 155: self.pg = 'D<sub>3</sub>'
        elif 147 <= self.n <= 148: self.pg = 'C<sub>3i</sub>'
        elif 143 <= self.n <= 146: self.pg = 'C<sub>3</sub>'
        elif 123 <= self.n <= 142: self.pg = 'D<sub>4h</sub>'
        elif 111 <= self.n <= 122: self.pg = 'D<sub>2d</sub>'
        elif 99 <= self.n <= 110:  self.pg = 'C<sub>4v</sub>'
        elif 89 <= self.n <= 98:   self.pg = 'D<sub>4</sub>'
        elif 83 <= self.n <= 88:   self.pg = 'C<sub>4h</sub>'
        elif 81 <= self.n <= 82:   self.pg = 'S<sub>4</sub>'
        elif 75 <= self.n <= 80:   self.pg = 'C<sub>4</sub>'
        elif 47 <= self.n <= 74:   self.pg = 'D<sub>2h</sub>'
        elif 25 <= self.n <= 46:   self.pg = 'C<sub>2v</sub>'
        elif 16 <= self.n <= 24:   self.pg = 'D<sub>2</sub>'
        elif 10 <= self.n <= 15:   self.pg = 'C<sub>2h</sub>'
        elif 6 <= self.n <= 9:     self.pg = 'C<sub>s</sub>'
        elif 3 <= self.n <= 5:     self.pg = 'C<sub>2</sub>'
        elif self.n == 2:          self.pg = 'C<sub>i</sub>'
        elif self.n == 1:          self.pg = 'C<sub>1</sub>'

        # space group to layer group conversion
        if tilde_obj.structures[-1].periodicity == 2:
            if self.n in [25, 26, 28, 51]:
                tilde_obj.warning('Warning! Diperiodical group setting is undefined!')
            DIPERIODIC_MAPPING = {3:8, 4:9, 5:10, 6:11, 7:12, 8:13, 10:14, 11:15, 12:16, 13:17, 14:18, 16:19, 17:20, 18:21, 21:22, 25:23, 25:24, 26:25, 26:26, 27:27, 28:28, 28:29, 29:30, 30:31, 31:32, 32:33, 35:34, 38:35, 39:36, 47:37, 49:38, 50:39, 51:40, 51:41, 53:42, 54:43, 55:44, 57:45, 59:46, 65:47, 67:48, 75:49, 81:50, 83:51, 85:52, 89:53, 90:54, 99:55, 100:56, 111:57, 113:58, 115:59, 117:60, 123:61, 125:62, 127:63, 129:64, 143:65, 147:66, 149:67, 150:68, 156:69, 157:70, 162:71, 164:72, 168:73, 174:74, 175:75, 177:76, 183:77, 187:78, 189:79, 191:80}

            cellpar = cell_to_cellpar( tilde_obj.structures[-1].cell ).tolist()
            if cellpar[3] != 90 or cellpar[4] != 90 or cellpar[5] != 90:
                DIPERIODIC_MAPPING.update({1:1, 2:2, 3:3, 6:4, 7:5, 10:6, 13:7})
            try: self.dg = DIPERIODIC_MAPPING[self.n]
            except KeyError: tilde_obj.warning('No diperiodical group found because rotational axes inconsistent with 2d translations!')
            else:
                if   65 <= self.dg <= 80: self.symmetry = '2d-hexagonal'
                elif 49 <= self.dg <= 64: self.symmetry = '2d-square'
                elif 8  <= self.dg <= 48: self.symmetry = '2d-rectangular'
                elif 1  <= self.dg <= 7:  self.symmetry = '2d-oblique'
Example #36
0
    def save(self, calc, session):
        '''
        Saves tilde_obj into the database
        NB: this is the PUBLIC method
        @returns checksum, error
        '''
        checksum = calc.get_checksum()

        try:
            existing_calc = session.query(model.Calculation).filter(model.Calculation.checksum == checksum).one()
        except NoResultFound:
            pass
        else:
            del calc
            return None, "This calculation already exists!"

        if not calc.download_size:
            for f in calc.related_files:
                calc.download_size += os.stat(f).st_size

        ormcalc = model.Calculation(checksum = checksum)

        if calc._calcset:
            ormcalc.meta_data = model.Metadata(chemical_formula = calc.info['standard'], download_size = calc.download_size)

            for child in session.query(model.Calculation).filter(model.Calculation.checksum.in_(calc._calcset)).all():
                ormcalc.children.append(child)
            ormcalc.siblings_count = len(ormcalc.children)
            ormcalc.nested_depth = calc._nested_depth

        else:
            # prepare phonon data for saving
            # this is actually a dict to list conversion TODO re-structure this
            if calc.phonons['modes']:
                phonons_json = []

                for bzpoint, frqset in calc.phonons['modes'].items():
                    # re-orientate eigenvectors
                    for i in range(0, len(calc.phonons['ph_eigvecs'][bzpoint])):
                        for j in range(0, len(calc.phonons['ph_eigvecs'][bzpoint][i])//3):
                            eigv = array([calc.phonons['ph_eigvecs'][bzpoint][i][j*3], calc.phonons['ph_eigvecs'][bzpoint][i][j*3+1], calc.phonons['ph_eigvecs'][bzpoint][i][j*3+2]])
                            R = dot( eigv, calc.structures[-1].cell ).tolist()
                            calc.phonons['ph_eigvecs'][bzpoint][i][j*3], calc.phonons['ph_eigvecs'][bzpoint][i][j*3+1], calc.phonons['ph_eigvecs'][bzpoint][i][j*3+2] = [round(x, 3) for x in R]

                    try: irreps = calc.phonons['irreps'][bzpoint]
                    except KeyError:
                        empty = []
                        for i in range(len(frqset)):
                            empty.append('')
                        irreps = empty

                    phonons_json.append({  'bzpoint':bzpoint, 'freqs':frqset, 'irreps':irreps, 'ph_eigvecs':calc.phonons['ph_eigvecs'][bzpoint]  })
                    if bzpoint == '0 0 0':
                        phonons_json[-1]['ir_active'] = calc.phonons['ir_active']
                        phonons_json[-1]['raman_active'] = calc.phonons['raman_active']
                    if calc.phonons['ph_k_degeneracy']:
                        phonons_json[-1]['ph_k_degeneracy'] = calc.phonons['ph_k_degeneracy'][bzpoint]

                ormcalc.phonons = model.Phonons()
                ormcalc.spectra.append( model.Spectra(kind = model.Spectra.PHONON, eigenvalues = json.dumps(phonons_json)) )

            # prepare electron data for saving TODO re-structure this
            for task in ['dos', 'bands']: # projected?
                if calc.electrons[task]:
                    calc.electrons[task] = calc.electrons[task].todict()

            if calc.electrons['dos'] or calc.electrons['bands']:
                ormcalc.electrons = model.Electrons(gap = calc.info['bandgap'])
                if 'bandgaptype' in calc.info:
                    ormcalc.electrons.is_direct = 1 if calc.info['bandgaptype'] == 'direct' else -1
                ormcalc.spectra.append(model.Spectra(
                    kind = model.Spectra.ELECTRON,
                    dos = json.dumps(calc.electrons['dos']),
                    bands = json.dumps(calc.electrons['bands']),
                    projected = json.dumps(calc.electrons['projected']),
                    eigenvalues = json.dumps(calc.electrons['eigvals'])
                ))

            # construct ORM for other props
            calc.related_files = list(map(virtualize_path, calc.related_files))
            ormcalc.meta_data = model.Metadata(location = calc.info['location'], finished = calc.info['finished'], raw_input = calc.info['input'], modeling_time = calc.info['duration'], chemical_formula = html_formula(calc.info['standard']), download_size = calc.download_size, filenames = json.dumps(calc.related_files))

            codefamily = model.Codefamily.as_unique(session, content = calc.info['framework'])
            codeversion = model.Codeversion.as_unique(session, content = calc.info['prog'])

            codeversion.instances.append( ormcalc.meta_data )
            codefamily.versions.append( codeversion )

            pot = model.Pottype.as_unique(session, name = calc.info['H'])
            pot.instances.append(ormcalc)
            ormcalc.recipinteg = model.Recipinteg(kgrid = calc.info['k'], kshift = calc.info['kshift'], smearing = calc.info['smear'], smeartype = calc.info['smeartype'])
            ormcalc.basis = model.Basis(kind = calc.info['ansatz'], content = json.dumps(calc.electrons['basis_set']) if calc.electrons['basis_set'] else None)
            ormcalc.energy = model.Energy(convergence = json.dumps(calc.convergence), total = calc.info['energy'])

            ormcalc.spacegroup = model.Spacegroup(n=calc.info['ng'])
            ormcalc.struct_ratios = model.Struct_ratios(chemical_formula=calc.info['standard'], formula_units=calc.info['expanded'], nelem=calc.info['nelem'], dimensions=calc.info['dims'])
            if len(calc.tresholds) > 1:
                ormcalc.struct_optimisation = model.Struct_optimisation(tresholds=json.dumps(calc.tresholds), ncycles=json.dumps(calc.ncycles))

            for n, ase_repr in enumerate(calc.structures):
                is_final = True if n == len(calc.structures)-1 else False
                struct = model.Structure(step = n, final = is_final)

                s = cell_to_cellpar(ase_repr.cell)
                struct.lattice = model.Lattice(a=s[0], b=s[1], c=s[2], alpha=s[3], beta=s[4], gamma=s[5], a11=ase_repr.cell[0][0], a12=ase_repr.cell[0][1], a13=ase_repr.cell[0][2], a21=ase_repr.cell[1][0], a22=ase_repr.cell[1][1], a23=ase_repr.cell[1][2], a31=ase_repr.cell[2][0], a32=ase_repr.cell[2][1], a33=ase_repr.cell[2][2])

                #rmts =      ase_repr.get_array('rmts') if 'rmts' in ase_repr.arrays else [None for j in range(len(ase_repr))]
                charges =   ase_repr.get_array('charges') if 'charges' in ase_repr.arrays else [None for j in range(len(ase_repr))]
                magmoms =   ase_repr.get_array('magmoms') if 'magmoms' in ase_repr.arrays else [None for j in range(len(ase_repr))]
                for n, i in enumerate(ase_repr):
                    struct.atoms.append( model.Atom( number=chemical_symbols.index(i.symbol), x=i.x, y=i.y, z=i.z, charge=charges[n], magmom=magmoms[n] ) )

                ormcalc.structures.append(struct)
            # TODO Forces

        ormcalc.uigrid = model.Grid(info=json.dumps(calc.info))

        # tags ORM
        uitopics = []
        for entity in self.hierarchy:

            if not entity['creates_topic']:
                continue

            if entity['multiple'] or calc._calcset:
                for item in calc.info.get( entity['source'], [] ):
                    uitopics.append( model.topic(cid=entity['cid'], topic=item) )
            else:
                topic = calc.info.get(entity['source'])
                if topic or not entity['optional']:
                    uitopics.append( model.topic(cid=entity['cid'], topic=topic) )

        uitopics = [model.Topic.as_unique(session, cid=x.cid, topic="%s" % x.topic) for x in uitopics]

        ormcalc.uitopics.extend(uitopics)

        if calc._calcset:
            session.add(ormcalc)
        else:
            session.add_all([codefamily, codeversion, pot, ormcalc])

        session.commit()
        del calc, ormcalc
        return checksum, None
Example #37
0
def get_cellinfo(cell, lattice=None, eps=2e-4):
    from ase.build.tools import niggli_reduce_cell
    rcell, M = niggli_reduce_cell(cell)
    latt = crystal_structure_from_cell(rcell, niggli_reduce=False)
    if lattice:
        assert latt == lattice.lower(), latt

    if latt == 'monoclinic':
        # Transform From Niggli to Setyawana-Curtarolo cell:
        a, b, c, alpha, beta, gamma = cell_to_cellpar(rcell, radians=True)
        if abs(beta - np.pi / 2) > eps:
            T = np.array([[0, 1, 0], [-1, 0, 0], [0, 0, 1]])
            scell = np.dot(T, rcell)
        elif abs(gamma - np.pi / 2) > eps:
            T = np.array([[0, 0, 1], [1, 0, 0], [0, -1, 0]])
        else:
            raise ValueError('You are using a badly oriented ' +
                             'monoclinic unit cell. Please choose one with ' +
                             'either beta or gamma != pi/2')

        scell = np.dot(np.dot(T, rcell), T.T)
        a, b, c, alpha, beta, gamma = cell_to_cellpar(scell, radians=True)

        assert alpha < np.pi / 2, 'Your monoclinic angle has to be < pi / 2'

        M = np.dot(M, T.T)
        eta = (1 - b * cos(alpha) / c) / (2 * sin(alpha)**2)
        nu = 1 / 2 - eta * c * cos(alpha) / b
        points = {
            'G': [0, 0, 0],
            'A': [1 / 2, 1 / 2, 0],
            'C': [0, 1 / 2, 1 / 2],
            'D': [1 / 2, 0, 1 / 2],
            'D1': [1 / 2, 0, -1 / 2],
            'E': [1 / 2, 1 / 2, 1 / 2],
            'H': [0, eta, 1 - nu],
            'H1': [0, 1 - eta, nu],
            'H2': [0, eta, -nu],
            'M': [1 / 2, eta, 1 - nu],
            'M1': [1 / 2, 1 - eta, nu],
            'M2': [1 / 2, eta, -nu],
            'X': [0, 1 / 2, 0],
            'Y': [0, 0, 1 / 2],
            'Y1': [0, 0, -1 / 2],
            'Z': [1 / 2, 0, 0]
        }
    elif latt == 'rhombohedral type 1':
        a, b, c, alpha, beta, gamma = cell_to_cellpar(cell=cell, radians=True)
        eta = (1 + 4 * np.cos(alpha)) / (2 + 4 * np.cos(alpha))
        nu = 3 / 4 - eta / 2
        points = {
            'G': [0, 0, 0],
            'B': [eta, 1 / 2, 1 - eta],
            'B1': [1 / 2, 1 - eta, eta - 1],
            'F': [1 / 2, 1 / 2, 0],
            'L': [1 / 2, 0, 0],
            'L1': [0, 0, -1 / 2],
            'P': [eta, nu, nu],
            'P1': [1 - nu, 1 - nu, 1 - eta],
            'P2': [nu, nu, eta - 1],
            'Q': [1 - nu, nu, 0],
            'X': [nu, 0, -nu],
            'Z': [0.5, 0.5, 0.5]
        }
    else:
        points = ibz_points[latt]

    myspecial_points = {label: np.dot(M, kpt) for label, kpt in points.items()}
    return CellInfo(rcell=rcell, lattice=latt, special_points=myspecial_points)
Example #38
0
    def classify(self, calc, symprec=None):
        '''
        Reasons on normalization, invokes hierarchy API and prepares calc for saving
        NB: this is the PUBLIC method
        @returns tilde_obj, error
        '''
        error = None
        symbols = calc.structures[-1].get_chemical_symbols()
        calc.info['formula'] = self.formula(symbols)
        calc.info['cellpar'] = cell_to_cellpar(calc.structures[-1].cell).tolist()
        if calc.info['input']:
            try:
                calc.info['input'] = str(calc.info['input'], errors='ignore')
            except:
                pass

        # applying filter: todo
        if (calc.info['finished'] == 0x1 and self.settings['skip_unfinished']) or \
           (not calc.info['energy'] and self.settings['skip_notenergy']):
            return None, 'data do not satisfy the active filter'

        # naive elements extraction
        fragments = re.findall(r'([A-Z][a-z]?)(\d*[?:.\d+]*)?', calc.info['formula'])
        for fragment in fragments:
            if fragment[0] == 'X':
                continue
            calc.info['elements'].append(fragment[0])
            calc.info['contents'].append(int(fragment[1])) if fragment[1] else calc.info['contents'].append(1)

        # extend hierarchy with modules
        for C_obj in self.Classifiers:
            try:
                calc = C_obj['classify'](calc)
            except:
                exc_type, exc_value, exc_tb = sys.exc_info()
                error = "Fatal error during classification:\n %s" % "".join(traceback.format_exception( exc_type, exc_value, exc_tb ))
                return None, error

        # chemical ratios
        if not len(calc.info['standard']):
            if len(calc.info['elements']) == 1: calc.info['expanded'] = 1
            if not calc.info['expanded']:
                calc.info['expanded'] = reduce(gcd, calc.info['contents'])
            for n, i in enumerate([x//calc.info['expanded'] for x in calc.info['contents']]):
                if i == 1:
                    calc.info['standard'] += calc.info['elements'][n]
                else:
                    calc.info['standard'] += calc.info['elements'][n] + str(i)
        if not calc.info['expanded']:
            del calc.info['expanded']

        calc.info['nelem'] = len(calc.info['elements'])
        if calc.info['nelem'] > 13:
            calc.info['nelem'] = 13
        calc.info['natom'] = len(symbols)

        # periodicity
        if calc.info['periodicity'] == 0:
            calc.info['periodicity'] = 0x4
        elif calc.info['periodicity'] == -1:
            calc.info['periodicity'] = 0x5

        # general calculation type reasoning
        if (calc.structures[-1].get_initial_charges() != 0).sum():
            calc.info['calctypes'].append(0x4) # numpy count_nonzero implementation
        if (calc.structures[-1].get_initial_magnetic_moments() != 0).sum():
            calc.info['calctypes'].append(0x5)
        if calc.phonons['modes']:
            calc.info['calctypes'].append(0x6)
        if calc.phonons['ph_k_degeneracy']:
            calc.info['calctypes'].append(0x7)
        if calc.phonons['dielectric_tensor']:
            calc.info['calctypes'].append(0x8) # CRYSTAL-only!
        if len(calc.tresholds) > 1:
            calc.info['calctypes'].append(0x3)
            calc.info['optgeom'] = True
        if calc.electrons['dos'] or calc.electrons['bands']:
            calc.info['calctypes'].append(0x2)
        if calc.info['energy']:
            calc.info['calctypes'].append(0x1)
        calc.info['spin'] = 0x2 if calc.info['spin'] else 0x1

        # TODO: standardize
        if 'vac' in calc.info:
            if 'X' in symbols:
                calc.info['techs'].append('vacancy defect: ghost')
            else:
                calc.info['techs'].append('vacancy defect: void space')

        calc.info['lata'] = round(calc.info['cellpar'][0], 3)
        calc.info['latb'] = round(calc.info['cellpar'][1], 3)
        calc.info['latc'] = round(calc.info['cellpar'][2], 3)
        calc.info['latalpha'] = round(calc.info['cellpar'][3], 2)
        calc.info['latbeta'] = round(calc.info['cellpar'][4], 2)
        calc.info['latgamma'] = round(calc.info['cellpar'][5], 2)

        # invoke symmetry finder
        found = SymmetryHandler(calc, symprec)
        if found.error:
            return None, found.error

        calc.info['sg'] = found.i
        calc.info['ng'] = found.n
        calc.info['symmetry'] = found.symmetry
        calc.info['spg'] = "%s &mdash; %s" % (found.n, found.i)
        calc.info['pg'] = found.pg
        calc.info['dg'] = found.dg

        # phonons
        if calc.phonons['dfp_magnitude']: calc.info['dfp_magnitude'] = round(calc.phonons['dfp_magnitude'], 3)
        if calc.phonons['dfp_disps']: calc.info['dfp_disps'] = len(calc.phonons['dfp_disps'])
        if calc.phonons['modes']:
            calc.info['n_ph_k'] = len(calc.phonons['ph_k_degeneracy']) if calc.phonons['ph_k_degeneracy'] else 1

        #calc.info['rgkmax'] = calc.electrons['rgkmax'] # LAPW

        # electronic properties reasoning by bands
        if calc.electrons['bands']:
            if calc.electrons['bands'].is_conductor():
                calc.info['etype'] = 0x2
                calc.info['bandgap'] = 0.0
                calc.info['bandgaptype'] = 0x1
            else:
                try:
                    gap, is_direct = calc.electrons['bands'].get_bandgap()
                except ElectronStructureError as e:
                    calc.electrons['bands'] = None
                    calc.warning(e.value)
                else:
                    calc.info['etype'] = 0x1
                    calc.info['bandgap'] = round(gap, 2)
                    calc.info['bandgaptype'] = 0x2 if is_direct else 0x3

        # electronic properties reasoning by DOS
        if calc.electrons['dos']:
            try: gap = round(calc.electrons['dos'].get_bandgap(), 2)
            except ElectronStructureError as e:
                calc.electrons['dos'] = None
                calc.warning(e.value)
            else:
                if calc.electrons['bands']: # check coincidence
                    if abs(calc.info['bandgap'] - gap) > 0.2:
                        calc.warning('Bans gaps in DOS and bands data differ considerably! The latter will be considered.')
                else:
                    calc.info['bandgap'] = gap
                    if gap:
                        calc.info['etype'] = 0x1
                    else:
                        calc.info['etype'] = 0x2
                        calc.info['bandgaptype'] = 0x1

        # TODO: beware to add something new to an existing item!
        # TODO2: unknown or absent?
        for entity in self.hierarchy:
            if entity['creates_topic'] and not entity['optional'] and not calc.info.get(entity['source']):
                if entity['enumerated']:
                    calc.info[ entity['source'] ] = [0x0] if entity['multiple'] else 0x0
                else:
                    calc.info[ entity['source'] ] = ['none'] if entity['multiple'] else 'none'

        calc.benchmark() # this call must be at the very end of parsing

        return calc, error
Example #39
0
def diffMap2(input_filename,
             Ro,
             b,
             co,
             dx,
             dy,
             dz,
             output_filename,
             anion='O',
             anion_number=8):

    # Read in the CIF file
    a = ase.io.read("{}".format(input_filename))

    # Take only the anion positions and unit cell vectors
    a = ase.Atoms([anion for i in range(len(a[a.numbers == anion_number]))],
                  cell=a.cell,
                  positions=a.positions[a.numbers == anion_number],
                  pbc=True)

    # Make a mesh over the resolution defined by dx,dy,dz
    x, y, z = np.mgrid[0:1 + dx:dx, 0:1 + dy:dy, 0:1 + dz:dz]
    r_scaled = np.stack([x, y, z])  # Cartesian coordinates of each voxel

    # Transform the unit cell to access each voxel by real lengths
    r = np.dot(r_scaled.reshape((3, r_scaled.shape[1]**3)).T,
               a.cell).T.reshape(r_scaled.shape)

    # Make an empty array to calculate the Valence over
    V = np.ones(x.shape)

    # Define oxygen positions
    O = a.get_positions()
    # Add coordinates of all anions in adjacent cells
    # (important if the interaction length is larger than the unit cell)
    permutations = [[-1, 0, 1] for i in range(3)]
    for i in itr.product(*permutations):
        if i != (0, 0, 0):
            shift = a.cell[0] * i[0] + a.cell[1] * i[1] + a.cell[2] * i[2]
            a.translate(shift)
            O = np.concatenate((O, a.get_positions()), axis=0)
            a.translate(-shift)

    # optionally, time this section because it is the time critical step
    start_time = time.time()

    # Iterate through each volume element in the unit cell
    lens = [range(r.shape[1]), range(r.shape[2]), range(r.shape[3])]
    for i in itr.product(*lens):

        # define upper and lower bound for anion coordinates as deffined by the cuttoff radii (co)
        top = r[:, i[0], i[1], i[2]] + co
        bottom = r[:, i[0], i[1], i[2]] - co

        # Store all oxygen within a box around the sphere defined by the cuttoff radii
        O2 = O[np.all(((O < top) == (O > bottom)), axis=1)]

        # Calculate the distance to each anion in the box
        Ri = np.array([
            np.sqrt((r[0, i[0], i[1], i[2]] -
                     O2[k, 0])**2 +  # This way worked faster
                    (r[1, i[0], i[1], i[2]] -
                     O2[k, 1])**2 +  # than with linalg.norm()
                    (r[2, i[0], i[1], i[2]] - O2[k, 2])**2)
            for k in range(len(O2[:, 0]))
        ])

        # Calculate the valence sum and apply the cutoff
        V[i[0], i[1],
          i[2]] = np.abs(np.sum(np.exp((Ro - Ri[Ri < co]) / b)) - 1)

    # Save the information
    with open('{0}.grd'.format(output_filename), "w") as savefile:

        savefile.write("Bond Valence Sum Difference\r")  # Title
        from ase.geometry import cell_to_cellpar
        cellParams = cell_to_cellpar(a.cell)  # get ABC alpha, beta, gamma
        savefile.write(" ".join([str(k) for k in cellParams]) + "\r")
        savefile.write(" ".join([str(k) for k in V.shape]) + "\r")
        for i in np.nditer(V.flatten()):
            savefile.write("%.6f  " %
                           (i))  # Write each valence difference value

    #print "Total time taken = %.4f s" % (time.time()-start_time)
    return V
Example #40
0
File: plot.py Project: wangvei/TB2J
def fix_cell(cell,eps=5e-3):
    cellpar=cell_to_cellpar(cell)
    for i in [3,4,5]:
        if abs(cellpar[i]/90.0*np.pi/2-np.pi/2)<eps:
            cellpar[i]=90.0
    return cellpar_to_cell(cellpar)
Example #41
0
def split_layer(atoms, thr=0.03, direction=2, sort=True, return_pos=False):
    """
    split atoms into layers
    Parameters:
      thr: max distance in direction from atoms in same layer.
      direction: 0 | 1| 2
      sort: whether to sort the layers by posiontion.
      return_pos: whether to return the positions of each atom.
    Returns:
       A list of symnum lists, each is the symnum of a layer.
       [['Ni1','O2'],['']]
    """
    for i in [0, 1, 2]:
        if i != direction:
            if atoms.get_cell()[direction][i] > 0.5:
                raise NotImplementedError(
                    "cellparameters should be orthgonal in the direction")
    atoms = force_near_0(atoms)
    z = cell_to_cellpar(atoms.get_cell())[direction]
    positions = [pos[direction] for pos in atoms.get_positions()]

    def is_near(pos1, pos2):
        if abs(pos1 - pos2) % z < thr or z - abs(pos1 - pos2) % z < thr:
            return True
        else:
            return False

    symdict = symbol_number(atoms)

    layer_indexes = []
    layer_poses = []
    layer_symnums = []
    for i, pos in enumerate(positions):
        got_layer = False
        if layer_indexes == []:
            layer_indexes.append([i])
            layer_poses.append([pos])
            layer_symnums.append([list(symdict.keys())[i]])
            continue

        for ind, layer_ind_pos in enumerate(zip(layer_indexes, layer_poses)):

            lp = np.average(layer_ind_pos[1])
            #print "ind_pos",layer_ind_pos[1]
            if is_near(pos, lp):
                print("got: %s" % ind, pos, lp)
                got_layer = True
                index = ind
                break
        if got_layer:
            layer_indexes[index].append(i)
            layer_poses[index].append(pos)
            layer_symnums[index].append(list(symdict.keys())[i])
        else:
            layer_indexes.append([i])
            layer_poses.append([pos])
            layer_symnums.append([list(symdict.keys())[i]])
    if sort:
        sort_i = sorted(list(range(len(layer_poses))),
                        key=layer_poses.__getitem__)
        layer_symnums = [layer_symnums[i] for i in sort_i]
        layer_poses = [layer_poses[i] for i in sort_i]
    if return_pos:
        return layer_symnums, layer_poses
    else:
        return layer_symnums
Example #42
0
def valence_map(input_filename, anion, anion_number, Ro, b, co, dx, dy, dz,
                output_filename):

    # Read in CIF file
    a = ase.io.read("{}.cif".format(input_filename))

    #
    a = ase.Atoms([anion for i in range(len(a[a.numbers == anion_number]))],
                  cell=a.cell,
                  positions=a.positions[a.numbers == anion_number],
                  pbc=True)
    x, y, z = np.mgrid[0:1 + dx:dx, 0:1 + dy:dy, 0:1 + dz:dz]  # Make mesh
    # Here we will take the mesh and transform it to fit the unit cell in real space
    r = np.stack([x, y, z])  # Gives the cartesian coordinates of each voxel
    lens = [range(r.shape[2]),
            range(r.shape[3])]  # List to iterate over all y,z columns
    for j in itr.product(*lens):
        r[:, :, j[0],
          j[1]] = np.dot(r[:, :, j[0], j[1]].T,
                         a.cell).T  # Transform to coordinates of unit cell
    # make an empty array to hold values of the Valence difference
    V = np.ones(x.shape)

    O = a.get_positions()[a.numbers == 8]
    #print O.shape # coordinates of all anions in the unit cell
    # Add coordinates of all anions in adjacent cells
    permutations = [[-1, 0, 1] for i in range(3)]
    for i in itr.product(*permutations):
        if i != (0, 0, 0):
            shift = a.cell[0] * i[0] + a.cell[1] * i[1] + a.cell[2] * i[2]
            a.translate(shift)
            O = np.concatenate((O, a.get_positions()), axis=0)
            a.translate(-shift)

    start_time = time.time(
    )  # We're going to time this section because it's the long part
    # Iterate through each volume element (the index of which is contained in lens)
    lens = [range(r.shape[1]), range(r.shape[2]), range(r.shape[3])]
    for i in itr.product(*lens):  # iterate through each voxel in the unit cell
        top = r[:, i[0], i[1],
                i[2]] + co  # define upper and lower bound for anion coordinates
        bottom = r[:, i[0], i[1],
                   i[2]] - co  # that fall within the cutoff radii of the voxel
        # Apply cuttoff 'box' on anions to reduce the number of distance calculations
        O2 = O[np.all(((O < top) == (O > bottom)),
                      axis=1)]  # Contains anions in cutoff box
        # Calculate the distance to each anion in the box
        Ri = np.array([
            np.sqrt((r[0, i[0], i[1], i[2]] -
                     O2[k, 0])**2 +  # This way worked faster
                    (r[1, i[0], i[1], i[2]] -
                     O2[k, 1])**2 +  # than with linalg.norm()
                    (r[2, i[0], i[1], i[2]] - O2[k, 2])**2)
            for k in range(len(O2[:, 0]))
        ])
        # Calculate the valence sum and apply the cutoff
        V[i[0], i[1], i[2]] = np.sum(np.exp((Ro - Ri[Ri < co]) / b))

    savefile = open('{0}.grd'.format(output_filename), "w")  # outputfile
    savefile.write("Bond Valence Sum Difference\r")  # Title
    from ase.geometry import cell_to_cellpar
    cellParams = cell_to_cellpar(a.cell)  # get ABC alpha, beta, gamma
    savefile.write(" ".join([str(k) for k in cellParams]) + "\r")
    savefile.write(" ".join([str(k) for k in V.shape]) + "\r")
    for i in np.nditer(V.flatten()):
        savefile.write("%.6f  " % (i))  # Write each valence difference value
    savefile.close()

    return r, V