예제 #1
0
파일: tools.py 프로젝트: vpasumarthi/CatHub
def extract_atoms(molecule):
    """Return a string with all atoms in molecule"""
    if molecule == '':
        return molecule
    try:
        return float(molecule)
    except BaseException:
        pass

    sign = ''
    molecule, prefactor = get_prefactor(molecule)
    if prefactor < 0:
        sign = '-'
    prefactor = abs(prefactor)

    atoms = Atoms(molecule)
    atoms = atoms.get_chemical_formula(mode='all')
    if prefactor % 1 == 0:
        atoms *= int(prefactor)
    elif prefactor % 1 == 0.5:
        atoms_sort = sorted(atoms)
        N = len(atoms)
        atoms = ''
        for n in range(N):
            for m in range(int(prefactor - 0.5)):
                atoms += atoms_sort[n]
            if n % 2 == 0:
                atoms += atoms_sort[n]
    return sign + ''.join(sorted(atoms))
예제 #2
0
def get_all_atoms(molecule):
    molecule = clear_state(molecule)
    molecule, prefactor = get_prefactor(molecule)

    atoms = Atoms(molecule)

    molecule = ''.join(sorted(atoms.get_chemical_formula(mode='all')))

    return molecule, prefactor
예제 #3
0
def test_formula():
    for sym in ['', 'Pu', 'Pu2', 'U2Pu2', 'U2((Pu2)2H)']:
        for mode in ['all', 'reduce', 'hill', 'metal']:
            for empirical in [False, True]:
                if empirical and mode in ['all', 'reduce']:
                    continue
                atoms = Atoms(sym)
                formula = atoms.get_chemical_formula(mode=mode,
                                                     empirical=empirical)
                atoms2 = Atoms(formula)
                print(repr(sym), '->', repr(formula))
                n1 = np.sort(atoms.numbers)
                n2 = np.sort(atoms2.numbers)
                if empirical and len(atoms) > 0:
                    reduction = len(n1) // len(n2)
                    n2 = np.repeat(n2, reduction)
                assert (n1 == n2).all()
예제 #4
0
def test_isolation_1D():
    atoms = Atoms(symbols='Cl6Ti2',
                  pbc=True,
                  cell=[[6.27, 0, 0], [-3.135, 5.43, 0], [0, 0, 5.82]],
                  positions=[[1.97505, 0, 1.455],
                             [0.987525, 1.71044347, 4.365],
                             [-0.987525, 1.71044347,
                              1.455], [4.29495, 0, 4.365],
                             [2.147475, 3.71953581, 1.455],
                             [-2.147475, 3.71953581, 4.365], [0, 0, 0],
                             [0, 0, 2.91]])

    result = isolate_components(atoms)
    assert len(result) == 1
    key, components = list(result.items())[0]
    assert key == '1D'
    assert len(components) == 1
    chain = components[0]
    assert (chain.pbc == [False, False, True]).all()
    assert chain.get_chemical_formula() == atoms.get_chemical_formula()
예제 #5
0
def redundancy_check(blank_coords, adsonly_positions, fp_dict,
                     repeat_unit_cell, symmetry_tol):

    inv_ruc = np.linalg.inv(repeat_unit_cell)

    atoms = Atoms()
    for c in blank_coords:
        atoms.append(Atom(c[0], c[1]))

    formula = atoms.get_chemical_formula()
    atoms.set_cell(repeat_unit_cell.T)
    atoms.pbc = True
    struct = AseAtomsAdaptor.get_structure(atoms)
    sga = SpacegroupAnalyzer(struct, symprec=symmetry_tol)
    sgs = sga.get_space_group_symbol()
    sgn = sga.get_space_group_number()

    adsonly_positions = np.asarray(adsonly_positions)

    dists = []
    for i in range(len(adsonly_positions)):
        icoord = np.dot(inv_ruc, adsonly_positions[i])
        for j in range(i + 1, len(adsonly_positions)):
            jcoord = np.dot(inv_ruc, adsonly_positions[j])
            fdist, sym = PBC3DF_sym(icoord, jcoord)
            dist = np.linalg.norm(np.dot(repeat_unit_cell, fdist))
            dists.append(dist)
    dists.sort()

    index = 0
    advance = True
    for ind in range(len(fp_dict[sgn])):
        fp = fp_dict[sgn][ind]
        if len(fp) == len(dists):
            if np.allclose(fp, dists):
                index = ind
                advance = False
                break
    index = str(index)

    return advance, index, sgs, sgn, dists, atoms, formula
예제 #6
0
def atomic_structure_generator(symbols, fu=None, ndensity=None, volume=None,
                               mindis=None, nstr=None, maxatomn=None,
                               cspd_file=None, lw=None, format=None, clean=None,
                               sgn=None, to_primitive=None):
    """
    This function will read crystal structure prototypes from CSPD.db file
    and return a list of ASE Atoms object for the symbols defined by users.
    It could also write the output structures into any file format supported
    by ASE.

    The symbols is the only parameter required to define. The function has
    very justified default values for the rest of the parameters. You could
    also customize them according to your own understanding of your system.

    :param symbols: str (formula) or list of str
        Can be a string formula, a list of symbols or a list of
        Atom objects.  Examples: 'H2O', 'COPt12', ['H', 'H', 'O'],
        [Atom('Ne', (x, y, z)), ...]. Same as the symbols in ASE Atoms class
        (https://wiki.fysik.dtu.dk/ase/ase/atoms.html). This parameter is
        passed to the Atoms class.
    :param fu: list of int
        Range of formula unit. The symbols is multiplied by every formula
        unit in this range. The length of this list has to be 2.
    :param ndensity: float
        Total number of atoms divided by volume. It controls how dense
        the atoms stack.
    :param volume: float
        Average volume of the structure per symbols. If ndensity is
        defined, volume will be ignored. I would strongly recommend to use
        ndensity rather than volume.
    :param mindis: list of lists of float
        Minimum inter-atomic distances. The dimension is nxn for the
        structure has number of n type of element. For example, we could
        define it as [[1.7, 1.4],[1.4, 1.2]] for binary compound.
        mindis[0][1] defines the minimum distance between element 1 with
        element 2.
    :param nstr: int
        This number of structures will be returned. Less of structures might
        be returned when there's not enough qualified structures in the
        database.
    :param maxatomn: int
        Maximum number of atoms in the structure.
    :param cspd_file: str
        Path and file name of CSPD.db.
    :param lw: logical
        Whether to write the structures into files. The structures are put in
        structure_folder.
    :param format: str
        Used to specify the file-format. Same as the format in ase.io.write()
        function. Check out the supported file-format at their website
        (https://wiki.fysik.dtu.dk/ase/ase/io/io.html).
    :param sgn: list of int
        Range of space group sequential number as given in the International
        Tables for Crystallography.
    :return: list of ASE Atoms object
    """
    random.seed(a=27173)
    strulist = []
    newstrulist = []
    locmindis = {}
    rdr = 0.61
    scale_ndensity = 2.22
    structure_folder = 'structure_folder'
    lwf = False

    if fu == None:
        fu = [2, 8]
    if nstr == None:
        nstr = 1600
    if maxatomn == None:
        maxatomn = 60
    if cspd_file == None:
        cspd_file = '~/CSPD.db'
    if lw == None:
        lw = False
    if format == None:
        format = 'cif'
    if clean == None:
        clean = True
    if sgn == None:
        sgn = [1, 230]
    if to_primitive == None:
        to_primitive = False

    tmpstru = Atoms(symbols)
    intctype = count_atoms(tmpstru.numbers)
    atomnn = unify_an(tmpstru.numbers)
    nele, strctype, gcd = nele_ctype_fu(intctype)
    # intctype=[int(float(i)/gcd+0.5) for i in intctype]
    fulist = [i * gcd for i in range(fu[0], fu[1] + 1)]
    if format == 'db':
        dbw = connect(structure_folder + '/' + tmpstru.get_chemical_formula() + '.db', append=False)
    if volume:
        volume = float(volume)
    if ndensity:
        ndensity = float(ndensity)
    if mindis:
        for i, an in enumerate(atomnn.keys()):
            for j, an2 in enumerate(atomnn.keys()):
                locmindis[str(an) + '_' + str(an2)] = mindis[i][j]
    elif not (ndensity or volume):
        for an in atomnn.keys():
            for an2 in atomnn.keys():
                locmindis[str(an) + '_' + str(an2)] = (covalent_radii[an] + covalent_radii[an2]) * rdr
    if not ndensity:
        if volume:
            ndensity = len(tmpstru.numbers) / volume
        elif mindis:
            ndensity = scale_ndensity * sum(atomnn.values()) / (4 / 3.0 * math.pi / ((2 * rdr) ** 3) / 0.34 * sum(
                [atomnn[sym] * mindis[i][i] ** 3 for i, sym in enumerate(atomnn.keys())]))
        else:
            ndensity = scale_ndensity * sum(atomnn.values()) / (
                    4 / 3.0 * math.pi / 0.34 * sum([atomnn[sym] * covalent_radii[sym] ** 3 for sym in atomnn.keys()]))
    if (ndensity or volume) and (not mindis):
        for an in atomnn.keys():
            for an2 in atomnn.keys():
                locmindis[str(an) + '_' + str(an2)] = (covalent_radii[an] + covalent_radii[an2]) * rdr
        # Need to improve!!! calculate locmindis according to ndensity

    db = connect(cspd_file)
    for row in db.select('ctype=_' + strctype):
        if row.lfocp and sgn[0] <= row.sgn <= sgn[1]:
            tempstru = row.toatoms()
            if to_primitive:
                pcell = standardize_cell(
                    (tempstru.cell, tempstru.get_scaled_positions(), tempstru.numbers),
                    to_primitive=True, symprec=0.01)
                # sgsn=get_spacegroup((strulist[struid].cell,strulist[struid].get_scaled_positions(),strulist[struid].numbers),symprec=0.01)
                # sgsn2=get_spacegroup(pcell,symprec=0.01)
                if pcell:
                    tempstru = Atoms(cell=pcell[0], scaled_positions=pcell[1], numbers=pcell[2], pbc=True)
            natoms = len(tempstru.numbers)
            if natoms <= maxatomn:
                if to_primitive:
                    final_fu = int(row.fu / row.natoms * natoms + 0.5)
                else:
                    final_fu = row.fu
                for i in fulist:
                    if final_fu == i:
                        strulist.append(tempstru)
                        strulist[-1].sgn = row.sgn
                        strulist[-1].dname = row.dname
                        strulist[-1].oid = row.oid
                        # Or construct a list of rows and convert part of them to atoms object.
                        break
    if lw:
        if os.path.exists(structure_folder):
            if clean:
                for tpfn in os.listdir(structure_folder):
                    path_file = os.path.join(structure_folder, tpfn)
                    if os.path.isfile(path_file):
                        os.remove(path_file)
        else:
            os.makedirs(structure_folder)
    isucc = 0
    ifail = 0
    for i in range(len(strulist)):
        struid = int(random.random() * len(strulist))
        tempstru = strulist[struid]
        tempstru.set_cell(
            tempstru.cell * (len(tempstru.numbers) / tempstru.get_volume() / ndensity) ** (1.0 / 3),
            scale_atoms=True)
        tempstru.set_atomic_numbers(subst_ele(tempstru.numbers, atomnn))
        # Add break points will change the random number!!! Wired!!!
        if checkdis(tempstru, locmindis):
            newstrulist.append(tempstru)
            isucc += 1
        else:
            if lwf:
                write(structure_folder + '/' + str(ifail + 1) + '_failed_' + tempstru.get_chemical_formula()
                      + '_{}'.format(tempstru.sgn) + '.cif'
                      , tempstru)
            del strulist[struid]
            ifail += 1
            continue
        if lw:
            suffix = ''
            if format == 'cif':
                suffix = '.cif'
            elif format == 'vasp':
                suffix = '.vasp'
            if (format == 'db'):
                dbw.write(newstrulist[-1], sgn=newstrulist[-1].sgn, dname=newstrulist[-1].dname,
                          oid=newstrulist[-1].oid)
            else:
                write(structure_folder + '/' + str(isucc) + '_' + newstrulist[-1].get_chemical_formula()
                      + '_{}'.format(newstrulist[-1].sgn) + suffix
                      , newstrulist[-1], format=format)
        print('Chemical Formula: {:9} Space Group: {:4d}'.format(newstrulist[-1].get_chemical_formula(),
                                                                 newstrulist[-1].sgn))
        if isucc == nstr:
            break
        del strulist[struid]
    print('{} structures generated\n{} physically unjustified structures are filtered out'.format(isucc, ifail))
    return newstrulist
예제 #7
0
def read_ion(fileobj, recover_indices=True, recover_constraints=True):
    text = fileobj.read()
    comments_removed = []
    comments = []
    label = fileobj.name.split('.ion')[0]

    for line in text.splitlines():  # break into lines
        # remove and store the comments
        entry = line.split('#')
        if not entry[0]:
            pass
        else:
            comments_removed.append(entry[0].strip())
        try:
            comments.append(line.split('#')[1])
        except:
            pass
    atoms = Atoms()
    ##################################
    # Parse the unit cell
    ##################################

    """
    because the unit cell is not included in the .ion atomic positions
    file in SPARC, this interface writes the information into the comments
    of .ion files. If a .inpt file is present this code will read that
    if it is not it will attempt to read the comments to find that information.
    The logic for doing this is quite complicated (as seen below)
    """

    inpt_file_usable = True
    lat_vec_speficied = False
    comments_bad = False
    lat_array = []
    # Loop to read cell/latvec from either .inpt or the comment
    # this is pretty complicated
    while True:
        # try to get the unit cell from the .inpt file in the same directory
        if label + '.inpt' in os.listdir('.') and inpt_file_usable == True:
            with open(label + '.inpt', 'r') as f:
                input_file = f.read()
            if 'CELL' not in input_file:
                # We can't find the CELL in the input file
                # set the flag to false and re-run the while loop.
                inpt_file_usable = False
                del input_file
                continue
            input_file = input_file.split('\n')
            for line in input_file:
                if 'CELL' in line:  # find the line with the cell info
                    cell = line.strip().split()[1:]
                    cell = np.array([float(a) for a in cell])
                if 'LATVEC' in line:  # get lattice vectors from next 3 lines
                    lat_vec_speficied = True
                    index = input_file.index(line)
                    for lat_vec in [input_file[a] for a in range(index + 1, index + 4)]:
                        vec = lat_vec.strip().split()
                        vec = np.array([float(a) for a in vec])
                        lat_array.append(
                            vec / np.linalg.norm(vec))  # normalize
                    lat_array = np.array(lat_array)
            if lat_vec_speficied == False:
                lat_array = np.eye(3)
            if 'cell' in locals() and 'lat_array' in locals():
                atoms.cell = (lat_array.T * cell).T * Bohr
                break  # we got the cell, leave the while loop
            else:
                inpt_file_usable = False
                del input_file
                continue

        # if the input file isn't usable, check the comments of the .ion file
        elif comments != [] and comments_bad == False:
            if 'CELL' in comments[1]:  # Check only the second line for a CELL
                cell = np.empty((3, 3))
                try:
                    cell = comments[1].strip().split()[1:]
                    cell = [float(a) for a in cell]
                    for lat_vec in comments[3:6]:  # check only these lines
                        vec = lat_vec.strip().split()
                        vec = np.array([float(a) for a in vec])
                        lat_array.append(
                            vec / np.linalg.norm(vec))  # normalize
                    lat_array = np.array(lat_array)
                    atoms.cell = (lat_array.T * cell).T * Bohr
                    break
                except:
                    comments_bad = True
            else:  # if getting it from the comments fails, return 0 unit cell
                warnings.warn('No lattice vectors were found in either the .inpt'
                              ' file or in the comments of the .ion file. Thus no'
                              ' unit cell was set for the resulting atoms object. '
                              'Use the output atoms at your own risk')
                atoms.cell = np.zeros((3, 3))
                break
        else:  # if there is no cell in the .inpt file, and the .ion file, return 0 unit cell
            warnings.warn('No lattice vectors were found in either the .inpt file '
                          'or in the comments of the .ion file. Thus no unit cell '
                          'was set for the resulting atoms object. Use the output '
                          'atoms at your own risk')
            atoms.cell = np.zeros((3, 3))
            break

    ############################################
    # parse the atoms
    ############################################

    # parse apart the comments to try to recover the indices and boundary conditions

    """
    The strategy of this code is to get the input text separated from the
    comments.

    The comments are then used to glean the information that is
    normally stored in the .inpt file, as well as the original indices
    of the atoms if this file was made by this wrapper.

    from there figure out where the different "Atom Type" blocks are 
    located in the full text with the comments removed. Once that has
    been found parse these sections to gain recover the atomic positions
    and elemental identies of these "atom types."

    We also need to find the locations of the "RELAX" blocks that contain
    the information on which atoms are constained in each "Atom Type"
    block.
    """

    indices_from_comments = []
    constraints = []
    spins = []
    for comment in comments:
        if 'index' in comment:
            if len(comment.split()) == 2:
                try:
                    index = int(comment.split()[1])
                    indices_from_comments.append(index)
                except:
                    pass
        if 'PBC:' in comment:
            pbc_list = []
            pbc = comment.split()[1:]
            for c in pbc:
                if c == 'True' or c == 'true':
                    pbc_list.append(True)
                else:
                    pbc_list.append(False)
            atoms.set_pbc(pbc_list)
            del pbc_list, pbc
    # find the index of line for all the different atom types
    atom_types = [i for i, x in enumerate(
        comments_removed) if 'ATOM_TYPE:' in x]
    relax_blocks = [i for i, x in enumerate(comments_removed) if 'RELAX:' in x]
    spin_blocks = [i for i, x in enumerate(comments_removed) if 'SPIN:' in x]
    for i, atom_type in enumerate(atom_types):
        type_dict = {}
        if i == len(atom_types) - 1:  # treat the last block differently
            # Get the slice of text associated with this atom type
            type_slice = comments_removed[atom_types[i]:]

            # figure out if there are constraints after this block
            if recover_constraints:
                relax_block_index = [a for a in relax_blocks if a > atom_type]
            spin_block_index = [a for a in spin_blocks if a > atom_type]
        else:
            # Get the slice of text associated with this atom type
            type_slice = comments_removed[atom_types[i]: atom_types[i+1]]
            [a for a in relax_blocks if a > atom_type]

            # figure out if there are constraints after this block.
            # the constraint index will be sandwiched between the indicies
            # the current block and the next block
            if recover_constraints:
                relax_block_index = [
                    a for a in relax_blocks if a > atom_types[i]]
                relax_block_index = [
                    a for a in relax_block_index if a < atom_types[i+1]]
            spin_block_index = [a for a in spin_blocks if a > atom_types[i]]
            spin_block_index = [
                a for a in spin_block_index if a < atom_types[i+1]]

        # extract informaton about the atom type from the section header
        for info in ['PSEUDO_POT', 'ATOM_TYPE', 'ATOMIC_MASS', 'COORD',
                     'N_TYPE_ATOM']:
            for line in type_slice[:15]:  # narrow the search for speed
                if info in line:
                    if 'COORD' in line:
                        if 'FRAC' in line:
                            type_dict['COORD_FRAC'] = 1
                        else:
                            type_dict['COORD'] = 1
                    elif 'COORD' not in line:
                        type_dict[info] = line.split()[1]

        # get the lines that contain the constraints block
        if recover_constraints:
            if len(relax_block_index) == 0:
                pass
            elif len(relax_block_index) == 1:
                # offest by one line
                relax_block_index = relax_block_index[0] + 1
                relax_block_end = relax_block_index + \
                    int(type_dict['N_TYPE_ATOM'])
                relax_slice = comments_removed[relax_block_index: relax_block_end]
            elif len(relax_block_index) > 1:
                raise Exception('There appear to be multiple blocks of'
                                ' constraints in one or more of the atom'
                                ' types in your .ion file. Please inspect'
                                ' it to repair it or pass in '
                                '`recover_constraints = False` to ingore'
                                ' constraints')
        # the same as the code above, but for spin
        if len(spin_block_index) == 0:
            pass
        elif len(spin_block_index) == 1:
            spin_block_index = spin_block_index[0] + 1  # offest by one line
            spin_block_end = spin_block_index + int(type_dict['N_TYPE_ATOM'])
            spin_slice = comments_removed[spin_block_index: spin_block_end]
        elif len(spin_block_index) > 1:
            raise Exception('There appear to be multiple blocks of'
                            ' spin values in one or more of the atom'
                            ' types in your .ion file. Please inspect'
                            ' it to repair it or pass in')

        # now parse out the atomic positions
        for coord_set in type_slice[len(type_dict):int(type_dict['N_TYPE_ATOM']) + len(type_dict)]:
            if 'COORD_FRAC' in type_dict.keys():
                x1, x2, x3 = [float(a) for a in coord_set.split()[:3]]
                x, y, z = sum(
                    [x * a for x, a in zip([x1, x2, x3], atoms.cell)])
            elif 'COORD' in type_dict.keys():
                x, y, z = [float(a) * Bohr for a in coord_set.split()[:3]]
            atoms += Atom(symbol=type_dict['ATOM_TYPE'], position=(x, y, z))
        # get the constraints
        if recover_constraints:
            if 'relax_slice' in locals():
                for cons_set in relax_slice:
                    constraints.append([int(a) for a in cons_set.split()])
                del relax_slice
            else:
                # there aren't constraints with this block, put in empty lists
                constraints += [[]] * int(type_dict['N_TYPE_ATOM'])
        if 'spin_slice' in locals():
            for init_spin in spin_slice:
                spins.append(float(init_spin))
            del spin_slice
        else:
            # there aren't spins with this block, put in zeros
            spins += [0] * int(type_dict['N_TYPE_ATOM'])
    # check if we can reorganize the indices
    if len(indices_from_comments) == len(atoms) and recover_indices:
        new_atoms = Atoms(['X'] * len(atoms),
                          positions=[(0, 0, 0)] * len(atoms))
        new_atoms.set_cell(atoms.cell)
        new_spins = [None] * len(atoms)
        # reassign indicies
        for old_index, new_index in enumerate(indices_from_comments):
            new_atoms[new_index].symbol = atoms[old_index].symbol
            new_atoms[new_index].position = atoms[old_index].position
            new_atoms.pbc = atoms.pbc
            new_spins[new_index] = spins[old_index]
        assert new_atoms.get_chemical_formula() == atoms.get_chemical_formula()
        atoms = new_atoms
        spins = new_spins
        # reorganize the constraints now
        if recover_constraints:
            new_constraints = [0] * len(atoms)
            for old_index, new_index in enumerate(indices_from_comments):
                new_constraints[new_index] = constraints[old_index]
            constraints = new_constraints

    atoms.set_initial_magnetic_moments(spins)
    # add constraints
    if recover_constraints:
        constraints = decipher_constraints(constraints)
        atoms.set_constraint(constraints)
    return atoms
예제 #8
0
def fast_specified_adsorption_enumeration(sites,
                                          loading,
                                          atoms,
                                          adsorbate,
                                          outdir,
                                          occluded_sites,
                                          write_format='cif',
                                          suffix='BCC'):

    lattice = atoms.get_cell()
    sites = [s for s in sites if s not in occluded_sites]
    site_indices = [i for i in range(len(sites))]

    distinct_combs = itertools.combinations(site_indices, loading)

    degeneracy_check = []
    distinct_sites = []

    metal_positions = atoms.get_positions()
    metal_atom_numb = list(atoms.get_chemical_symbols())

    index = 0
    for comb in distinct_combs:

        if loading == 1:
            indices = [0] + list(comb)
        else:
            indices = list(comb)

        new_sites = [np.array(sites[i]) for i in indices]

        if len(new_sites) > 1:
            ns_pd = [
                np.linalg.norm(np.dot(lattice,
                                      PBC3DF_sym(i, j)[0]))
                for i, j in itertools.combinations(new_sites, 2)
            ]
            if min(ns_pd) < 2.5:
                continue

        occ_sites = [np.array(sites[i]) for i in indices
                     ]  #+ [np.array(s) for s in occluded_sites]
        comb_sites = np.array(occ_sites)

        # loading 1 special case
        if loading in (1, 9):
            pair_distances = 0.0
        # remaining cases
        else:
            pair_distances = [
                np.linalg.norm(np.dot(lattice,
                                      PBC3DF_sym(i, j)[0]))
                for i, j in itertools.combinations(comb_sites, 2)
            ]
            if min(pair_distances) < 2.0:
                continue
            pair_distances = np.average([
                np.linalg.norm(np.dot(lattice,
                                      PBC3DF_sym(i, j)[0]))
                for i, j in itertools.combinations(comb_sites, 2)
            ])

        all_coords = np.r_[comb_sites, metal_positions]
        all_anumbs = [adsorbate for x in range(loading)] + metal_atom_numb

        test_atoms = Atoms()
        for i, j in zip(all_anumbs, all_coords):
            test_atoms.append(Atom(i, j))

        test_atoms.set_cell(lattice)
        test_atoms.pbc = True
        struct = AseAtomsAdaptor.get_structure(test_atoms)
        sga = SpacegroupAnalyzer(struct, symprec=1.0e-5)
        sgn = sga.get_space_group_number()

        # break and continue conditions for loadings above 2

        degen = (sgn, pair_distances)

        if degen in degeneracy_check:
            continue
        else:
            index += 1
            degeneracy_check.append(degen)
            distinct_sites.append((index, sgn, pair_distances, comb_sites))

    for config in distinct_sites:

        index, spgn, spec, positions = config
        loaded_atoms = Atoms()
        loaded_atoms.set_cell(lattice)

        for m in atoms:
            loaded_atoms.append(m)

        for vec in positions:
            cvec = np.dot(lattice, vec)
            loaded_atoms.append(Atom(adsorbate, cvec))

        comp = loaded_atoms.get_chemical_formula()

        write(outdir + os.sep + comp + '_' + str(spgn) + '_' + str(index) +
              '_' + suffix + '.' + write_format,
              loaded_atoms,
              format=write_format)
        return loaded_atoms
예제 #9
0
def WriteSlabFilm(output,
                  atoms,
                  precision=0.06,
                  smear=0.6,
                  slab_identifers=(78, 79),
                  excluded_from_density=(8, 1),
                  formula_prefix=("Pt", "Au"),
                  formula_suffix=("O", "H")):
    '''
    Write the slab and film layer/type information to the output file,
    returns an array of slab layer tags. After the script runs, 
    the atoms will be tagged to appropriate layers.
    '''

    #First generate an array of the density of atoms on each step of the Z axis.
    #This step is influenced by precision and smear.
    density = []
    for step in DRange(0, atoms.get_cell()[2][2], precision):
        #Keep track of how many atoms are in this density step
        dcount = 0
        for atom in atoms:
            #Exclude some atoms from the density count.  Hydrogen and oxygen are ignored
            #by default as they typically arn't represented in layers very well.
            if not (atom.number in excluded_from_density):
                if ((atom.position[2] - smear) < step) and (
                    (atom.position[2] + smear) > step):
                    dcount += 1
        density.append(dcount)
    #At this point density is populated with the density of atoms on the Z axis
    #Next is identifying the layer ranges.
    startx = -1
    endx = -1
    density_max = -1

    #Ranges are defined
    layer_ranges = []

    #Iterate over all the density values
    for d_index in xrange(0, len(density), 1):
        #Move the value to a local variable
        value = density[d_index]
        #if there is no know endx
        if endx == -1:
            #check if the value is higher than 1/6th the last known highest density for this section.
            if value > (density_max / 6):
                #set all the relevant values
                startx = d_index
                endx = d_index
                density_max = value
        #if you have found a starting index and the value has increased,  then restart the range
        elif value > density_max:
            startx = d_index
            endx = d_index
            density_max = value
        #if the value is the same,  extend the range
        elif value == density_max:
            endx = d_index
        #if the value ever drops under 1/6th of the highest known density for this section,  then the
        #layer can be defined with known values and the startx/endx can be reset.  Don't reset the density
        #max as there is no good reason to.  All layers should have densities that are at least that similar
        #or the definition of a layer is hard to define.  Also might cause errors as moving down away from
        #the density peak.
        elif value <= density_max / 6:
            layer_ranges.append((startx - (smear / precision), endx - startx,
                                 endx + (smear / precision)))
            startx = -1
            endx = -1

    #Find which atoms belong in which layers and tag them.
    for index, layer_range in enumerate(layer_ranges):
        atoms_in_layer = [
            atom for atom in atoms
            if (atom.position[2] / precision > layer_range[0]) and (
                atom.position[2] / precision < layer_range[2])
        ]
        for atom in atoms_in_layer:
            atom.tag = index + 1

    #Define a dictionary of atom tag keys and if they are part of the slab
    slab_layers = {}

    #Try to indentify which layers belong in the slab
    for atom in atoms:
        if atom.tag == 0:
            continue
        if atom.number in slab_identifers:
            slab_layers[atom.tag] = True
        else:
            #This part is designed to not overwrite a value if it already exists.  You never want
            #to overwrite a True value.
            try:
                slab_layers[atom.tag] = (slab_layers[atom.tag])
            except:
                slab_layers[atom.tag] = False

    for atom in atoms:
        #if atom is not assigned
        if atom.tag == 0:
            new_layer = 0
            distance = 1000
            #loop over all layer ranges
            for index, layer_range in enumerate(layer_ranges):
                #the atoms excluded from the density are not part of the slab
                if (atom.number
                        in excluded_from_density) and (slab_layers[index + 1]):
                    continue
                start = abs((atom.position[2] / smear * precision) -
                            layer_range[0])
                end = abs((atom.position[2] / smear * precision) -
                          layer_range[2])
                if (distance >= start):
                    new_layer = index
                    distance = start
                if (distance > end):
                    new_layer = index
                    distance = start
            atom.tag = new_layer + 1

    slab_layer_count = 0
    film_layer_count = 0
    for slab_layer in slab_layers:
        if slab_layers[slab_layer]:
            slab_layer_count += 1
        else:
            film_layer_count += 1

    output['SlabLayers'] = slab_layer_count
    output['FilmLayers'] = film_layer_count

    if film_layer_count == 0:
        return Reports.NoFilmLayers, None
    if slab_layer_count == 0:
        return Reports.NoSlabLayers, None

    slab = Atoms([atom for atom in atoms if (slab_layers[atom.tag])])
    slab.set_cell(atoms.get_cell())
    film = Atoms([atom for atom in atoms if not (slab_layers[atom.tag])])
    film.set_cell(atoms.get_cell())

    slabform = slab.get_chemical_formula()
    filmform = film.get_chemical_formula()

    #Obtain the layers of the slab as individual layers for additional slab analysis
    layers = []
    known_layers = set()
    for atom in slab:
        known_layers.add(atom.tag)
    for layer in known_layers:
        this_layer = []
        for atom in atoms:
            if atom.tag == layer:
                this_layer.append(atom)
        layers.append(Atoms(this_layer))

    layers.sort(key=lambda layer: layer[0].position[2])

    layer_formulas = []
    for layer in layers:
        layer_formulas.append(layer.get_chemical_formula())

    slab_numbers = slab.get_atomic_numbers()

    slabtype = ""  #H**o,  Hetero,  Skin
    skintype = ""  #Only defined if skinned | Plate,  Anneal
    skincomp = ""  #Only defined if skinned
    annealcomp = ""  #Reduced composition of annealed layer
    slabcomp = layer_formulas[0]
    if (CountList(slab_numbers, slab_numbers[0]) == len(slab_numbers)):
        slabtype = "H**o"
    elif CountList(layer_formulas, layer_formulas[0]) == len(layer_formulas):
        slabtype = "Hetero"
    elif ((CountList(layer_formulas, layer_formulas[0])
           == len(layer_formulas) - 1)
          or (CountList(layer_formulas, layer_formulas[1])
              == len(layer_formulas) - 1)):  #Plated Skin
        slabtype = "Skin"
        skintype = "Plate"
        skincomp = layer_formulas[len(layer_formulas) - 1]
    elif ((CountList(layer_formulas, layer_formulas[0])
           == len(layer_formulas) - 2) or (CountList(
               layer_formulas, layer_formulas[1]) == len(layer_formulas) - 2)
          or (CountList(layer_formulas, layer_formulas[2])
              == len(layer_formulas) - 2)):  #Annealed Skin
        slabtype = "Skin"
        skintype = "Anneal"
        skincomp = layer_formulas[len(layer_formulas) - 1]
        annealcomp = layer_formulas[len(layer_formulas) - 2]
    else:  #Failed
        return Reports.SlabTypeIDFail, None

    output['SystemFormula'] = FormatFormula(atoms.get_chemical_formula(),
                                            formula_prefix,
                                            formula_suffix,
                                            red=False)
    output['SlabFormula'] = FormatFormula(slabform,
                                          formula_prefix,
                                          formula_suffix,
                                          red=False)
    output['SlabType'] = slabtype
    output['SkinType'] = skintype
    output['SkinComp'] = FormatFormula(skincomp, formula_prefix,
                                       formula_suffix)
    output['AnnealComp'] = FormatFormula(annealcomp, formula_prefix,
                                         formula_suffix)
    output['SlabComp'] = FormatFormula(slabcomp, formula_prefix,
                                       formula_suffix)
    output['FilmFormula'] = FormatFormula(filmform,
                                          formula_prefix,
                                          formula_suffix,
                                          red=False)
    output['FilmComp'] = FormatFormula(filmform, formula_prefix,
                                       formula_suffix)

    return False, slab_layers
예제 #10
0
파일: formula.py 프로젝트: shuchingou/ase
import numpy as np
from ase import Atoms
from ase.formula import Formula

assert Atoms('MoS2').get_chemical_formula() == 'MoS2'
assert Atoms('SnO2').get_chemical_formula(mode='metal') == 'SnO2'
if sys.version_info >= (3, 6):
    assert Formula('A3B2C2D').format('abc') == 'DB2C2A3'

for sym in ['', 'Pu', 'Pu2', 'U2Pu2', 'U2((Pu2)2H)']:
    for mode in ['all', 'reduce', 'hill', 'metal']:
        for empirical in [False, True]:
            if empirical and mode in ['all', 'reduce']:
                continue
            atoms = Atoms(sym)
            formula = atoms.get_chemical_formula(mode=mode,
                                                 empirical=empirical)
            atoms2 = Atoms(formula)
            print(repr(sym), '->', repr(formula))
            n1 = np.sort(atoms.numbers)
            n2 = np.sort(atoms2.numbers)
            if empirical and len(atoms) > 0:
                reduction = len(n1) // len(n2)
                n2 = np.repeat(n2, reduction)
            assert (n1 == n2).all()

for x in ['H2O', '10H2O', '2(CuO2(H2O)2)10', 'Cu20+H2', 'H' * 15, 'AuBC2', '']:
    f = Formula(x)
    y = str(f)
    assert y == x
    print(f.count(), '{:latex}'.format(f))
    a, b = divmod(f, 'H2O')
예제 #11
0
파일: vap.py 프로젝트: Bismarrck/vap
 def get_feed_dict(self, atoms: Atoms, print_time=False):
     """
     Return the feed dict.
     """
     rc = self._params['rc']
     angular = self._params['angular']
     elements = self._params['elements']
     all_kbody_terms = get_kbody_terms(elements, angular)[0]
     kbody_sizes = compute_dimension(all_kbody_terms,
                                     len(self._params['eta']),
                                     len(self._params['omega']),
                                     len(self._params['beta']),
                                     len(self._params['gamma']),
                                     len(self._params['zeta']))[1]
     offsets = np.insert(np.cumsum(kbody_sizes), 0, 0)
     symbols = atoms.get_chemical_symbols()
     c = Counter(symbols)
     max_occurs = Counter({el: max(1, c[el]) for el in elements})
     vap = VirtualAtomMap(max_occurs, symbols)
     formula = atoms.get_chemical_formula('reduce')
     if formula not in self.vap_cache:
         self.vap_cache[formula] = vap
     nij_max, nijk_max = get_nij_and_nijk(atoms,
                                          self._params['rc'],
                                          angular=self._params['angular'])
     g2_map = get_g2_map(atoms,
                         rc,
                         nij_max,
                         all_kbody_terms,
                         vap,
                         offsets,
                         for_prediction=True,
                         print_time=print_time)
     positions = vap.map_array_to_gsl(atoms.positions).astype(np.float32)
     cell = np.asarray(atoms.cell).astype(np.float32)
     mask = vap.atom_masks.astype(np.float32)
     n_atoms_vap = np.int32(vap.max_vap_n_atoms)
     volume = np.float32(atoms.get_volume())
     pulay_stress = np.float32(0.0)
     row_splits = vap.splits.astype(np.int32)
     n_elements = len(elements)
     composition = np.zeros(n_elements, dtype=np.float32)
     for element, count in Counter(symbols).items():
         composition[elements.index(element)] = np.float32(count)
     feed_dict = {
         self._placeholders['positions']: positions,
         self._placeholders['cells']: cell,
         self._placeholders['n_atoms_plus_virt']: n_atoms_vap,
         self._placeholders['mask']: mask,
         self._placeholders['composition']: composition,
         self._placeholders['row_splits']: row_splits,
         self._placeholders['volume']: volume,
         self._placeholders['pulay_stress']: pulay_stress,
     }
     for key, value in g2_map.items():
         feed_dict[self._placeholders[key]] = value
     if angular:
         g4_map = get_g4_map(atoms,
                             g2_map,
                             all_kbody_terms,
                             offsets,
                             vap,
                             nijk_max,
                             for_prediction=True)
         for key, value in g4_map.items():
             feed_dict[self._placeholders[key]] = value
     return feed_dict
예제 #12
0
if __name__ != '__main__':
    exit()

max_E = 3.0
bond_dist_dict = {"max_E": max_E}
if not os.path.exists('csv'):
    os.makedirs('csv')

for a0 in range(1, 100):
    for a1 in range(1, 100):
        a = Atoms([a0, a1])
        s0, s1 = a[0].symbol, a[1].symbol
        dirname = '%02d-%02d' % (a0, a1)
        if os.path.exists(dirname):
            logging.info('Info: ' + dirname + ' ' + a.get_chemical_formula())
            csv = dirname + '/' + a.get_chemical_formula() + '_1'
            csv_new = 'csv/' + a.get_chemical_formula()
            if not os.path.exists(csv):
                logging.info('dirname: ' + dirname + ' cvs :' + csv +
                             ' not exists')
                continue
            df = pd.read_csv(csv)
            logging.info(df)
            if len(df) == 0:
                logging.error(dirname + ' dataframe is empty')
                # os.system('rm -f '+dirname+'/Done')
                continue
            try:
                df['energy'] -= [min(df['energy'])] * len(df['energy'])
            except:
예제 #13
0
def WriteSlabFilm(output, atoms, precision = 0.06, smear = 0.6,
                 slab_identifers = (78, 79),
                 excluded_from_density = (8, 1),
                 formula_prefix = ("Pt","Au"),
                 formula_suffix = ("O", "H")):
    #First generate an array of the density of atoms on each step of the Z axis.
    #This step is influenced by precision and smear.
    density = []
    for step in DRange(0,atoms.get_cell()[2][2],precision):
        #Keep track of how many atoms are in this density step
        dcount=0
        for atom in atoms:
            #Exclude some atoms from the density count.  Hydrogen and oxygen are ignored
            #by default as they typically arn't represented in layers very well.
            if not (atom.number in excluded_from_density):
                if ((atom.position[2]-smear) < step) and ((atom.position[2]+smear) > step):
                    dcount+=1
        density.append(dcount)
    #At this point density is populated with the density of atoms on the Z axis
    #Next is identifying the layer ranges.
    startx = -1
    endx = -1
    density_max = -1

    #Ranges are defined
    layer_ranges = []
    
    #Iterate over all the density values
    for d_index in xrange(0,len(density),1):
        #Move the value to a local variable
        value = density[d_index]
        #if there is no know endx
        if endx == -1:
            #check if the value is higher than 1/6th the last known highest density for this section.
            if value > (density_max/6):
                #set all the relevant values
                startx = d_index
                endx = d_index
                density_max = value
        #if you have found a starting index and the value has increased,  then restart the range
        elif value > density_max:
            startx = d_index
            endx = d_index
            density_max = value
        #if the value is the same,  extend the range
        elif value == density_max:
            endx = d_index
        #if the value ever drops under 1/6th of the highest known density for this section,  then the
        #layer can be defined with known values and the startx/endx can be reset.  Don't reset the density
        #max as there is no good reason to.  All layers should have densities that are at least that similar
        #or the definition of a layer is hard to define.  Also might cause errors as moving down away from
        #the density peak.
        elif value <= density_max/6:
            layer_ranges.append((startx-(smear/precision),endx-startx,endx+(smear/precision)))
            startx = -1
            endx = -1 
    
    #Find which atoms belong in which layers and tag them.
    for index, layer_range in enumerate(layer_ranges):
        atoms_in_layer = [atom for atom in atoms if (atom.position[2]/precision > layer_range[0])
                           and (atom.position[2]/precision < layer_range[2])]
        for atom in atoms_in_layer:
            atom.tag=index+1

    #Define a dictionary of atom tag keys and if they are part of the slab
    slab_layers = {}
    
    #Try to indentify which layers belong in the slab
    for atom in atoms:
        if atom.tag == 0:
            continue
        if atom.number in slab_identifers:
            slab_layers[atom.tag] = True
        else:
            #This part is designed to not overwrite a value if it already exists.  You never want
            #to overwrite a True value.
            try:
                slab_layers[atom.tag]=(slab_layers[atom.tag])
            except:
                slab_layers[atom.tag]=False

    for atom in atoms:
        #if atom is not assigned
        if atom.tag == 0:
            new_layer = 0
            distance = 1000
            #loop over all layer ranges
            for index, layer_range in enumerate(layer_ranges):
                #the atoms excluded from the density are not part of the slab
                if (atom.number in excluded_from_density) and (slab_layers[index+1]):
                    continue
                start = abs((atom.position[2]/smear*precision)-layer_range[0])
                end = abs((atom.position[2]/smear*precision)-layer_range[2])
                if (distance>=start):
                    new_layer = index
                    distance = start
                if (distance>end):
                    new_layer = index
                    distance = start
            atom.tag=new_layer+1
    
    slab_layer_count=0
    film_layer_count=0
    for slab_layer in slab_layers:
        if slab_layers[slab_layer]:
            slab_layer_count+=1
        else:
            film_layer_count+=1

    output[K._VSF_SlabLayers_]=slab_layer_count
    output[K._VSF_FilmLayers_]=film_layer_count

    if film_layer_count == 0:
        return E._NoFilmLayers_, None
    if slab_layer_count == 0:
        return E._NoSlabLayers_, None
        
    slab = Atoms([atom for atom in atoms if (slab_layers[atom.tag])])
    slab.set_cell(atoms.get_cell())
    film = Atoms([atom for atom in atoms if not (slab_layers[atom.tag])])
    film.set_cell(atoms.get_cell())

    slabform = slab.get_chemical_formula()
    filmform = film.get_chemical_formula()

    #Obtain the layers of the slab as individual layers for additional slab analysis
    layers=[]
    known_layers=set()
    for atom in slab:
        known_layers.add(atom.tag)
    for layer in known_layers:
        this_layer=[]
        for atom in atoms:
            if atom.tag == layer:
                this_layer.append(atom)
        layers.append(Atoms(this_layer))

    layers.sort(key=lambda layer: layer[0].position[2])
    
    layer_formulas = []
    for layer in layers:
        layer_formulas.append(layer.get_chemical_formula())
        
    slab_numbers = slab.get_atomic_numbers()
    
    slabtype = "" #H**o,  Hetero,  Skin
    skintype = "" #Only defined if skinned | Plate,  Anneal
    skincomp = "" #Only defined if skinned
    annealcomp = "" #Reduced composition of annealed layer
    slabcomp = layer_formulas[0]
    if (Count(slab_numbers, slab_numbers[0])==len(slab_numbers)):
        slabtype = "H**o"
    elif Count(layer_formulas, layer_formulas[0])==len(layer_formulas):
        slabtype = "Hetero"
    elif ((Count(layer_formulas, layer_formulas[0])==len(layer_formulas)-1) or
          (Count(layer_formulas, layer_formulas[1])==len(layer_formulas)-1)): #Plated Skin
        slabtype = "Skin"
        skintype = "Plate"
        skincomp = layer_formulas[len(layer_formulas)-1]
    elif ((Count(layer_formulas, layer_formulas[0])==len(layer_formulas)-2) or
          (Count(layer_formulas, layer_formulas[1])==len(layer_formulas)-2) or 
          (Count(layer_formulas, layer_formulas[2])==len(layer_formulas)-2)): #Annealed Skin
        slabtype = "Skin"
        skintype = "Anneal"
        skincomp = layer_formulas[len(layer_formulas)-1]
        annealcomp = layer_formulas[len(layer_formulas)-2]
    else: #Failed
        return E._SlabTypeIDFail_, None

    output[K._SystemFormula_] = FormatForm(atoms.get_chemical_formula(), formula_prefix, formula_suffix, red = False)
    output[K._VSF_SlabFormula_] = FormatForm(slabform, formula_prefix, formula_suffix, red = False)
    output[K._VSF_SlabType_] = slabtype
    output[K._VSF_SkinType_] = skintype
    output[K._VSF_SkinComp_] = FormatForm(skincomp, formula_prefix, formula_suffix)
    output[K._VSF_AnnealComp_] = FormatForm(annealcomp, formula_prefix, formula_suffix)
    output[K._VSF_SlabComp_] = FormatForm(slabcomp, formula_prefix, formula_suffix)
    output[K._VSF_FilmFormula_] = FormatForm(filmform, formula_prefix, formula_suffix, red = False)
    output[K._VSF_FilmComp_] = FormatForm(filmform, formula_prefix, formula_suffix)

    return False, slab_layers
예제 #14
0
파일: react.py 프로젝트: logsdail/carmm
    def vibrate(self, atoms: Atoms, indices: list, read_only=False):
        '''

        This method uses ase.vibrations module, see more for info.
        User provides the FHI-aims parameters, the Atoms object and list
        of indices of atoms to be vibrated. Variables related to FHI-aims are governed by the React object.
        Calculation folders are generated automatically and a sockets calculator is used for efficiency.

        Work in progress

        Args:
            atoms: Atoms object
            indices: list
                List of indices of atoms that require vibrations
            read_only: bool
                Flag for postprocessing - if True, the method only extracts information from existing files,
                no calculations are performed

        Returns:
            Zero-Point Energy: float
        '''
        '''Retrieve common properties'''
        basis_set = self.basis_set
        hpc = self.hpc
        params = self.params
        parent_dir = os.getcwd()
        dimensions = sum(atoms.pbc)

        if not self.filename:
            '''develop a naming scheme based on chemical formula'''
            self.filename = atoms.get_chemical_formula()

        vib_dir = parent_dir + "/VibData_" + self.filename + "/Vibs"
        print(vib_dir)

        vib = Vibrations(atoms, indices=indices, name=vib_dir)
        '''If a calculation was terminated prematurely (e.g. time limit) empty .json files remain and the calculation
        of the corresponding stretch modes would be skipped on restart. The line below prevents this'''
        vib.clean(empty_files=True)
        '''Extract vibration data from existing files'''
        if read_only:
            vib.read()

        else:
            '''Calculate required vibration modes'''
            required_cache = [
                os.path.join(vib_dir, "cache." + str(x) + y + ".json")
                for x in indices
                for y in ["x+", "x-", "y+", "y-", "y-", "z+", "z-"]
            ]
            check_required_modes_files = np.array(
                [os.path.exists(file) for file in required_cache])

            if np.all(check_required_modes_files == True):
                vib.read()
            else:
                '''Set the environment variables for geometry optimisation'''
                set_aims_command(hpc=hpc,
                                 basis_set=basis_set,
                                 defaults=2020,
                                 nodes_per_instance=self.nodes_per_instance)
                '''Generate a unique folder for aims calculation'''
                counter, subdirectory_name = self._restart_setup(
                    "Vib",
                    filename=self.filename,
                    restart=False,
                    verbose=False)

                os.makedirs(subdirectory_name, exist_ok=True)
                os.chdir(subdirectory_name)
                '''Name the aims output file'''
                out = str(counter) + "_" + str(self.filename) + ".out"
                '''Calculate vibrations and write the in a separate directory'''
                with _calc_generator(params, out_fn=out,
                                     dimensions=dimensions)[0] as calculator:
                    if not self.dry_run:
                        atoms.calc = calculator
                    else:
                        atoms.calc = EMT()

                    vib = Vibrations(atoms, indices=indices, name=vib_dir)
                    vib.run()

            vib.summary()
        '''Generate a unique folder for aims calculation'''
        if not read_only:
            os.chdir(vib_dir)
            vib.write_mode()
        os.chdir(parent_dir)

        return vib.get_zero_point_energy()
예제 #15
0
파일: model.py 프로젝트: libAtoms/abcd
    def from_atoms(cls, atoms: Atoms, extra_info=None, store_calc=True):
        """ASE's original implementation"""

        reserved_keys = {
            'n_atoms', 'cell', 'pbc', 'calculator_name',
            'calculator_parameters', 'derived', 'formula'
        }
        arrays_keys = set(atoms.arrays.keys())
        info_keys = set(atoms.info.keys())
        results_keys = set(
            atoms.calc.results.keys()) if store_calc and atoms.calc else {}

        all_keys = (reserved_keys, arrays_keys, info_keys, results_keys)
        if len(set.union(*all_keys)) != sum(map(len, all_keys)):
            print(all_keys)
            raise ValueError('All the keys must be unique!')

        item = cls()

        n_atoms = len(atoms)

        dct = {
            'n_atoms': n_atoms,
            'cell': atoms.cell.tolist(),
            'pbc': atoms.pbc.tolist(),
            'formula': atoms.get_chemical_formula()
        }

        info_keys.update({'n_atoms', 'cell', 'pbc', 'formula'})

        for key, value in atoms.arrays.items():
            if isinstance(value, np.ndarray):
                dct[key] = value.tolist()
            else:
                dct[key] = value

        for key, value in atoms.info.items():
            if isinstance(value, np.ndarray):
                dct[key] = value.tolist()
            else:
                dct[key] = value

        if store_calc and atoms.calc:
            dct['calculator_name'] = atoms.calc.__class__.__name__
            dct['calculator_parameters'] = atoms.calc.todict()
            info_keys.update({'calculator_name', 'calculator_parameters'})

            for key, value in atoms.calc.results.items():

                if isinstance(value, np.ndarray):
                    if value.shape[0] == n_atoms:
                        arrays_keys.update(key)
                    else:
                        info_keys.update(key)
                    dct[key] = value.tolist()

        item.arrays_keys = list(arrays_keys)
        item.info_keys = list(info_keys)
        item.results_keys = list(results_keys)

        item.update(dct)

        if extra_info:
            item.info_keys.extend(extra_info.keys())
            item.update(extra_info)

        item.pre_save()
        return item