Ejemplo n.º 1
0
        def test_fcc(self):
            system = reference_systems.fcc()
            lattice_vectors = system.lattice
            unit_cell = system.unit_cell
            al = 10.26
            neigh_radius = (0.25 * np.sqrt(3) * al) * 0.1
            cutoff = 20

            max_integers_cubic = lattice.simple_cubic_cell_translation_integers(
                lattice_vectors, cutoff)
            max_integers_gen = lattice.translation_integers_for_radial_cutoff(
                lattice_vectors, cutoff)

            #"For FCC, the simple cubic max integers should differ from those found "
            #"using the generic expression"
            self.assertEqual(max_integers_cubic[0], 6)
            self.assertEqual(max_integers_cubic[1], 6)
            self.assertEqual(max_integers_cubic[2], 6)

            self.assertEqual(max_integers_gen[0], 7)
            self.assertEqual(max_integers_gen[1], 7)
            self.assertEqual(max_integers_gen[2], 7)

            translations_cubic = lattice.lattice_sum(lattice_vectors,
                                                     max_integers_cubic,
                                                     cutoff)
            super_cell_cubic = supercell.build_supercell(
                unit_cell, translations_cubic)
            neighbour_indices = get_neighbours(super_cell_cubic, neigh_radius)
            surface_atom_indices = get_surface_atoms(neighbour_indices,
                                                     n_neighbours_bulk=4)
            mean_cubic, sd_cubic = get_radial_distance_mean_and_sd(
                super_cell_cubic, surface_atom_indices)

            # Contains WAY too many atoms
            # Points for C++. Use small cut-offs, hence small cells
            # Create the central cell and find closest distance of neighbours to central atom
            # the number of atoms with the same distance define the neighbour cut-off and NumNN

            translations_gen = lattice.lattice_sum(lattice_vectors,
                                                   max_integers_gen, cutoff)
            super_cell_gen = supercell.build_supercell(unit_cell,
                                                       translations_gen)
            neighbour_indices = get_neighbours(super_cell_gen, neigh_radius)
            surface_atom_indices = get_surface_atoms(neighbour_indices,
                                                     n_neighbours_bulk=4)
            mean_genr, sd_genr = get_radial_distance_mean_and_sd(
                super_cell_gen, surface_atom_indices)

            # Maybe more meaningful to just do a single point at the origin of each cell
            # Gives comparable results for smaller cut-offs too
            # This works... but the difference isn't as striking as expected
            print(mean_cubic, sd_cubic)
            print(mean_genr, sd_genr)
Ejemplo n.º 2
0
def test_fcc():
    print("Testing FCC")

    system = dummy_fcc()
    lattice_vectors = system.lattice
    unit_cell = system.unit_cell
    al = system.al

    cutoff = 20
    max_integers_cubic = lattice.simple_cubic_cell_translation_integers(lattice_vectors, cutoff)
    max_integers_gen = lattice.translation_integers_for_radial_cutoff(lattice_vectors, cutoff)

    # "For FCC, the simple cubic max integers should differ from those found "
    # "using the generic expression"
    assert max_integers_cubic[0] == 3
    assert max_integers_cubic[1] == 3
    assert max_integers_cubic[2] == 3

    assert max_integers_gen[0] == 4
    assert max_integers_gen[1] == 4
    assert max_integers_gen[2] == 4

    NN_dist, num_NN = get_neighbour_details(dummy_fcc())

    translations_cubic = lattice.lattice_sum(lattice_vectors, max_integers_cubic, cutoff)
    super_cell_cubic = supercell.build_supercell(unit_cell, translations_cubic)

    surface_atom_indices = get_surface_atoms(super_cell_cubic, NN_dist, num_NN)
    distances = get_radial_distance_mean_and_sd(super_cell_cubic, surface_atom_indices)
    mean_cubic = np.mean(distances)
    sd_cubic   = np.std(distances)
    max_cubic  = np.max(distances)

    translations_gen = lattice.lattice_sum(lattice_vectors, max_integers_gen, cutoff)
    super_cell_gen = supercell.build_supercell(unit_cell, translations_gen)

    print(len(super_cell_cubic), len(super_cell_gen))


    surface_atom_indices = get_surface_atoms(super_cell_gen, NN_dist, num_NN)
    distances = get_radial_distance_mean_and_sd(super_cell_gen, surface_atom_indices)
    mean_genr = np.mean(distances)
    sd_genr = np.std(distances)
    max_genr= np.max(distances)

    # I think this is the best one can do: Evaluate the standard deviatiosn
    print(cutoff)
    print(mean_cubic, sd_cubic, max_cubic)
    print(mean_genr, sd_genr, max_genr)

    assert mean_cubic < mean_genr
    assert sd_cubic > sd_genr
Ejemplo n.º 3
0
def silicon_supercell(n, centred_on_zero=None):
    if centred_on_zero == None:
        centred_on_zero = (n[0] * n[1] * n[2]) % 2 != 0

    translations = supercell.translation_vectors(
        lattice, n, centred_on_zero=centred_on_zero)
    super_cell = supercell.build_supercell(unit_cell, translations)

    # Information
    print('Number of atoms in supercell:', len(super_cell))

    # Find central cell
    icentre = supercell.flatten_supercell_limits(
        [0, 0, 0], n, centred_on_zero=centred_on_zero)

    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])

    return super_cell
Ejemplo n.º 4
0
        def test_simple_cubic(self):
            system = reference_systems.simple_cubic()
            lattice_vectors = system.lattice
            unit_cell = system.unit_cell
            cutoff = 40

            max_integers_cubic = lattice.simple_cubic_cell_translation_integers(
                lattice_vectors, cutoff)
            max_integers_gen = lattice.translation_integers_for_radial_cutoff(
                lattice_vectors, cutoff)

            self.assertTrue(np.all(max_integers_cubic == max_integers_gen))
            self.assertEqual(max_integers_gen[0], 4)
            self.assertEqual(max_integers_gen[1], 4)
            self.assertEqual(max_integers_gen[2], 4)

            translations_cubic = lattice.lattice_sum(lattice_vectors,
                                                     max_integers_cubic,
                                                     cutoff)
            super_cell_cubic = supercell.build_supercell(
                unit_cell, translations_cubic)
            neighbour_indices = get_neighbours(super_cell_cubic, radius=6)
            surface_atom_indices = get_surface_atoms(neighbour_indices,
                                                     n_neighbours_bulk=4)
            mean_cubic, sd_cubic = get_radial_distance_mean_and_sd(
                super_cell_cubic, surface_atom_indices)

            # Visualise - annoyingly won't write directly to file
            #surface_cell = [super_cell_cubic[ia] for ia in surface_atom_indices]
            #print(write.xyz_string(surface_cell))

            translations_gen = lattice.lattice_sum(lattice_vectors,
                                                   max_integers_gen, cutoff)
            super_cell_gen = supercell.build_supercell(unit_cell,
                                                       translations_gen)
            neighbour_indices = get_neighbours(super_cell_gen, radius=6)
            surface_atom_indices = get_surface_atoms(neighbour_indices,
                                                     n_neighbours_bulk=4)
            mean_genr, sd_genr = get_radial_distance_mean_and_sd(
                super_cell_gen, surface_atom_indices)

            # Maybe more meaningful to just do a single point at the origin of each cell
            print(mean_cubic, sd_cubic)
            print(mean_genr, sd_genr)
Ejemplo n.º 5
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.º 6
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.º 7
0
def super_cell_from_crystal(rutile: dict, cell_integers: list) -> dict:
    assert rutile['lattice_parameters']['a'].unit == 'angstrom'

    # Extract data
    a = rutile['lattice_parameters']['a'].value
    c = rutile['lattice_parameters']['c'].value
    lattice = bravais.simple_tetragonal(a, c)
    positions_angstrom = [
        np.matmul(lattice, pos) for pos in rutile['fractional']
    ]
    species = rutile['species']

    # Compute supercell
    unit_cell = [
        atoms.Atom(species[ia], positions_angstrom[ia])
        for ia in range(0, len(species))
    ]
    super_cell = supercell.build_supercell(
        unit_cell, supercell.translation_vectors(lattice, cell_integers))
    assert len(super_cell) == np.prod(cell_integers) * len(unit_cell)
    lattice_scell = supercell.get_supercell_vector(lattice, cell_integers)
    #write.xyz('rutile_222.xyz', super_cell)

    # Repackage the information
    inv_lattice_scell = np.linalg.inv(lattice_scell)
    positions_frac = [
        np.matmul(inv_lattice_scell, atom.position) for atom in super_cell
    ]
    species_sc = [atom.species for atom in super_cell]
    a = np.linalg.norm(lattice_scell[:, 0])
    c = np.linalg.norm(lattice_scell[:, 2])
    lattice_parameters = {'a': Set(a, 'angstrom'), 'c': Set(c, 'angstrom')}
    data = {
        'fractional': positions_frac,
        'species': species_sc,
        'lattice_parameters': lattice_parameters,
        'space_group': ('', 136),  #rutile['space_group']
        'n_atoms': len(species_sc)
    }

    return data
Ejemplo n.º 8
0
def get_neighbour_details(system: System):
    unit_cell = system.unit_cell
    lattice_vectors = system.lattice

    coordinating_translations = supercell.translation_vectors(
        lattice_vectors, [3, 3, 3], centred_on_zero=True)

    central_index = int(0.5 * len(coordinating_translations))
    assert np.all(coordinating_translations[central_index] == 0.), "translation = [0, 0, 0]"

    super_cell = supercell.build_supercell(unit_cell, coordinating_translations)
    positions = get_positions(super_cell)
    d_matrix = spatial.distance_matrix(positions, positions)
    #write.xyz("cell", super_cell)

    NN_dist = np.sort(d_matrix[central_index, :])[1]
    tol = 1.e-5
    NN_indices = np.where((d_matrix[central_index, :] > 0.) &
                          (d_matrix[central_index, :] <= NN_dist + tol))[0]

    #write.xyz("reduced_cell", [super_cell[ia] for ia in NN_indices])
    return NN_dist, len(NN_indices)
Ejemplo n.º 9
0
# Silicon cubic unit cell in angstrom
al = params.silicon['lattice_constant']['angstrom']
fractional_basis_positions = crystals.silicon('conventional')
lattice = bravais.simple_cubic(al)

unit_cell = []
for atom in fractional_basis_positions:
    pos_angstrom = np.matmul(lattice, atom.position)
    unit_cell.append(atoms.Atom(atom.species, pos_angstrom))

# Silicon supercell
# 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=True)
super_cell = supercell.build_supercell(unit_cell, translations)

icentre = supercell.flatten_supercell_limits([0, 0, 0],
                                             n,
                                             centred_on_zero=True)
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])

#################################################################
# Test. For one atomic species, compare function output to
Ejemplo n.º 10
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
Ejemplo n.º 11
0
def find_neighbour_cell_oxygens(unit_cell: atoms.Atoms,
                                translations: list,
                                distance_cutoff=2.5):
    """
    :brief: For a given unit cell, list all oxygens in adjacent cells that are
    neighbours with silicons in the (central) unit cell

    :param unit_cell: A list of atoms in the unit cell
    :param translations: List of translation vectors
    :param distance_cutoff: Upper bound for NN bond length of AEI framework (in angstrom)
    Found the default via trial and error
    :return coordinating_atoms: A list of oxygen atoms in adjacent unit cells that neighbour
    silicon 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]"
    assert len(
        translations
    ) == 27, "Require 27 translation vectors for central cell to be fully coordinated"
    n_atoms = len(unit_cell)

    # Move central cell translation vector to start of list
    # Makes central-cell atoms entries [0: n_atoms]
    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
    cells_are_the_same(unit_cell, super_cell[0:n_atoms])

    # Distance matrix for supercell
    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"

    # Find silicon indices in central cell
    silicon_indices = []
    for ia in range(0, n_atoms):
        if super_cell[ia].species.lower() == 'si':
            silicon_indices.append(ia)

    def all_species_equal_to(iSi, species, label: str):
        species_lowercase = [i.lower() for i in species]
        if species_lowercase.count(label.lower()) != len(species):
            print("For si index " + str(iSi) + "neighbour species:", species)
            quit("They should all be '" + label + "'")
        return

    # Find all within a distance_cutoff oxygen
    indices = []
    coordinating_atom_indices = []
    for iSi in silicon_indices:
        indices = np.where((d_supercell[iSi, :] > 0.)
                           & (d_supercell[iSi, :] <= distance_cutoff))[0]
        assert len(
            indices
        ) == 4, "Expect 4 indices to be found for each Si to be fully coordinated"
        all_species_equal_to(iSi, [super_cell[i].species for i in indices],
                             'o')
        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_indices2 = []
    for index in coordinating_atom_indices:
        if index >= n_atoms:
            coordinating_atom_indices2.append(index)

    coordinating_atoms = [super_cell[ia] for ia in coordinating_atom_indices2]
    return coordinating_atoms