예제 #1
0
    def insert_cluster(self, new_cluster: Union[Minimum, Cluster], *args,
                       **kwargs) -> Cluster:
        """Insert a single existing_cluster into the database

        Note: this method does check that the new cluster is unique

        Args:
            new_cluster: Cluster, required, object to be inserted

        Returns:
            cluster: Cluster, inserted into database (or the pre-existing cluster already in the database)

        """
        # if isinstance(new_cluster, Cluster):
        cluster = Minimum(cluster=copy.deepcopy(new_cluster))

        session = self.get_session()

        cost = cluster.cost

        if self.compareClusters is not None:
            close_clusters = session.query(Minimum). \
                filter(Minimum.cost > cost - self.accuracy). \
                filter(Minimum.cost < cost + self.accuracy)

            for existing_cluster in close_clusters:
                # compareClusters returns True if clusters are the same
                old_cluster = Cluster(db_cluster=existing_cluster)
                if not self.compareClusters(new_cluster, old_cluster, *args, **
                                            kwargs):
                    continue
                else:
                    session.rollback()
                    session.commit()
                    return copy.deepcopy(old_cluster)

        else:
            message = f"""Cluster inserted without checking, as self.compareClusters is None: {cluster}
            This can lead to non-unique clusters in the database"""
            self.log.warning(message)

        try:
            session.add(cluster)
        except InvalidRequestError:
            session.rollback()

        session.commit()
        return Cluster(db_cluster=cluster)
예제 #2
0
    def get_clusters_by_cost(self,
                             cost_max: float or int,
                             cost_min: float or int = None,
                             max_number_of_clusters: int = None) -> list:
        """
        Method to return Clusters ordered by energy under a threshold (and optionally above another threshold)

        Parameters
        ----------
        cost_max: float, required, Clusters above this energy are not returned
        cost_min: float, optional, Clusters below this energy are excluded
        max_number_of_clusters: int, optional, number of lowest energy clusters to be returned (default is all)

        Returns
        -------
        List of Cluster objects ordered by ascending energy

        """
        session = self.get_session()
        if cost_min is not None:
            if cost_min >= cost_max and cost_min is not None:
                raise ValueError(
                    "Minimum cost: {} exceeds maximum cost: {}!".format(
                        cost_min, cost_max))

            result = session.query(Minimum).filter(Minimum.cost <= cost_max).\
                filter(Minimum.cost >= cost_min)

        else:
            result = session.query(Minimum).filter(Minimum.cost <= cost_max)

        result = [Cluster(db_cluster=cluster) for cluster in result]

        return sorted(result[:max_number_of_clusters], key=lambda x: x.cost)
예제 #3
0
    def get_clusters_by_id(self, ids: int or list) -> Cluster or list:
        """Returns clusters selected by id

        Can request single cluster or list of clusters

        If a list of ids is supplied, clusters will be returned in the same order

        Args:
            ids: int or list(int), required, id/s of cluster/s to fetch

        Returns:
            list of clusters with ids from above order is preserved

        """
        assert isinstance(ids, int) or (isinstance(ids, list)
                                        and isinstance(ids[0], int))

        session = self.get_session()

        if isinstance(ids, int):
            ids = [ids]

        clusters = []
        for _id in ids:
            clusters.append(
                Cluster(db_cluster=session.query(Minimum).get(_id)))

        if len(ids) == 1:
            return clusters[0]
        else:
            return clusters
예제 #4
0
    def test_minimise(self):

        testdir = "test_dftb"
        if os.path.exists(testdir):
            shutil.rmtree(testdir)
        os.mkdir(testdir)

        clus1 = Cluster(molecules=[Molecule(particle_names=["H", "H", "O"],
                                            coordinates=np.array([[0.0, 0.0, 1.0],
                                                                  [0.0, 0.0, -1.0],
                                                                  [0.0, 0.0, 0.0]])),
                                   Molecule(particle_names=["H", "H", "O"],
                                            coordinates=np.array([[2.0, 0.0, 1.0],
                                                                  [2.0, 0.0, -1.0],
                                                                  [2.0, 0.0, 0.0]]))
                                   ]
                        )

        pot = DFTBPotential(minimize_template=self.test_data_path+"/dftb_in.hsd", energy_template="NONE", work_dir=".",
                            run_string="/home/john/.local/bin//dftb+")
        clus2 = pot.minimize(clus1, dir_name=testdir)

        print(clus2.cost)

        coords, mol_ids, atom_names = clus2.get_particle_positions()

        self.assertListEqual(atom_names, ["H", "H", "O", "H", "H", "O"])

        if os.path.exists(testdir):
            shutil.rmtree(testdir)
예제 #5
0
    def get_minima(self) -> List[Cluster]:
        """

        Returns:
            A list of all the Minimum objects in the database ordered by energy

        """
        session = self.get_session()
        clusters = session.query(Minimum).order_by(Minimum.cost)
        return [Cluster(db_cluster=c) for c in clusters]
예제 #6
0
    def get_global_minimum(self) -> Cluster:
        """
        Returns
        -------
        The Cluster object with lowest energy in the database

        """
        session = self.get_session()
        cluster = session.query(Minimum).order_by(Minimum.cost).first()
        # clusters = sorted(clusters, key=lambda x: x.cost)
        return Cluster(db_cluster=cluster)
예제 #7
0
    def minimize(self, cluster: Cluster, dir_name=None, *args, **kwargs) -> Cluster:
        """
        
        Args:
            cluster: 
            dir_name: 
            *args: 
            **kwargs: 

        Returns:

        """  # TODO finish docstring
        dir_name = dir_name or os.path.abspath(self.get_directory())

        copyfile(self.minimize_template, dir_name+"/dftb_in.hsd")

        coords, molecule_ids, atom_labels = cluster.get_particle_positions()

        if self.particle_types is None:
            self.particle_types = []
            self.particle_names = []

            for l in atom_labels:
                if l not in self.particle_names:
                    self.particle_names.append(l)
                self.particle_types.append(self.particle_names.index(l))
            self.natoms = len(self.particle_types)

        inp_gen_fname = dir_name + "/genfile.gen"
        inp_fname = self.minimize_template
        out_fname = dir_name+"/output.out"
        out_coords_fname = dir_name+"/geom.out.xyz"

        struct_dict = {"coords": coords,
                       "names": self.particle_names,
                       "types": self.particle_types,
                       "Natoms": self.natoms}

        self.write_gen(inp_gen_fname, struct_dict)

        with open(out_fname, "w") as outf:
            commands = [self.run_string, inp_fname]
            dftb_process = subprocess.Popen(commands, cwd=dir_name, shell=True, stdout=outf, stderr=outf)

        exit_code = dftb_process.wait()


        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.info(f"DFTB+ exited successfully. Exit code: {exit_code}")

        energy, coords = self.parse_dftbp_output(out_fname, out_coords_fname, **kwargs)

        # print(f"\n\nEnergy: {energy},\n\n coords:\n{coords}\n\nlabels:\n{atom_labels}\n\n")


        cluster.set_particle_positions((coords, molecule_ids, atom_labels))

        cluster.cost = float(energy)

        return cluster