Ejemplo n.º 1
0
def main():
    """
    :brief Convert an AEI zeolite structure to a borate structure
           Main routine

    :return borate: borate structure
    """
    directories = cell_operations.Directories(structure='aei',
                                              input='inputs',
                                              output='aei_outputs')
    silicate = full_coordinated_structure(directories)
    xyz('fully_coordinated/fully_coord', silicate)

    print("If atomic ordering changes => Different starting structure,"
          "I break get_structural_components")
    structures = get_structural_components(silicate, visualise=False)

    # Boron framework components
    upper_ring = ring_mods.ring_substitutions(structures['upper_ring'])
    xyz('fully_coordinated/upper_ring_swap', upper_ring)

    # lower_ring = ring_substitutions(structures['lower_ring'])
    # chains = chain_substitutions(structures['chains'])


    # Piece the borate components together

    # Some guess at the new lattice vectors

    # Fold atoms back into central cell and remove ~ duplicates

    # Return the final structure for relaxing
    return
Ejemplo n.º 2
0
def output_cell_from_general_criterion(system, cutoff):
    """
    (Hopefully) works for any cell
    Expect it to give the same max integers as cubic cells
    """
    name = system.system_label
    lattice = system.lattice
    unit_cell = system.unit_cell
    max_integers = lattice_module.translation_integers_for_radial_cutoff(
        lattice, cutoff)
    print("General criterion for " + name, "Lattice integers:", max_integers)

    translations = lattice_sum(lattice, max_integers, cutoff)
    super_cell = supercell.build_supercell(unit_cell, translations)
    write.xyz(name + "_general_criterion", super_cell)
    return
Ejemplo n.º 3
0
def replace_uncoordinated_atoms(unit_cell: list,
                                translations: list) -> List[atoms.Atom]:
    """
    Identify non-bonded atoms in the unit cell and replace with their equivalent positions
    in adjacent unit cells that result in a fully connected structure

    Translate each loose atom by all translation vectors and see if any
    equivalent position puts each loose atom as a NN of an atom in the central cell
    Retain these and dump the uncoordinated atoms, producing a fully-coordinated structure.

    This is the structure one works with for boron substitutions. Once the boron framework
    has been constructed, one wraps all atoms outside the cell to inside.

    :param unit_cell: atoms.Atoms Unit cell of atoms
    :param translations: List of translation vectors
    :return unit_cell: Unit cell with no uncoordinated atoms
    """

    # AEI Si and O bond bounds (angstrom)
    bond_bounds = BondLengthBounds(1.4, 1.8)
    n_atoms = len(unit_cell)

    unit_cell_positions = [atom.position for atom in unit_cell]
    loose_atom_indices = index_loose_atoms(unit_cell_positions,
                                           bond_bounds.upper)
    assert len(loose_atom_indices) == 4, \
        "For the AEI structure, expect 4 Oxy atoms with no connections in the central cell"

    # One equivalent position per loose atom
    equivalent_positions = [
        find_equivalent_position(ia, unit_cell, translations, bond_bounds)
        for ia in loose_atom_indices
    ]

    unit_cell = replace_loose_atoms(unit_cell, loose_atom_indices,
                                    equivalent_positions)
    assert len(unit_cell) == n_atoms

    output = True
    if output:
        output_dir = 'aei_outputs'
        xyz(output_dir + '/' + "aei_primitive_cell_fullcon", unit_cell)
        print(
            "Output a cell with full connectivity. "
            "Atoms that lie outside the primitive cell can be folded back in")

    return unit_cell
Ejemplo n.º 4
0
def output_cell_from_cubic_criterion(system, cutoff):
    """
    Works for simple cubic cells, but
    is not appropriate as cell angles deviation from 90 degrees
    This typically underestimates max_integers by 1 (see BCC or FCC), such
    that one can improve the situation by doing:
    max_integers = [i + 1 for i in max_integers]
    """

    name = system.system_label
    lattice = system.lattice
    unit_cell = system.unit_cell
    max_integers = lattice_module.simple_cubic_cell_translation_integers(
        lattice, cutoff)
    print("Cubic criterion for " + name, "Lattice integers:", max_integers)

    translations = lattice_sum(lattice, max_integers, cutoff)
    super_cell = supercell.build_supercell(unit_cell, translations)
    write.xyz(name + "_cubic_criterion", super_cell)
    return
Ejemplo n.º 5
0
def local_snip(molecule, neighbours, shared_oxy):

    silicons = neighbours[shared_oxy]
    assert(len(silicons) == 2)

    # List corresponding tetrahedrons wth common corner-sharing
    # oxygen removed
    tetrahedra = []
    for silicon in silicons:
        tetrahedron = [molecule[silicon]]
        for oxy in neighbours[silicon]:
            if oxy != shared_oxy: tetrahedron.append(molecule[oxy])
        tetrahedra.append(tetrahedron)

    molecule = []
    for tetrahedron in tetrahedra:
        for atom in tetrahedron:
            molecule.append(atom)
    write.xyz("two_tet.xyz", molecule, "Two tetra")

    # tetrahedron -> triangle
    triangles = []
    for tetrahedron in tetrahedra:
        # If the tetrahedron has one si and 3 oxy
        if len(tetrahedron) == 4:
            triangles.append(operations.triangle_from(tetrahedron))
        else:
            triangles.append([])


    # Remove shared oxygen last, due to indexing
    changes = [SnipClass(old_atom_index=shared_oxy, modifier='remove')]
    # Extract B positions and labels to B
    for i,silicon in enumerate(silicons):
        new_atom = [atom for atom in molecule if atom.species.lower()=='si'][0]
        change = SnipClass(old_atom_index=silicon, new_atom=new_atom, modifier='replace')
        changes.append(change)

    return operations
Ejemplo n.º 6
0
def replace_loose_atoms(unit_cell: list,
                        loose_atom_indices: List[int],
                        equivalent_positions: list,
                        visualise_parts=False) -> list:
    """
    Remove uncoordinated atoms and their equivalents to the unit cell

    :param unit_cell: List of atoms
    :param loose_atom_indices: Indices for uncoordinated atoms
    :param equivalent_positions: List of equivalent positions for uncoordinated atoms.
    of size len(loose_atom_indices)
    :param visualise_parts: bool, output .xyz of original unit_cell, removed atoms and
     replacement atoms
    :return new_unit_cell with no uncoordinated atoms
    """
    assert len(loose_atom_indices) == len(equivalent_positions), "Should be one new (equivalent) " \
                                                                 "position per uncoordinated atom"

    # Can't use pop as it changes the indexing each time
    new_unit_cell = []
    removed_atoms = []
    for ia in range(0, len(unit_cell)):
        if ia not in loose_atom_indices:
            new_unit_cell.append(unit_cell[ia])
        else:
            removed_atoms.append(unit_cell[ia])

    #TODO(Alex) Generalise
    # I know they're all oxygens but should treat this correctly if making general
    replacements = [
        atoms.Atom(position=position, species='O')
        for position in equivalent_positions
    ]

    if visualise_parts:
        output_dir = 'aei_outputs'
        xyz(output_dir + '/' + "aei_central_cell", unit_cell)
        xyz(output_dir + '/' + "aei_replacements_cell", replacements)
        xyz(output_dir + '/' + "aei_removed_atoms", removed_atoms)

    return new_unit_cell + replacements
Ejemplo n.º 7
0
# If odd, can centre on T=(0,0,0), else can't
n = [3, 3, 3]
translations = supercell.translation_vectors(lattice, n, centred_on_zero=False)
super_cell = supercell.build_supercell(unit_cell, translations)



# Information
print('Number of atoms in supercell:', len(super_cell))
#print("Supercell lattice constants: ", , '(Ang)')  Write this


structure_string = input_strings.structure(super_cell, unit=atoms.CoordinateType.XYZ)
#print(structure_string)

xyz_string = write.xyz(super_cell)
print(xyz_string)

quit()


# Find central cell
icentre = supercell.flatten_supercell_limits([0,0,0], n, centred_on_zero=True)
# could return this from the build_supercell command

cells = supercell.list_global_atom_indices_per_cells(unit_cell, translations)
central_cell_atom_indices = cells[icentre]
print("Central cell contains atoms with this indices:", central_cell_atom_indices)
central_cell = []
for iatom in central_cell_atom_indices:
    central_cell.append(super_cell[iatom])
Ejemplo n.º 8
0
    nearest neighbours to atoms in the central cell. One can then perform substitutions on this system
    and fold any positions that lie outside the central cell of the final structure, back inside
"""

from modules.fileio.write import xyz

import cell_operations

# Main Routine
directories = cell_operations.Directories(structure='aei',
                                          input='inputs',
                                          output='aei_outputs')

unit_cell, lattice_vectors = cell_operations.get_primitive_unit_cell(
    directories, visualise=True)
xyz("cell_primitive", unit_cell)
translations = cell_operations.translations_for_fully_coordinated_unit(
    unit_cell, lattice_vectors)

# Find atoms neighbouring central cell
coordinating_atoms = cell_operations.find_atoms_neighbouring_central_cell(
    unit_cell, translations, visualise=True)

# Replace uncoordinated atoms in central cell (doesn't utilise coordinating_atoms)
unit_cell_replaced = cell_operations.replace_uncoordinated_atoms(
    unit_cell, translations)
xyz("cell_replaced", unit_cell_replaced)

# Check moved atoms can be restored to positions in the central cell
unit_cell_restored = cell_operations.ensure_atoms_in_central_cell(
    unit_cell_replaced, lattice_vectors)
Ejemplo n.º 9
0
def convert_ring(species, positions):

    n_atoms = len(species)
    ring_centre = geometry.find_centre(positions)
    si_oo_units, oxy_ring_atoms = identify_si_o_o_units(species,
                                                        positions,
                                                        plane_index=0)
    # Else same atom will get shifted for each time it reappears in a unit
    si_oo_units = remove_sharing_oxygens(si_oo_units)

    # Move each si_oo_unit, length found from trial and error (specific to AEI)
    length = 8
    for unit in si_oo_units:
        # Find unit centre
        unit_centre = geometry.find_centre(
            [positions[iatom] for iatom in unit])
        # Point outwards from the ring centre
        radial_vector = np.asarray(unit_centre) - ring_centre
        scaled_radial_vector = scaled_vector(length, radial_vector)
        # Update each position
        for atom in unit:
            positions[atom] = scaled_radial_vector + positions[atom]

    # Move each oxy atom in the ring
    oxy_ring_positions = move_atoms_radially(oxy_ring_atoms, positions,
                                             ring_centre, 2)  # 5
    cnt = 0
    for iatom in oxy_ring_atoms:
        positions[iatom] = oxy_ring_positions[cnt]
        cnt += 1

    print_intermediate = True
    if print_intermediate:
        molecule = atoms.Atoms(species, positions)
        write.xyz("select.xyz", molecule)

    quit('up to here - looks like things move unexpectedly')

    # Assume Br-O bond length
    bond_length_bo = 1.7

    # For each Si-O-O of a former tetrahedron, move a unit towards each of the two closest ring oxygens
    # cleave off one of the oxy and convert Si -> Br
    translated_species = []
    translated_positions = []

    for si_oo_unit in si_oo_units:
        neighbouring_ring_atoms = find_closest_ring_oxygens(
            species, positions, si_oo_unit)
        ts, tp = translate_si_o_o_unit(species, positions, si_oo_unit,
                                       neighbouring_ring_atoms)
        translated_species += ts
        translated_positions += tp

    # Remove old atoms
    atom_indices = np.delete(np.arange(0, n_atoms), flatten(si_oo_units))
    new_species = []
    new_positions = []
    for iatom in atom_indices:
        new_species.append(species[iatom])
        new_positions.append(positions[iatom])

    # Add new atoms
    assert len(translated_species) % 2 == 0
    for iatom in range(0, len(translated_species)):
        new_species.append(translated_species[iatom])
        new_positions.append(translated_positions[iatom])

    assert (len(new_species) == len(new_positions))

    # Smart way to do this is to create sets of pair indices from the translated silicons,
    # then put oxygens between them
    # I always do operations in pairs, so should go [b,o,b,o,b,o...] => elements 0 and 2 are a pair in the ring.
    n_atoms = len(new_species)
    boron_indices = [i for i in range(0, n_atoms) if new_species[i] == 'B']

    # Create list of pairs
    boron_pairs = []
    for i in range(0, len(boron_indices), 2):
        boron_A = boron_indices[i]
        boron_B = boron_indices[i + 1]
        print(new_species[boron_A], new_species[boron_B])
        boron_pairs.append([boron_A, boron_B])

    # Put oxygens between these pairs
    for pair in boron_pairs:
        pos_A = np.asarray(new_positions[pair[0]])
        pos_B = np.asarray(new_positions[pair[1]])
        print(np.linalg.norm(pos_A - pos_B))
        pos_oxy = 0.5 * (pos_A + pos_B)
        new_positions.append(pos_oxy.tolist())
        new_species.append('O')

    return new_species, new_positions
Ejemplo n.º 10
0
    One can then perform substitutions on this system
    and fold any positions that lie outside the central cell of the final structure, back inside
    (assuming I can get the new lattice vectors correct)
"""

from modules.fileio.write import xyz

import cell_operations

# Main Routine
directories = cell_operations.Directories(structure='aei',
                                          input='inputs',
                                          output='aei_outputs')

unit_cell, lattice_vectors = cell_operations.get_primitive_unit_cell(directories, visualise=True)
xyz("fully_coordinated/unit_cell", unit_cell)
translations = cell_operations.translations_for_fully_coordinated_unit(unit_cell, lattice_vectors)

# Ah, the below won't work properly if I pass unit_cell_no_dangling to find_neighbour_cell_oxygens
# because the supercell is then constructed from a unit cell that doesn't contain ALL oxygen atoms
#
# Find and delete atoms that are not coordinated
# unit_cell_no_dangling = cell_operations.delete_uncoordinated_atoms(unit_cell)
# xyz("fully_coordinated/unit_no_dangling", unit_cell_no_dangling)

# Find oxygens from other cells that neighbour Si in the central cell
coordinating_atoms = cell_operations.find_neighbour_cell_oxygens(unit_cell, translations)
xyz("fully_coordinated/coordinating_oxy", coordinating_atoms)

Ejemplo n.º 11
0
def get_structural_components(unit_cell, visualise=False):
    """
    :brief For an input unit cell, split into main structural components
        upper ring, lower ring and connecting chains

     ghost atoms are duplicates present in the two connecting
     structures (i.e. ring + chain), and act to indicate the bonding direction

     NOTE: those returned are erroenous. I've tabulated Si but should
     have looked at oxy

    :return structures :  List of structures
    """

    upper_ring = ring_mods.get_ring(unit_cell, lambda atom: atom.position[0] > 11.5,
                                  erroneous_indices=[4, 11])
    lower_ring = ring_mods.get_ring(unit_cell, lambda atom: atom.position[0] <= 7,
                                    erroneous_indices=[3, 12])

    # Don't need these ghosts: the terminating oxygens are the actual ghost/duplicate atoms
    chain1, chain2 = ring_mods.get_connecting_chains(unit_cell)

    structures = {'upper_ring': upper_ring, "lower_ring": lower_ring,
                  'chain1': chain1, 'chain2': chain2}

    if visualise:
        xyz('fully_coordinated/upper_ring', structures['upper_ring'])
        xyz('fully_coordinated/lower_ring', structures['lower_ring'])
        xyz('fully_coordinated/chain1', structures['chain1']['main'])
        xyz('fully_coordinated/chain2', structures['chain2']['main'])
        xyz('fully_coordinated/ghost1', structures['chain1']['ghost'])
        xyz('fully_coordinated/ghost2', structures['chain2']['ghost'])

    return structures
Ejemplo n.º 12
0
def find_atoms_neighbouring_central_cell(unit_cell: atoms.Atoms,
                                         translations: list,
                                         upper_bound_length=1.8,
                                         visualise=False):
    """
    :brief: For a given unit cell, list all atoms in adjacent cells that are
    neighbours with atoms in the (central) unit cell

    :param unit_cell: A list of atoms in the unit cell
    :param translations: List of translation vectors
    :param upper_bound_length: Upper bound for NN bond length of AEI framework (in angstrom)
    :param visualise: Optional, output intermediate structures
    :return coordinating_atoms: A list of atoms in adjacent unit cells that neighbour
    atoms in the central unit_cell.
    """
    assert len(unit_cell) > 0, "len(unit_cell) = 0"
    assert isinstance(unit_cell, list), "unit_cell should be list[atoms.Atom]"
    assert isinstance(unit_cell[0],
                      atoms.Atom), "unit_cell should be list[atoms.Atom]"
    n_atoms_prim = len(unit_cell)

    # Move central cell translation vector to start of list
    # Makes central-cell atoms entries [0: n_atoms_prim]
    zero_translation = np.array([0, 0, 0])
    for i, translation in enumerate(translations):
        if np.array_equal(translation, zero_translation):
            translations.insert(0, translations.pop(i))
            break

    # Super cell = Unit cell plus all coordinating cells
    super_cell = supercell.build_supercell(unit_cell, translations)
    assert len(super_cell) == len(translations) * n_atoms_prim

    positions = [atom.position for atom in super_cell]
    d_supercell = spatial.distance_matrix(positions, positions)
    assert d_supercell.shape[0] == len(
        super_cell), "d_supercell.shape[0] != len(super_cell)"
    assert d_supercell.shape[1] == d_supercell.shape[
        0], "distance matrix is not square"

    # For atoms (indexed by ia) in central cell,
    # list neighbours inside and outside of central cell
    coordinating_atom_indices = []
    for ia in range(0, n_atoms_prim):
        # This should only check for neighbours outside of the central cell
        # but doesn't give the correct answer and I can't see why
        # indices = np.where((d_supercell[ia, n_atoms_prim:] > 0.) &
        #                    (d_supercell[ia, n_atoms_prim:] <= upper_bound_length))[0]
        indices = np.where((d_supercell[ia, :] > 0.)
                           & (d_supercell[ia, :] <= upper_bound_length))[0]
        coordinating_atom_indices.extend(indices)

    # Remove dups
    coordinating_atom_indices = list(set(coordinating_atom_indices))
    # Remove indices associated with atoms in central cell
    coordinating_atom_indices = np.asarray(coordinating_atom_indices)
    coordinating_atom_indices = coordinating_atom_indices[
        coordinating_atom_indices >= n_atoms_prim]

    # Store coordinating atoms
    coordinating_atoms = [super_cell[ia] for ia in coordinating_atom_indices]

    if visualise:
        #TODO(Alex) Resolve this to make general
        output_dir = 'aei_outputs'
        xyz(output_dir + '/' + "aei_supercell", super_cell)
        xyz(output_dir + '/' + "aei_central_cell", unit_cell)
        xyz(output_dir + '/' + "aei_neighbours", coordinating_atoms)

    return coordinating_atoms