Ejemplo n.º 1
0
    def place_neighbour(self,
                        atomic_number,
                        cluster,
                        neighbour_index,
                        max_tries=10,
                        min_distance_ratio=1.0):
        """ Tries to add the new atom to the cluster by placing it at
            bond_length's distance from the atom at neighbour_index and at
            min_distance_ratio x bond_length distance from all other atoms. If
            it succeeds a Atom object is return, else None.

            Parameters:

            atomic_number : {int, str} Atomic number of the new atom
            cluster : Atoms object to add an atom to.
            max_tries : {positive int} The number of random directions to try
            and add the new atom at every neighbour before switching to the next one.
            min_distance_ratio : {positive float} Ratio of bond_length allowed
            as minimum distance to other atoms (not the one it is placed by).
            Only use value less than 2.

            Returns:

            Atom object if successful, else None.
        """
        new_atom = Atom(symbol=atomic_number)
        counter = 0
        found_position = False
        while not found_position and counter < max_tries:
            counter += 1
            # Start at neighbour position
            new_atom.position = cluster.positions[neighbour_index].copy()
            # Add random vector with norm of the provided bond_length
            bond_length = self.blmin[(atomic_number,
                                      cluster.numbers[neighbour_index])]
            new_atom.position += get_random_direction() * bond_length

            # Check if other distances are large enough
            atoms_to_check = np.array([True] * cluster.get_number_of_atoms())
            # neighbour distance may be smaller than other distances
            atoms_to_check[neighbour_index] = False
            found_position = min_distance_fulfilled(
                new_atom, cluster.numbers[atoms_to_check],
                cluster.positions[atoms_to_check], self.blmin,
                min_distance_ratio)

        if counter == max_tries:
            # Could not place atom
            return None

        return new_atom
Ejemplo n.º 2
0
def random_structure_on_substrate(symbols,
                                  amin,
                                  amax,
                                  dmin,
                                  model_file,
                                  Natt=RANDOM_ATTEMPTS):
    # returns random structure (ase Atoms) on substrate with lowest e_tot according to Megnet model
    substrate = read_vasp("POSCAR.substrate")
    adapt = AseAtomsAdaptor()
    model = MEGNetModel.from_file(model_file)
    e_tot_min = 1000.

    for i in range(Natt):
        s = surface(substrate, (0, 0, 1), 1, vacuum=0., tol=1e-10)
        cell = s.get_cell()
        cell[2][2] = CELL_Z
        s.set_cell(cell)
        amin = cell[0][0]
        amax = cell[0][0]
        struct = random_structure(symbols, amin, amax, dmin, iwrite=0)

        j = 0
        atoms = struct.get_chemical_symbols()
        positions = struct.get_positions()
        for atom in atoms:
            at = Atom(atom)
            positions[j][2] = positions[j][2] + SURF_DIST
            pos = positions[j]
            at.position = pos
            s.append(at)
            j = j + 1

        struct_pymatgen = adapt.get_structure(s)
        try:
            e_tot = model.predict_structure(struct_pymatgen)
            # print(e_tot)
        except:
            e_tot = 0.
            print("isolated molecule exception handled")
        if e_tot < e_tot_min:
            struct_out = s
            e_tot_min = e_tot

    print("e_tot min: ", e_tot_min)
    write(filename='best.in', images=struct_out, format="espresso-in")

    del model

    return struct_out
Ejemplo n.º 3
0
    def update_charges_van_der_waals(self):
        """Updates the atomic charges by using the electron density within a
        sphere of van Der Waals radius.

        The charge for each atom in the system is integrated from the electron
        density inside the van Der Waals radius of the atom in hand. The link
        atoms will affect the distribution of the electron density.
        """
        self.timer.start("van Der Waals charge calculation")

        # Turn debugging on or off here
        debugging = False

        atoms_with_links = self.atoms_for_subsystem
        calc = self.calculator

        # The electron density is calculated from the system with link atoms.
        # This way the link atoms can modify the charge distribution
        calc.set_atoms(atoms_with_links)

        if self.charge_source == "pseudo":
            try:
                density = np.array(calc.get_pseudo_density())
            except AttributeError:
                error("The DFT calculator on subsystem \"" + self.name +
                      "\" doesn't provide pseudo density.")

        if self.charge_source == "all-electron":
            try:
                density = np.array(
                    calc.get_all_electron_density(gridrefinement=1))
            except AttributeError:
                error("The DFT calculator on subsystem \"" + self.name +
                      "\" doesn't provide all electron density.")

        # Write the charge density as .cube file for VMD
        if debugging:
            write('nacl.cube', atoms_with_links, data=density)

        grid = self.density_grid

        if debugging:
            debug_list = []

        # The link atoms are at the end of the list
        n_atoms = len(atoms_with_links)
        projected_charges = np.zeros((1, n_atoms))

        for i_atom, atom in enumerate(atoms_with_links):
            r_atom = atom.position
            z = atom.number

            # Get the van Der Waals radius
            R = ase.data.vdw.vdw_radii[z]

            # Create a 3 x 3 x 3 x 3 array that can be used for vectorized
            # operations with the density grid
            r_atom_array = np.tile(
                r_atom, (grid.shape[0], grid.shape[1], grid.shape[2], 1))

            diff = grid - r_atom_array

            # Numpy < 1.8 doesn't recoxnize axis argument on norm. This is a
            # workaround for diff = np.linalg.norm(diff, axis=3)
            diff = np.apply_along_axis(np.linalg.norm, 3, diff)
            indices = np.where(diff <= R)
            densities = density[indices]
            atom_charge = np.sum(densities)
            projected_charges[0, i_atom] = atom_charge

            if debugging:
                debug_list.append((atom, indices, densities))

        #DEBUG: Visualize the grid and contributing grid points as atoms
        if debugging:
            d = Atoms()
            d.set_cell(atoms_with_links.get_cell())

            # Visualize the integration spheres with atoms
            for point in debug_list:
                atom = point[0]
                indices = point[1]
                densities = point[2]
                d.append(atom)
                print "Atom: " + str(atom.symbol) + ", Density sum: " + str(
                    np.sum(densities))
                print "Density points included: " + str(len(densities))
                for i in range(len(indices[0])):
                    x = indices[0][i]
                    y = indices[1][i]
                    z = indices[2][i]
                    a = Atom('H')
                    a.position = grid[x, y, z, :]
                    d.append(a)
            view(d)

        # Normalize the projected charges according to the electronic charge in
        # the whole system excluding the link atom to preserve charge neutrality
        atomic_numbers = np.array(atoms_with_links.get_atomic_numbers())
        total_electron_charge = -np.sum(atomic_numbers)
        total_charge = np.sum(np.array(projected_charges))
        projected_charges *= total_electron_charge / total_charge

        # Add the nuclear charges and initial charges
        projected_charges += atomic_numbers

        # Set the calculated charges to the atoms.  The call for charges was
        # changed between ASE 3.6 and 3.7
        try:
            self.atoms_for_interaction.set_initial_charges(
                projected_charges[0, :].tolist())
        except:
            self.atoms_for_interaction.set_charges(
                projected_charges[0, :].tolist())

        self.pseudo_density = density
        self.timer.stop()
Ejemplo n.º 4
0
    def update_charges_van_der_waals(self):
        """Updates the atomic charges by using the electron density within a
        sphere of van Der Waals radius.

        The charge for each atom in the system is integrated from the electron
        density inside the van Der Waals radius of the atom in hand. The link
        atoms will affect the distribution of the electron density.
        """
        self.timer.start("van Der Waals charge calculation")

        # Turn debugging on or off here
        debugging = False

        atoms_with_links = self.atoms_for_subsystem
        calc = self.calculator

        # The electron density is calculated from the system with link atoms.
        # This way the link atoms can modify the charge distribution
        calc.set_atoms(atoms_with_links)

        if self.charge_source == "pseudo":
            try:
                density = np.array(calc.get_pseudo_density())
            except AttributeError:
                error("The DFT calculator on subsystem \"" + self.name + "\" doesn't provide pseudo density.")

        if self.charge_source == "all-electron":
            try:
                density = np.array(calc.get_all_electron_density(gridrefinement=1))
            except AttributeError:
                error("The DFT calculator on subsystem \"" + self.name + "\" doesn't provide all electron density.")

        # Write the charge density as .cube file for VMD
        if debugging:
            write('nacl.cube', atoms_with_links, data=density)

        grid = self.density_grid

        if debugging:
            debug_list = []

        # The link atoms are at the end of the list
        n_atoms = len(atoms_with_links)
        projected_charges = np.zeros((1, n_atoms))

        for i_atom, atom in enumerate(atoms_with_links):
            r_atom = atom.position
            z = atom.number

            # Get the van Der Waals radius
            R = ase.data.vdw.vdw_radii[z]

            # Create a 3 x 3 x 3 x 3 array that can be used for vectorized
            # operations with the density grid
            r_atom_array = np.tile(r_atom, (grid.shape[0], grid.shape[1], grid.shape[2], 1))

            diff = grid - r_atom_array

            # Numpy < 1.8 doesn't recoxnize axis argument on norm. This is a
            # workaround for diff = np.linalg.norm(diff, axis=3)
            diff = np.apply_along_axis(np.linalg.norm, 3, diff)
            indices = np.where(diff <= R)
            densities = density[indices]
            atom_charge = np.sum(densities)
            projected_charges[0, i_atom] = atom_charge

            if debugging:
                debug_list.append((atom, indices, densities))

        #DEBUG: Visualize the grid and contributing grid points as atoms
        if debugging:
            d = Atoms()
            d.set_cell(atoms_with_links.get_cell())

            # Visualize the integration spheres with atoms
            for point in debug_list:
                atom = point[0]
                indices = point[1]
                densities = point[2]
                d.append(atom)
                print "Atom: " + str(atom.symbol) + ", Density sum: " + str(np.sum(densities))
                print "Density points included: " + str(len(densities))
                for i in range(len(indices[0])):
                    x = indices[0][i]
                    y = indices[1][i]
                    z = indices[2][i]
                    a = Atom('H')
                    a.position = grid[x, y, z, :]
                    d.append(a)
            view(d)

        # Normalize the projected charges according to the electronic charge in
        # the whole system excluding the link atom to preserve charge neutrality
        atomic_numbers = np.array(atoms_with_links.get_atomic_numbers())
        total_electron_charge = -np.sum(atomic_numbers)
        total_charge = np.sum(np.array(projected_charges))
        projected_charges *= total_electron_charge/total_charge

        # Add the nuclear charges and initial charges
        projected_charges += atomic_numbers

        # Set the calculated charges to the atoms.  The call for charges was
        # changed between ASE 3.6 and 3.7
        try:
            self.atoms_for_interaction.set_initial_charges(projected_charges[0, :].tolist())
        except:
            self.atoms_for_interaction.set_charges(projected_charges[0, :].tolist())

        self.pseudo_density = density
        self.timer.stop()