def generate_conformers_random_sampling(self, ini_cart): """ Generate a random sampling of each dihedral for a number nconfs of conformers """ for ni in range(self.nconfs): cart = copy.deepcopy(ini_cart) if ni == 0: sample = [0. for di in self.species.conf_dihed] else: sample = [ random.choice([0., 120., 240.]) for di in self.species.conf_dihed ] for rotor in range(len(self.species.conf_dihed)): zmat_atom, zmat_ref, zmat, zmatorder = zmatrix.make_zmat_from_cart( self.species, rotor, cart, 1) zmat[3][2] += sample[rotor] for i in range(4, self.species.natom): if zmat_ref[i][2] == 4: zmat[i][2] += sample[rotor] if zmat_ref[i][2] == 1: zmat[i][2] += sample[rotor] cart = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) self.qc.qc_conf(self.species, cart, self.conf) self.conf += 1 return 0
def generate_conformers(self, rotor, cart): """ Generate guesses for all of the canonical conformers. This is a recursive routine to generate them. rotor: the rotor number in the order it was discovered """ if self.cyc_conf == 0: cycles = 1 else: cycles = self.cyc_conf theoretical_confs = np.power(3, len(self.species.conf_dihed)) * cycles if len(self.species.conf_dihed ) > self.max_dihed or theoretical_confs > self.nconfs: self.generate_conformers_random_sampling(cart) return 0 if rotor == len(self.species.conf_dihed): self.qc.qc_conf(self.species, cart, self.conf) self.conf += 1 return 0 cart = np.asarray(cart) zmat_atom, zmat_ref, zmat, zmatorder = zmatrix.make_zmat_from_cart( self.species, rotor, cart, 1) rotor += 1 cart0 = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) self.generate_conformers(rotor, cart0) zmat[3][2] += 120. for i in range(4, self.species.natom): if zmat_ref[i][2] == 4: zmat[i][2] += 120. if zmat_ref[i][2] == 1: zmat[i][2] += 120. cart1 = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) self.generate_conformers(rotor, cart1) zmat[3][2] += 120. for i in range(4, self.species.natom): if zmat_ref[i][2] == 4: zmat[i][2] += 120. if zmat_ref[i][2] == 1: zmat[i][2] += 120. cart2 = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) self.generate_conformers(rotor, cart2) return 0
def generate_hir_geoms(self, cart, rigid): """ Generate the initial geometries of the points along the scans """ # re-initialize the lists in case of a restart of the HIR scans self.hir_status = [] self.hir_energies = [] self.hir_geoms = [] while len(self.hir_status) < len(self.species.dihed): self.hir_status.append([-1 for i in range(self.nrotation)]) self.hir_energies.append([-1 for i in range(self.nrotation)]) self.hir_geoms.append([[] for i in range(self.nrotation)]) for rotor in range(len(self.species.dihed)): if skip_rotor(self.species.name, self.species.dihed[rotor]) == 1: self.hir_status[rotor] = [2 for i in range(self.nrotation)] logging.info('For {} rotor {} was skipped in HIR.'.format( self.species.name, rotor)) continue cart = np.asarray(cart) zmat_atom, zmat_ref, zmat, zmatorder = zmatrix.make_zmat_from_cart( self.species, rotor, cart, 0) # first element has same geometry # ( TODO: this shouldn't be recalculated) cart_new = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) fi = [(zi + 1) for zi in zmatorder[:4]] self.qc.qc_hir(self.species, cart_new, rotor, 0, [fi], rigid) for ai in range(1, self.nrotation): ang = 360. / float(self.nrotation) zmat[3][2] += ang for i in range(4, self.species.natom): if zmat_ref[i][2] == 4: zmat[i][2] += ang if zmat_ref[i][2] == 1: zmat[i][2] += ang cart_new = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) self.qc.qc_hir(self.species, cart_new, rotor, ai, [fi], rigid) return 0
def generate_hir_geoms(self, cart): """ Generate the initial geometries of the points along the scans """ # re-initialize the lists incase of a restart of the HIR scans self.hir_status = [] self.hir_energies = [] self.hir_geoms = [] while len(self.hir_status) < len(self.species.dihed): self.hir_status.append([-1 for i in range(self.nrotation)]) self.hir_energies.append([-1 for i in range(self.nrotation)]) self.hir_geoms.append([[] for i in range(self.nrotation)]) for rotor in range(len(self.species.dihed)): cart = np.asarray(cart) zmat_atom, zmat_ref, zmat, zmatorder = zmatrix.make_zmat_from_cart(self.species, rotor, cart, 0) # first element has same geometry # ( TODO: this shouldn't be recalculated) cart_new = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) fi = [(zi+1) for zi in zmatorder[:4]] self.qc.qc_hir(self.species, cart_new, rotor, 0, [fi]) for ai in range(1, self.nrotation): ang = 360. / float(self.nrotation) zmat[3][2] += ang for i in range(4, self.species.natom): if zmat_ref[i][2] == 4: zmat[i][2] += ang if zmat_ref[i][2] == 1: zmat[i][2] += ang cart_new = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) self.qc.qc_hir(self.species, cart_new, rotor, ai, [fi]) return 0
def generate_conformers(self, rotor, cart): """ Generate guesses for all of the canonical conformers. This is a recursive routine to generate them. rotor: the rotor number in the order it was discovered """ if len(self.species.conf_dihed) > self.max_dihed: self.generate_conformers_random_sampling(cart) return 0 if rotor == len(self.species.conf_dihed): self.qc.qc_conf(self.species, cart, self.conf) self.conf += 1 return 0 cart = np.asarray(cart) zmat_atom, zmat_ref, zmat, zmatorder = zmatrix.make_zmat_from_cart(self.species, rotor, cart, 1) rotor += 1 cart0 = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) self.generate_conformers(rotor, cart0) zmat[3][2] += 120. for i in range(4, self.species.natom): if zmat_ref[i][2] == 4: zmat[i][2] += 120. if zmat_ref[i][2] == 1: zmat[i][2] += 120. cart1 = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) self.generate_conformers(rotor, cart1) zmat[3][2] += 120. for i in range(4, self.species.natom): if zmat_ref[i][2] == 4: zmat[i][2] += 120. if zmat_ref[i][2] == 1: zmat[i][2] += 120. cart2 = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) self.generate_conformers(rotor, cart2) return 0
def generate_conformers_random_sampling(self, ini_cart): """ Generate a random sampling of each dihedral for a number nconfs of conformers """ for ni in range(self.nconfs): cart = copy.deepcopy(ini_cart) if ni == 0: sample = [0. for di in self.species.conf_dihed] else: sample = [random.choice([0., 120., 240.]) for di in self.species.conf_dihed] for rotor in range(len(self.species.conf_dihed)): zmat_atom, zmat_ref, zmat, zmatorder = zmatrix.make_zmat_from_cart(self.species, rotor, cart, 1) zmat[3][2] += sample[rotor] for i in range(4, self.species.natom): if zmat_ref[i][2] == 4: zmat[i][2] += sample[rotor] if zmat_ref[i][2] == 1: zmat[i][2] += sample[rotor] cart = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) self.qc.qc_conf(self.species, cart, self.conf) self.conf += 1 return 0
def generate_conformers(self, rotor, cart, print_warning=False): """ Generate guesses for all of the canonical conformers. This is a recursive routine to generate them. rotor: the rotor number in the order it was discovered if -999, then just do a single calculation at the given geometry """ if self.cyc_conf == 0: cycles = 1 else: cycles = self.cyc_conf name = self.get_name() # what is the value of cycles # what is value of all things associated w/ conf generation # what is length of conf_dihed? theoretical_confs = np.power(self.grid, len( self.species.conf_dihed)) * cycles if rotor != -999: if len(self.species.conf_dihed ) > self.max_dihed or theoretical_confs > self.nconfs: if rotor == 0: if self.info: logging.info( 'Random conformer search is carried out for {}.'. format(name)) self.info = False # skipping generation if done if self.cyc_conf > 1: nrandconf = int(round(self.nconfs / self.cyc_conf) + 2) else: nrandconf = self.nconfs if os.path.exists('{}.log'.format( self.get_job_name(nrandconf - 1) )) and os.path.exists('conf/{}_low.log'.format(name)): rows = self.db.select( name=self.get_job_name(nrandconf - 1)) for row in rows: self.conf = nrandconf logging.info( 'Last conformer was found in kinbot.db, generation is skipped for {}.' .format(self.get_job_name(nrandconf))) logging.info( 'Make sure the files are correct, you can reactivate calcs by deleting the last log file.' ) return 1 self.generate_conformers_random_sampling(cart) return 0 # retraction from the recursion if rotor == len(self.species.conf_dihed) or rotor == -999: self.qc.qc_conf(self.species, cart, self.conf, semi_emp=self.semi_emp) if self.conf == 0: logging.info( 'Theoretical number of conformers is {} for {}.'.format( theoretical_confs, name)) self.conf += 1 return 0 # skipping generation if done if os.path.exists('{}.log'.format( self.get_job_name(theoretical_confs - 1))) and os.path.exists( 'conf/{}_low.log'.format(name)): rows = self.db.select(name=self.get_job_name(theoretical_confs - 1)) for row in rows: self.conf = theoretical_confs if print_warning: logging.info( 'Theoretical number of conformers is {} for {}.'. format(theoretical_confs, name)) logging.info( 'Last conformer was found in kinbot.db, generation is skipped for {}.' .format(name)) logging.info( 'Make sure the files are correct, you can reactivate calcs by deleting the last log file.' ) return 1 cart = np.asarray(cart) zmat_atom, zmat_ref, zmat, zmatorder = zmatrix.make_zmat_from_cart( self.species, rotor, cart, 1) rotor += 1 for gr in range(self.grid): zmat[3][2] += 360. / self.grid for i in range(4, self.species.natom): if zmat_ref[i][2] == 4: zmat[i][2] += 360. / self.grid if zmat_ref[i][2] == 1: zmat[i][2] += 360. / self.grid cartmod = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, self.species.natom, self.species.atom, zmatorder) self.generate_conformers(rotor, cartmod) return 0
def modify_coordinates(species, name, geom, changes, bond, write_files=0): """ Geom is the geometry (n x 3 matrix with n the number of atoms) in cartesian coordinates Changes is a list of lists, each list containing the coordinates and their new value (atom indices start at 0): To change a bond length: [atom1, atom2, bond_length] To change a bond angle: [neighbor1, central_atom, neighbor2, angle_in_degrees] To change a dihedral angle: [atom1, atom2, atom3, atom4, dihedarl_angle_in_degrees] Bond is the bond matrix of the molecule """ start_time = time.time() logging.debug('Starting coordinate modification for {}'.format(name)) logging.debug('Changes:') for c in changes: logging.debug('\t{}'.format('\t'.join([str(ci) for ci in c]))) step = 1 atoms_list = [] count = 0 fname = '{}_{}.xyz'.format(name, count) while os.path.exists(fname): count += 1 fname = '{}_{}.xyz'.format(name, count) f_out = None if write_files: f_out = open(fname, 'w') new_geom = copy.deepcopy(geom) step = append_geom(species.natom, step, 0., species.atom, new_geom, np.zeros((species.natom * 3)), atoms_list, f_out=f_out) # change dihedrals, if necessary for ci in changes: if len(ci) == 5: zmat_atom, zmat_ref, zmat, zmatorder = zmatrix.make_zmat_from_cart( species, ci[:-1], new_geom, 2) # write_zmat(zmat_atom, zmat_ref, zmat, new_geom, species.atom) orig_dih = zmat[3][2] new_dih = ci[-1] dih_diff = new_dih - orig_dih zmat[3][2] += dih_diff for i in range(4, species.natom): if zmat_ref[i][2] == 4: zmat[i][2] += dih_diff if zmat_ref[i][2] == 1: zmat[i][2] += dih_diff new_geom = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, species.natom, species.atom, zmatorder) # write_zmat(zmat_atom, zmat_ref, zmat, new_geom, species.atom) step = append_geom(species.natom, step, 0., species.atom, new_geom, np.zeros((species.natom * 3)), atoms_list, f_out=f_out) # change angles, if necessary if len(ci) == 4: # original angle in radians orig_angle = geometry.calc_angle(new_geom[ci[0]], new_geom[ci[1]], new_geom[ci[2]]) new_angle = np.radians(ci[-1]) # new angle in radians v1 = new_geom[ci[0]] - new_geom[ci[1]] v2 = new_geom[ci[2]] - new_geom[ci[1]] rot_ax = [0., 0., 0.] # create a vector perpendicular to v1 and v2 # verify if points are collinear if np.linalg.norm(np.cross(v1, v2)) == 0: # rotate around any axis perpendicular to the axis along the three points: if v1[0] != 0 or v1[1] != 0: rot_ax = [v1[1], -v1[0], 0.] elif v1[0] != 0 or v1[2] != 0: rot_ax = [v1[2], 0., -v1[0]] else: rot_ax = [1., 0., 0.] else: rot_ax = np.cross(v1, v2) rot_ax = rot_ax / np.linalg.norm(rot_ax) # rotate all the atoms on the side of the last atom st, ats, ats2 = divide_atoms(ci[2], ci[1], bond, species.natom, species.atom) if not st: break for atj in ats: new_geom[atj] = perform_rotation(new_geom[atj], new_geom[ci[1]], rot_ax, new_angle - orig_angle) step = append_geom(species.natom, step, 1., species.atom, new_geom, np.zeros((species.natom * 3)), atoms_list, f_out=f_out) coords = get_coords(species, bond, new_geom, changes, 0) # optimize the geometry to meet the coords list x0 = np.reshape(new_geom, 3 * species.natom) cost_fct = cost_function(coords) logging.debug('Starting BFGS') gs = '' # initial geomtry string for i, at in enumerate(species.atom): x, y, z = new_geom[i] gs += '{}, {:.8f}, {:.8f}, {:.8f}, \n'.format(at, x, y, z) logging.debug("For the following initial geometry:\n" + gs) opt = bfgs.BFGS() x_opt, x_i, g_i = opt.optimize(cost_fct, x0) new_geom = np.reshape(x_opt, (species.natom, 3)) for i, xi in enumerate(x_i): geomi = np.reshape(xi, (species.natom, 3)) gradi = np.reshape(g_i[i], (species.natom, 3)) step = append_geom(species.natom, step, 2., species.atom, geomi, gradi, atoms_list, f_out=f_out) if write_files: write(fname.replace('.xyz', '.traj'), atoms_list) f_out.close() success = control_changes(species, name, geom, new_geom, changes, bond) end_time = time.time() logging.debug( 'Finished coordinate changes after {:.2f} seconds'.format(end_time - start_time)) return success, new_geom
def modify_coordinates(species, name, geom, changes, bond, write_files=0): """ Geom is the geometry (n x 3 matrix with n the number of atoms) in cartesian coordinates Changes is a list of lists, each list containing the coordinates and their new value (atom indices start at 0): To change a bond length: [atom1, atom2, bond_length] To change a bond angle: [neighbor1, central_atom, neighbor2, angle_in_degrees] To change a dihedral angle: [atom1, atom2, atom3, atom4, dihedarl_angle_in_degrees] Bond is the bond matrix of the molecule """ start_time = time.time() logging.debug('Starting coordinate modification for {}'.format(name)) logging.debug('Changes:') for c in changes: logging.debug('\t{}'.format('\t'.join([str(ci) for ci in c]))) step = 1 atoms_list = [] count = 0 fname = '{}_{}.xyz'.format(name, count) while os.path.exists(fname): count += 1 fname = '{}_{}.xyz'.format(name, count) f_out = None if write_files: f_out = open(fname, 'w') new_geom = copy.deepcopy(geom) step = append_geom(species.natom, step, 0., species.atom, new_geom, np.zeros((species.natom*3)), atoms_list, f_out=f_out) # change dihedrals, if necessary for ci in changes: if len(ci) == 5: zmat_atom, zmat_ref, zmat, zmatorder = zmatrix.make_zmat_from_cart(species, ci[:-1], new_geom, 2) # write_zmat(zmat_atom, zmat_ref, zmat, new_geom, species.atom) orig_dih = zmat[3][2] new_dih = ci[-1] dih_diff = new_dih - orig_dih zmat[3][2] += dih_diff for i in range(4, species.natom): if zmat_ref[i][2] == 4: zmat[i][2] += dih_diff if zmat_ref[i][2] == 1: zmat[i][2] += dih_diff new_geom = zmatrix.make_cart_from_zmat(zmat, zmat_atom, zmat_ref, species.natom, species.atom, zmatorder) # write_zmat(zmat_atom, zmat_ref, zmat, new_geom, species.atom) step = append_geom(species.natom, step, 0., species.atom, new_geom, np.zeros((species.natom*3)), atoms_list, f_out=f_out) # change angles, if necessary if len(ci) == 4: # original angle in radians orig_angle = geometry.calc_angle(new_geom[ci[0]], new_geom[ci[1]], new_geom[ci[2]]) new_angle = np.radians(ci[-1]) # new angle in radians v1 = new_geom[ci[0]] - new_geom[ci[1]] v2 = new_geom[ci[2]] - new_geom[ci[1]] rot_ax = [0., 0., 0.] # create a vector perpendicular to v1 and v2 # verify if points are collinear if np.linalg.norm(np.cross(v1, v2)) == 0: # rotate around any axis perpendicular to the axis along the three points: if v1[0] != 0 or v1[1] != 0: rot_ax = [v1[1], -v1[0], 0.] elif v1[0] != 0 or v1[2] != 0: rot_ax = [v1[2], 0., -v1[0]] else: rot_ax = [1., 0., 0.] else: rot_ax = np.cross(v1, v2) rot_ax = rot_ax/np.linalg.norm(rot_ax) # rotate all the atoms on the side of the last atom st, ats, ats2 = divide_atoms(ci[2], ci[1], bond, species.natom, species.atom) if not st: break for atj in ats: new_geom[atj] = perform_rotation(new_geom[atj], new_geom[ci[1]], rot_ax, new_angle-orig_angle) step = append_geom(species.natom, step, 1., species.atom, new_geom, np.zeros((species.natom*3)), atoms_list, f_out=f_out) coords = get_coords(species, bond, new_geom, changes, 0) # optimize the geometry to meet the coords list x0 = np.reshape(new_geom, 3*species.natom) cost_fct = cost_function(coords) logging.debug('Starting BFGS') gs = '' # initial geomtry string for i, at in enumerate(species.atom): x, y, z = new_geom[i] gs += '{}, {:.8f}, {:.8f}, {:.8f}, \n'.format(at, x, y, z) logging.debug("For the following initial geometry:\n" + gs) opt = bfgs.BFGS() x_opt, x_i, g_i = opt.optimize(cost_fct, x0) new_geom = np.reshape(x_opt, (species.natom, 3)) for i, xi in enumerate(x_i): geomi = np.reshape(xi, (species.natom, 3)) gradi = np.reshape(g_i[i], (species.natom, 3)) step = append_geom(species.natom, step, 2., species.atom, geomi, gradi, atoms_list, f_out=f_out) if write_files: write(fname.replace('.xyz', '.traj'), atoms_list) f_out.close() success = control_changes(species, name, geom, new_geom, changes, bond) end_time = time.time() logging.debug('Finished coordinate changes after {:.2f} seconds'.format(end_time - start_time)) return success, new_geom