def launch_opt_jobs(self): """ Mimicing DihedralScanner.launch_opt_jobs, """ assert hasattr(self, 'next_jobs') and hasattr( self, 'current_finished_job_results') while len(self.opt_queue) > 0: m, from_grid_id, to_grid_id = self.opt_queue.pop() # check if this job already exists m_geo_key = get_geo_key(m.xyzs[0]) if m_geo_key in self.task_cache[to_grid_id]: final_geo, final_energy, final_gradient, job_folder = self.task_cache[ to_grid_id][m_geo_key] result_m = Molecule() result_m.elem = list(m.elem) result_m.xyzs = [final_geo] result_m.qm_energies = [final_energy] if final_gradient is not None: result_m.qm_grads = [final_gradient] result_m.build_topology() grid_id = self.get_dihedral_id(result_m, check_grid_id=to_grid_id) if grid_id is None: print( f"Cached result from {job_folder} is ignored because optimized geometry is far from grid id {to_grid_id}" ) else: self.current_finished_job_results.push((result_m, grid_id), priority=job_folder) else: # append the job to self.next_jobs, which is the output of torsiondrive-API self.next_jobs[to_grid_id].append(m.xyzs[0].copy())
def launch_opt_jobs(self): """ Launch constrained optimizations for molecules in opt_queue Tasks current opt_queue will be popped in order. If a task exist in self.task_cache, the cached result will be checked, then put into self.current_finished_job_results Else, the task will be launched by self.launch_constrained_opt, and information is saved as self.running_job_path_info[job_path] = m, from_grid_id, to_grid_id """ assert hasattr(self, 'running_job_path_info') and hasattr( self, 'current_finished_job_results') while len(self.opt_queue) > 0: m, from_grid_id, to_grid_id = self.opt_queue.pop() # check if this job already exists m_geo_key = get_geo_key(m.xyzs[0]) if m_geo_key in self.task_cache[to_grid_id]: final_geo, final_energy, final_gradient, job_folder = self.task_cache[ to_grid_id][m_geo_key] result_m = Molecule() result_m.elem = list(m.elem) result_m.xyzs = [final_geo] result_m.qm_energies = [final_energy] if final_gradient is not None: result_m.qm_grads = [final_gradient] result_m.build_topology() grid_id = self.get_dihedral_id(result_m, check_grid_id=to_grid_id) if grid_id is None: print( f"Cached result from {job_folder} is ignored because optimized geometry is far from grid id {to_grid_id}" ) else: self.current_finished_job_results.push((result_m, grid_id), priority=job_folder) #self.grid_status[to_grid_id].append((m.xyzs[0], final_geo, final_energy)) else: job_path = self.launch_constrained_opt(m, to_grid_id) self.running_job_path_info[ job_path] = m, from_grid_id, to_grid_id
def finish(self): """ Write qdata.txt and scan.xyz file based on converged scan results """ m = Molecule() m.elem = list(self.engine.M.elem) m.qm_energies, m.xyzs, m.comms = [], [], [] # optionally writing qm gradients into qdata.txt if avilable writing_gradients = False if len(self.grid_final_gradients) == len(self.grid_final_geometries): m.qm_grads = [] writing_gradients = True # only print grid with energies for gid in sorted(self.grid_energies.keys()): m.qm_energies.append(self.grid_energies[gid]) m.xyzs.append(self.grid_final_geometries[gid]) if writing_gradients: m.qm_grads.append(self.grid_final_gradients[gid]) m.comms.append("Dihedral %s Energy %.9f" % (str(gid), self.grid_energies[gid])) m.write('qdata.txt') print( f"Final scan energies{' and gradients' if writing_gradients else ''} are written to qdata.txt" ) m.write('scan.xyz') print("Final scan energies are written to scan.xyz")