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