def test_conformer(self, conf): """ Test whether a conformer has the same bond matrix as the original structure. Returns the conformer object and -1 if not yet finished, 0 if same, and 1 if not. """ add = '' if self.semi_emp: add = 'semi_emp_' job = self.get_job_name(conf, add=add) status, geom = self.qc.get_qc_geom(job, self.species.natom) if status == 1: # still running return np.zeros((self.species.natom, 3)), -1 elif status == -1: # conformer search failed return np.zeros((self.species.natom, 3)), 1 else: # check if all the bond lenghts are withing 10% of the original bond lengths temp = StationaryPoint('temp', self.species.charge, self.species.mult, atom=self.species.atom, geom=geom) temp.bond_mx() if geometry.equal_geom(self.species, temp, 0.10): return geom, 0 else: return np.zeros((self.species.natom, 3)), 1
def test_hir(self): for rotor in range(len(self.species.dihed)): for ai in range(self.nrotation): success = None if self.hir_status[rotor][ai] == -1: if self.species.wellorts: job = 'hir/' + self.species.name + '_hir_' + str( rotor) + '_' + str(ai).zfill(2) else: job = 'hir/' + str( self.species.chemid) + '_hir_' + str( rotor) + '_' + str(ai).zfill(2) err, geom = self.qc.get_qc_geom(job, self.species.natom) if err == 1: # still running continue elif err == -1: # failed success = -1 else: # check if all the bond lenghts are within # 15% or the original bond lengths temp = StationaryPoint('temp', self.species.charge, self.species.mult, atom=self.species.atom, geom=geom) temp.bond_mx() if geometry.equal_geom(self.species, temp, 0.15): err, energy = self.qc.get_qc_energy(job) if ai == 0: success = 1 # cut off barriers above 20 kcal/mol to prevent the Fourier fit to oscillate elif (energy - self.hir_energies[rotor][0] ) < 20. / constants.AUtoKCAL: success = 1 else: success = -1 else: success = -1 if success == 1: err, energy = self.qc.get_qc_energy(job) self.hir_status[rotor][ai] = 0 self.hir_energies[rotor][ai] = energy self.hir_geoms[rotor][ai] = geom elif success == -1: logging.warning( "Hindered rotor optimization not successful for {}". format(job)) self.hir_status[rotor][ai] = 1 self.hir_energies[rotor][ai] = -1 self.hir_geoms[rotor][ai] = geom return 0
def test_ring_conformer(self, index): """ Test whether a conformer has the same bond matrix as the original structure. Returns the conformer object and -1 if not yet finished, 0 if same, and 1 if not. """ job = self.get_job_name(index, cyc=1) status, geom = self.qc.get_qc_geom(job, self.species.natom) if status == 1: # still running return np.zeros((self.species.natom, 3)), -1 elif status == -1: # conformer search failed logging.debug( 'Conformer search failed for scan point {}'.format(job)) return np.zeros((self.species.natom, 3)), 1 else: if self.start_ring_conformer_search(index, geom): logging.debug( 'Running the next dihedral for conformer {}'.format(job)) return geom, -1 else: # check if all the bond lenghts are withing 10% or the original bond lengths temp = StationaryPoint('temp', self.species.charge, self.species.mult, atom=self.species.atom, geom=geom) temp.bond_mx() if geometry.equal_geom(self.species, temp, 0.10): logging.debug( 'Successfullly finished conformer {}'.format(job)) return geom, 0 else: logging.debug( 'Conformer too far from original structure {}'.format( job)) return np.zeros((self.species.natom, 3)), 1
def do_optimization(self): while 1: # do the conformational search if self.par['conformer_search'] == 1: if self.scycconf == -1 and self.sconf == -1: logging.info('\tStarting conformational search of {}'.format(self.name)) self.species.confs = Conformers(self.species, self.par, self.qc) # first do the cyclic part of the molecule if self.scycconf == -1: # start the ring conf search if len(self.species.cycle_chain) > 0: # there are rings in the molecule, do a search self.species.confs.generate_ring_conformers(copy.deepcopy(self.species.geom)) # set the cyclic conf status to running self.scycconf = 0 else: # there are no rings in the molecule, continue from the current geometry self.species.confs.cyc_conf_geoms = [copy.deepcopy(self.species.geom)] # no ring conf search has to be done, set status to finished self.scycconf = 1 if self.scycconf == 0: # ring conf search is running, check if finished status, self.species.confs.cyc_conf_geoms = self.species.confs.check_ring_conformers() if status: # ring conf search is finished self.scycconf = 1 # first do an semi empirical optimization if requested by the user if self.par['semi_emp_conformer_search'] == 1: logging.info('semi empirical conformer search is starting for {}'.format(self.name)) if self.ssemi_empconf == -1: # semi empirical part has not started yet self.species.semi_emp_confs = Conformers(self.species, self.par, self.qc, semi_emp=1) for geom in self.species.confs.cyc_conf_geoms: # take all the geometries from the cyclic part # generate the conformers for the current geometry self.species.semi_emp_confs.generate_conformers(0, geom) # set conf status to running self.ssemi_empconf = 0 if self.ssemi_empconf == 0: # semi empirical conformational search is running # check if the conformational search is done status, lowest_conf, geom, self.semi_emp_low_energy, self.semi_emp_conformers, self.semi_emp_energies = self.species.semi_emp_confs.check_conformers(wait=self.wait) if status == 1: logging.info("semi empirical lowest energy conformer for species {} is number {}".format(self.name, lowest_conf)) # set conf status to finished self.ssemi_empconf = 1 else: self.ssemi_empconf = 1 if self.ssemi_empconf == 1 and self.scycconf == 1: # do open chain part if cyclic (if there were any) and semi empirical (if requested) parts are done if self.sconf == -1: # open chain part has not started yet # if semi empirical conformer were searched for, start from those, # else start from cyclic conformers if self.par['semi_emp_conformer_search'] == 1: self.species.confs.nconfs = 1 for i, geom in enumerate(self.semi_emp_conformers): if (self.semi_emp_energies[i] - self.semi_emp_low_energy) * constants.AUtoKCAL < self.par['semi_emp_confomer_threshold']: self.species.confs.generate_conformers(-999, geom) logging.info("There are {} structures below the {} kcal/mol threshold for species {} in the semiempirical search.". \ format(i, self.par['semi_emp_confomer_threshold'], self.name)) else: print_warning = True for geom in self.species.confs.cyc_conf_geoms: # take all the geometries from the cyclic part # generate the conformers for the current geometry self.skip_conf_check = self.species.confs.generate_conformers(0, geom, print_warning=print_warning) print_warning = False # set conf status to running self.sconf = 0 if self.sconf == 0: # conformational search is running # check if the conformational search is done if self.skip_conf_check == 0: status, lowest_conf, geom, low_energy, conformers, energies = self.species.confs.check_conformers(wait=self.wait) if status == 1: logging.info("lowest energy conformer for species: {} is number {}".format(self.name, lowest_conf)) # save lowest energy conformer as species geometry self.species.geom = geom # save lowest energy conformer energy self.species.energy = low_energy # set conf status to finished self.sconf = 1 elif self.skip_conf_check == 1: self.species.geom, self.species.energy = self.species.confs.lowest_conf_info() logging.info('Conformers are not checked for {} to speed up calculations.'.format(self.name)) logging.info('They seem to have been done in a previous run.') logging.info('Energy and geometry updated based on conf/{}_low file.'.format(self.name)) self.sconf = 1 else: # no conf search necessary, set status to finished self.sconf = 1 if self.sconf == 1: # conf search is finished # if the conformers were already done in a previous run # not clear what the purpose of these lines were if self.par['conformer_search'] == 1: status, lowest_conf, geom, low_energy, conformers, energies = self.species.confs.check_conformers(wait=self.wait) while self.restart <= self.max_restart: # do the high level calculations if self.par['high_level'] == 1: if self.shigh == -1: if self.species.wellorts: name = self.species.name else: name = self.species.chemid # high level calculation did not start yet logging.info('\tStarting high level optimization of {}'.format(name)) if self.species.wellorts: # do the high level optimization of a ts self.qc.qc_opt_ts(self.species, self.species.geom, high_level=1) else: # do the high level optimization of a well self.qc.qc_opt(self.species, self.species.geom, high_level=1) self.shigh = 0 # set the high status to running if self.shigh == 0: # high level calculation is running # check if it is finished status = self.qc.check_qc(self.job_high) if status == 'error': # found an error logging.info('\tHigh level optimization failed for {}'.format(self.name)) self.shigh = -999 elif status == 'normal': # finished successfully err, new_geom = self.qc.get_qc_geom(self.job_high, self.species.natom, wait=self.wait) temp = StationaryPoint('temp', self.species.charge, self.species.mult, atom=self.species.atom, geom=new_geom) temp.bond_mx() if self.species.wellorts: # for TS we need reasonable geometry agreement and normal mode correlation if self.par['conformer_search'] == 0: fr_file = self.fr_file_name(0) # name of the original TS file else: if self.skip_conf_check == 0: fr_file = 'conf/{}_{}'.format(self.fr_file_name(0), lowest_conf) else: fr_file = 'conf/{}_low'.format(self.fr_file_name(0)) if self.qc.qc == 'gauss': imagmode = reader_gauss.read_imag_mode(fr_file, self.species.natom) fr_file = self.fr_file_name(1) if self.qc.qc == 'gauss': imagmode_high = reader_gauss.read_imag_mode(fr_file, self.species.natom) # either geom is roughly same with closely matching imaginary modes, or geometry is very close # maybe we need to do IRC at the high level as well... same_geom = ((geometry.matrix_corr(imagmode, imagmode_high) > 0.9) and \ (geometry.equal_geom(self.species, temp, 0.3))) \ or (geometry.equal_geom(self.species, temp, 0.15)) else: same_geom = geometry.equal_geom(self.species, temp, 0.1) err, fr = self.qc.get_qc_freq(self.job_high, self.species.natom) if self.species.natom == 1: freq_ok = 1 elif len(fr) == 1 and fr[0] == 0: freq_ok = 0 elif self.species.wellorts == 0 and fr[0] > 0.: freq_ok = 1 elif self.species.wellorts == 1 and fr[0] < 0. and fr[1] > 0.: freq_ok = 1 else: freq_ok = 0 if same_geom and freq_ok: # geometry is as expected and normal modes are the same for TS err, self.species.geom = self.qc.get_qc_geom(self.job_high, self.species.natom) err, self.species.energy = self.qc.get_qc_energy(self.job_high) err, self.species.freq = self.qc.get_qc_freq(self.job_high, self.species.natom) err, self.species.zpe = self.qc.get_qc_zpe(self.job_high) self.shigh = 1 else: # geometry diverged to other structure if not same_geom: logging.info('\tHigh level optimization converged to different structure for {}, related channels are deleted.'.format(self.name)) if not freq_ok: logging.info('\tWrong number of imaginary frequencies for {}, related channels are deleted.'.format(self.name)) self.shigh = -999 else: # no high-level calculations necessary, set status to finished self.shigh = 1 if self.shigh == 1: # do the HIR calculation if self.par['rotor_scan'] == 1: if self.shir == -1: # hir not stated yet logging.info('\tStarting hindered rotor calculations of {}'.format(self.name)) self.species.hir = HIR(self.species, self.qc, self.par) self.species.hir.generate_hir_geoms(copy.deepcopy(self.species.geom), self.par['rigid_hir']) self.shir = 0 if self.shir == 0: # hir is running # check if it is done: status = self.species.hir.check_hir(wait=self.wait) if status: if len(self.species.hir.hir_energies) > 0: # check if along the hir potential a structure was found with a lower energy min = self.species.hir.hir_energies[0][0] min_rotor = -1 min_ai = -1 for rotor in range(len(self.species.dihed)): for ai in range(self.species.hir.nrotation): # use a 0.1kcal/mol cutoff for numerical noise if self.species.hir.hir_energies[rotor][ai] < min - 1.6E-4: min = self.species.hir.hir_energies[rotor][ai] min_rotor = rotor min_ai = ai if min_rotor > -1: self.restart += 1 if self.restart < self.max_restart: # lower energy structure found logging.info('\t\tLower energy found during hindered rotor scan for {}'.format(self.name)) logging.info('\t\tRestart number: ' + str(self.restart)) logging.info('\t\tRotor: ' + str(min_rotor)) logging.info('\t\tScan point: ' + str(min_ai)) job = self.job_hir + str(min_rotor) + '_' + str(min_ai).zfill(2) err, self.species.geom = self.qc.get_qc_geom(job, self.species.natom) # delete the high_level log file and the hir log files if os.path.exists(self.job_high + '.log'): # logging.info("\t\t\tRemoving file " + self.job_high + '.log') os.remove(self.job_high + '.log') for rotor in range(len(self.species.dihed)): for ai in range(self.species.hir.nrotation): if os.path.exists(self.job_hir + str(rotor) + '_' + str(ai).zfill(2) + '.log'): # logging.info("\t\t\tRemoving file " + self.job_hir + str(rotor) + '_' + str(ai).zfill(2) + '.log') os.remove(self.job_hir + str(rotor) + '_' + str(ai).zfill(2) + '.log') # set the status of high and hir back to not started self.shigh = -1 self.shir = -1 else: logging.info('\t\tLower energy found, but readched max restart for {}'.format(self.name)) self.shir = 1 else: self.shir = 1 else: self.shir = 1 else: # no hir calculations necessary, set status to finished self.shir = 1 if not self.wait or self.shir == 1 or self.shigh == -999: # break the loop if no waiting is required or # if the hir calcs are done or # if the high level calc failed break else: time.sleep(1) if self.shir == 1: # finalize if no waiting is required or if the hir calcs are done # calculate the symmetry numbers symmetry.calculate_symmetry(self.species) # calculate the new frequencies with the internal rotations projected out fr_file = self.name if not self.species.wellorts: fr_file += '_well' if self.par['high_level']: fr_file += '_high' fr_file = self.fr_file_name(self.par['high_level']) hess = self.qc.read_qc_hess(fr_file, self.species.natom) self.species.kinbot_freqs, self.species.reduced_freqs = frequencies.get_frequencies(self.species, hess, self.species.geom) # write the molpro input and read the molpro energy, if available if self.par['L3_calc'] == 1: if self.par['single_point_qc'] == 'molpro': molp = Molpro(self.species, self.par) if 'barrierless_saddle' in self.name: key = self.par['barrierless_saddle_single_point_key'] molp.create_molpro_input(bls=1) else: key = self.par['single_point_key'] molp.create_molpro_input() molp.create_molpro_submit() status, molpro_energy = molp.get_molpro_energy(key) # FIXME this might be wrong here: if status: self.species.energy = molpro_energy self.delete_files() if self.wait: if self.shir == 1 or self.shigh == -999: return 0 time.sleep(1) else: return 0