Esempio n. 1
0
    def get_energy(self, cluster: Cluster) -> float:
        """
        Method to return the single-point energy of a system

        Parameters
        ----------


        Returns
        -------
        float(energy): Energy of the system at the given coordinates

        Args:
            cluster: Cluster instance, required, the for which to calculate energy
            *args: list, optional, postitional arguments
            **kwargs: Other keyword arguments needed on a per-implementation basis (i.e. atom labels)

        """
        if isinstance(cluster, Cluster):
            coordinates, _, _ = cluster.get_particle_positions()
        elif isinstance(cluster, np.ndarray):
            natoms = int(len(cluster) / 3)
            coordinates = cluster.reshape((natoms, 3))
        else:
            raise AttributeError("coordinate format is not accepted")

        rs = get_all_magnitudes(coordinates)
        coulombic = self.coulombic_E_component(rs)

        LJ_component = self.LJ_E_component(rs)
        return coulombic + LJ_component
Esempio n. 2
0
    def minimize(self, cluster: Cluster, *args, **kwargs) -> Cluster:
        """
        Method to locally minimise a cluster of Lennard-Jones particles.
        Uses the L-BFGS-B method implemented within scipy.minimize.

        Attributes:
            coordinates: np.array(shape=(number_of_particles, 3), dtype=float) array of coordinates
            kwargs: Dict containing any other keyword arguments to be passed to the scipy optimizer
            molecules: list(int), optional,

        Returns
        -------
        result_dict{'coordinates': optimised structure coordinates,
                    'energy': final energy of the cluster,
                    'success': True if successfully minimised}

        """
        positions = list(cluster.get_particle_positions())
        coordinates = positions[0].flatten()

        # args = {"sigma": self.sigma, "epsilon": self.epsilon4, "base_exp": 6}

        result = scipy.optimize.minimize(
            fun=self.get_energy,
            x0=coordinates,  # , args=args,
            method='L-BFGS-B',
            jac=self.get_jacobian)

        positions = (result.x.reshape(
            (self.n_atoms, 3)), positions[1], positions[2])
        cluster.set_particle_positions(positions)
        cluster.cost = result.fun
        return cluster
Esempio n. 3
0
    def minimize(self, cluster: Cluster, *args, **kwargs) -> Cluster:
        """
        Method to locally minimise a cluster of Lennard-Jones particles.
        Uses the L-BFGS-B method implemented within scipy.minimize.


        Args:
            cluster: Cluster instance, required, cluster instance to be minimized
            kwargs: Dict containing any other keyword arguments to be passed to the scipy optimizer

        Returns:
            result_dict{'coordinates': optimised structure coordinates,
                    'energy': final energy of the cluster,
                    'success': True if successfully minimised}

        """
        coords, ids, labels = cluster.get_particle_positions()
        coordinates = coords

        result = minimize(fun=self.get_energy,
                          x0=coordinates.flatten(),
                          method='L-BFGS-B',
                          jac=self.get_jacobian,
                          *args,
                          **kwargs)

        if not result.success:
            print("Optimization failed")

        cluster.set_particle_positions(
            (result.x.reshape(coordinates.shape), ids, labels))
        cluster.cost = result.fun
        return cluster
Esempio n. 4
0
    def check_cluster(self, cluster: Cluster) -> bool:
        """Simple distance check to ensure that there is no particle overlap"""

        all_r_sq = (cluster.get_particle_positions()[0]**2).sum(-1)

        if min(all_r_sq) <= self.cutoff:
            return False
        else:
            return True
Esempio n. 5
0
def align_clusters(c1: Cluster, c2: Cluster) -> None:
    """Employs the Kabsch algorithm to align two clusters.

    c1 and c2 will be modified in-place

    See:

    Kabsch, Wolfgang, (1976) "A solution of the best rotation to relate two sets of vectors",
    Acta Crystallographica 32:922

    Args:
        c1, (Cluster): required
        c2, (Cluster): required

    Returns:
        None

    """
    # Transform both clusters CoM to the origin.
    c1.center()
    c2.center()

    coords_mols_labels_c1 = c1.get_particle_positions()
    coords_c1 = coords_mols_labels_c1[0]
    coords_mols_labels_c2 = c2.get_particle_positions()
    coords_c2 = coords_mols_labels_c2[0]

    # Calculate covarience matrix
    A = np.dot(coords_c2.transpose(), coords_c1)
    # Single value decomp
    u, s, v = np.linalg.svd(A)

    if np.linalg.det(u) * np.linalg.det(v) + 1.0 < 1e-8:
        s[-1] = -s[-1]
        u[:, -1] = -u[:, -1]

    rot_mat = np.dot(u, v).transpose()

    new_coordinates = []
    for c in coords_c2:
        new_coordinates.append(np.dot(c, rot_mat))
    new_coordinates = np.array(new_coordinates)
    c2.set_particle_positions(
        (new_coordinates, coords_mols_labels_c2[1], coords_mols_labels_c2[2]))
Esempio n. 6
0
    def compare(self, cluster1: Cluster, cluster2: Cluster, *args,
                **kwargs) -> bool:
        """Simple geometry comparison.

        Aligns clusters, then

        Args:
            cluster1, (Cluster): required
            cluster2, (Cluster): required

        Returns:
            bool, True if the clusters are the same to within self.accuracy, else False

        """
        align_clusters(cluster1, cluster2)
        g_tensor_c1 = gyration_tensor(cluster1.get_particle_positions()[0])
        g_tensor_c2 = gyration_tensor(cluster2.get_particle_positions()[0])
        g_tensor_c1 -= g_tensor_c2

        return np.linalg.norm(g_tensor_c1) <= self.accuracy
Esempio n. 7
0
    def test_simple_case(self):

        c1 = Cluster(molecules=[
            Molecule(coordinates=np.array([[1.0, 0.0, 0.0]]),
                     particle_names=["LJ"]),
            Molecule(coordinates=np.array([[0.0, 1.0, 0.0]]),
                     particle_names=["LJ"]),
            Molecule(coordinates=np.array([[0.0, 0.0, 1.0]]),
                     particle_names=["LJ"])
        ])
        c2 = Cluster(molecules=[
            Molecule(coordinates=np.array([[-1.0, 0.0, 0.0]]),
                     particle_names=["LJ"]),
            Molecule(coordinates=np.array([[0.0, -1.0, 0.0]]),
                     particle_names=["LJ"]),
            Molecule(coordinates=np.array([[0.0, 0.0, -1.0]]),
                     particle_names=["LJ"])
        ])

        align_clusters(c1, c2)

        for a, b in zip(c1.get_particle_positions()[0].flatten().tolist(),
                        c2.get_particle_positions()[0].flatten().tolist()):
            self.assertAlmostEqual(a, b)
Esempio n. 8
0
    def test_shake_mutate_good(self) -> None:
        c1 = Cluster(cost=9.0,
                     molecules=[Molecule(coordinates=np.random.uniform(low=10, size=(2, 3)),
                                         particle_names=["B", "Be"]),
                                Molecule(coordinates=np.random.uniform(low=10, size=(3, 3)),
                                         particle_names=["Be", "B", "Be"])])

        mutation = Shake()

        mutated_c1 = mutation.mutate(copy.deepcopy(c1))

        diff = c1.get_particle_positions()[0] - mutated_c1.get_particle_positions()[0]

        self.assertEqual(magnitude(diff[0]), 0.42059300827254525)
        self.assertEqual(magnitude(diff[-1]), 0.4186786088973787)
Esempio n. 9
0
    def test_ljc_get_jacobian_1(self) -> None:
        fast_potential = LJcPotential(2)

        c1 = Cluster(cost=0.0,
                     molecules=[
                         Molecule(coordinates=np.array([[0.0, 0.0, 0.0]]),
                                  particle_names=["LJ"]),
                         Molecule(coordinates=np.array(
                             [[2**(1 / 6.), 0.0, 0.0]]),
                                  particle_names=["LJ"])
                     ])

        test_jac = fast_potential.get_jacobian(
            c1.get_particle_positions()[0].flatten())

        # print(ref_jac, test_jac)
        self.assertEqual(
            np.array([-12.0, 0.0, 0.0, 12.0, 0.0, 0.0]).all(), test_jac.all())
Esempio n. 10
0
    def minimize(self, cluster: Cluster) -> Cluster:

        self.step_size = self.initial_step
        step = 0

        _ = cluster.get_particle_positions()
        # n_coords = len(_[1])
        self.coords = _[0]

        np.random.shuffle(self.coords)

        last_energy = self.pot.get_energy(cluster)

        converged = False
        while step <= self.max_steps and not converged:
            converged = True
            step += 1

            for idx in range(len(self.coords)):
                self._take_step(self.coords[idx])

            cluster.set_particle_positions((self.coords, _[1], _[2]))
            new_energy = self.pot.get_energy(cluster)
            # print(new_energy, last_energy)
            dE = new_energy - last_energy
            last_energy = new_energy
            self.update()

            if abs(dE) >= self.convergence_gradient:
                converged = False

        # print(f"Exiting in {step} steps")
        # for i in self.coords:
        #     print("Cl   " + "   ".join([str(a) for a in i]))
        cluster.minimum = True
        return cluster
Esempio n. 11
0
    def minimize(self, cluster: Cluster, *args, **kwargs) -> Cluster:

        name = self.get_directory()

        raw_coordinates = cluster.get_particle_positions()
        coordinates = self.format_XYZ(raw_coordinates[0], raw_coordinates[2])

        tag_dict = {"<XYZ>": coordinates, "<NAME>": name}

        run_dir = self.work_dir + name

        input_file_name = run_dir + "/input.nw"
        output_file_name = run_dir + "/output.out"

        self.insert_to_template(template=self.minimize_template,
                                out_file=input_file_name,
                                target_dict=tag_dict)

        commands = [
            self.run_string, f"{input_file_name}", f"> {output_file_name}"
        ]
        commands = ' '.join(commands)

        self.log.info(f"Starting NWChem with commands: {commands}")

        nwchem_process = subprocess.Popen(commands, cwd=run_dir, shell=True)

        exit_code = nwchem_process.wait()

        if exit_code == 134:
            try:
                raise DFTExitedUnexpectedlyError(
                    f"NWChem exited unexpectedly with exitcode: {exit_code}\n")
            except DFTExitedUnexpectedlyError as error:
                self.log.exception(error)
                raise

        if exit_code != 0:
            try:
                raise DFTExitedUnexpectedlyError(
                    f"NWChem exited unexpectedly with exitcode: {exit_code}\n")
            except DFTExitedUnexpectedlyError as error:
                self.log.exception(error)
                raise
        else:
            self.log.info(
                f"NWChem exited successfully. Exit code: {exit_code}")

        output_parser = NWChemOutputParser(output_file_name)

        output_parser.parse()

        final_structure = output_parser.final_structure

        try:
            assert all(raw_coordinates[2]) == all(final_structure[0])
        except AssertionError as error:
            self.log.exception(
                f"Atoms out of order! {raw_coordinates[2]} != {final_structure[0]}\n{error}"
            )
        cluster.set_particle_positions(
            (final_structure[1], raw_coordinates[1], final_structure[0]))

        return cluster
Esempio n. 12
0
    def run(self, cluster: Cluster, n_steps) -> dict:

        # setup quench here, so we know how many atoms we are dealing with...
        self.quench = SGD(len(cluster.get_particle_positions()[0]))
        super().run(cluster, n_steps)
        return self.results_dict
Esempio n. 13
0
    def run_DeMonNano(
            self,
            cluster: Cluster,
            dir_name: str,
            optimize: bool = False):  # TODO move minimize and energy to here
        """Common interface to DeMonNano"""

        if dir_name is not None:
            dir_name = os.path.abspath(dir_name)
        else:
            dir_name = os.path.abspath(self.get_directory())

        inp_fname = dir_name + "/deMon.inp"
        out_fname = dir_name + "/deMon.out"

        shutil.copyfile("SCC-SLAKO", dir_name + "/SCC-SLAKO")
        shutil.copyfile("SLAKO", dir_name + "/SLAKO")

        coords, molecule_ids, atom_labels = cluster.get_particle_positions()

        Natoms = len(molecule_ids)

        xyz_formatted_coordinates = self.format_XYZ(coords, atom_labels)

        with work_dir():

            os.chdir(dir_name)  # Change into the scratch dir.

            tag_dict = {"<XYZ>": xyz_formatted_coordinates}

            if optimize:
                template = self.minimize_template
            else:
                template = self.energy_template

            self.insert_to_template(template=self.work_dir + template,
                                    out_file=inp_fname,
                                    target_dict=tag_dict)

            with open("error_file", "w") as ef:
                # self.run_string should just be the location of the deMonNano executable
                dftb_process = subprocess.Popen([self.run_string],
                                                cwd=dir_name,
                                                shell=True,
                                                stderr=ef,
                                                stdout=ef)

            exit_code = dftb_process.wait()

            # check exit code
            if exit_code != 0:
                try:
                    raise DFTBError(
                        f"DFTB+ exited unexpectedly with exitcode: {exit_code}\n"
                    )
                except DFTBError as error:
                    self.log.exception(error)
                    raise
            else:
                self.log.debug(
                    f"DFTB+ exited successfully. Exit code: {exit_code}")

            if optimize:

                # noinspection PyTypeChecker
                parser = DeMonNanoParser(out_fname,
                                         natoms=Natoms,
                                         logger=self.log)

                result_dict = parser.parse_DeMonNano_output()
                new_coords = result_dict["coordinates"]
                cluster.set_particle_positions(
                    (new_coords, molecule_ids, atom_labels))

            else:

                # noinspection PyTypeChecker
                parser = DeMonNanoParser(out_fname,
                                         natoms=Natoms,
                                         geometry_opt=False,
                                         logger=self.log)

                result_dict = parser.parse_DeMonNano_output()

            energy = result_dict["energy"]
            cluster.cost = energy
            # os.chdir("..")  # Come back out of the scratch dir.

            return cluster