def _set_sort_info_lst(sort_str, thy_dct, spc_info): """ Return the levels to sort conformers by if zpve or sp levels were assigned in input if we ask for zpe(lvl_wbs),sp(lvl_b2t),gibbs(700) out sort_info_lst will be [('gaussian', 'wb97xd', '6-31*', 'RU'), ('gaussian', 'b2plypd3', 'cc-pvtz', 'RU'), None, None, 700.] """ sort_lvls = [None, None, None, None, None] sort_typ_lst = ['freqs', 'sp', 'enthalpy', 'entropy', 'gibbs'] if sort_str is not None: for sort_param in sort_str.split(','): idx = None for typ_idx, typ_str in enumerate(sort_typ_lst): if typ_str in sort_param: lvl_key = sort_str.split(typ_str + '(')[1].split(')')[0] idx = typ_idx if idx is not None: if idx < 2: method_dct = thy_dct.get(lvl_key) if method_dct is None: ioprinter.warning_message( f'no {lvl_key} in theory.dat, ' f'not using {sort_typ_lst[idx]} in sorting') continue thy_info = tinfo.from_dct(method_dct) mod_thy_info = tinfo.modify_orb_label(thy_info, spc_info) sort_lvls[idx] = mod_thy_info else: sort_lvls[idx] = float(lvl_key) return sort_lvls
def saddle_point_checker(imags): """ run things for checking Hessian """ big_imag, kick_imag = 0, 0 ioprinter.checking('the imaginary frequencies of the saddle point...') if len(imags) < 1: ioprinter.warning_message('No imaginary modes for geometry') status = 'fail' else: if len(imags) > 1: ioprinter.warning_message('More than one imaginary mode for geometry') for idx, imag in enumerate(imags): if imag <= 50.0: ioprinter.warning_message('Mode {} {} cm-1 is low,'.format(str(idx+1), imag)) elif 50.0 < imag <= 200.0: lowstr = 'Mode {} {} cm-1 is low,'.format(str(idx+1), imag) ioprinter.warning_message(lowstr + 'need a kickoff procedure to remove') kick_imag += 1 else: ioprinter.debug_message('Mode {} {} cm-1 likely fine,'.format(str(idx+1), imag)) big_imag += 1 if big_imag > 1: ioprinter.warning_message('WARNING: More than one imaginary mode for geometry') if kick_imag >= 1: ioprinter.debug_message('Will kickoff to get saddle point') status = 'kickoff' elif big_imag == 1: status = 'success' return status
def read_locs_harmonic_freqs(cnf_fs, cnf_locs, run_prefix, zrxn=None): """ Read the harmonic frequencies for a specific conformer Do the freqs obtain for two species for fake and pst? """ if cnf_locs is not None: geo_exists = cnf_fs[-1].file.geometry.exists(cnf_locs) hess_exists = cnf_fs[-1].file.hessian.exists(cnf_locs) if not geo_exists: ioprinter.error_message( 'No Reference geometry for harmonic frequencies at path', cnf_fs[-1].file.hessian.path(cnf_locs)) if not hess_exists: ioprinter.error_message( 'No Hessian available for harmonic frequencies at path', cnf_fs[-1].file.hessian.path(cnf_locs)) else: geo_exists, hess_exists = False, False if geo_exists and hess_exists: # Obtain geom and freqs from filesys geo = cnf_fs[-1].file.geometry.read(cnf_locs) hess = cnf_fs[-1].file.hessian.read(cnf_locs) ioprinter.reading('Hessian', cnf_fs[-1].path(cnf_locs)) # Build the run filesystem using locs fml_str = automol.geom.formula_string(geo) vib_path = job_path(run_prefix, 'PROJROT', 'FREQ', fml_str) # Obtain the frequencies ioprinter.info_message( 'Calling ProjRot to diagonalize Hessian and get freqs...') script_str = autorun.SCRIPT_DCT['projrot'] freqs, _, imag_freqs, _ = autorun.projrot.frequencies( script_str, vib_path, [geo], [[]], [hess]) # Obtain the displacements norm_coord_str, _ = autorun.projrot.displacements( script_str, vib_path, [geo], [[]], [hess]) # Calculate the zpve ioprinter.frequencies(freqs) zpe = (sum(freqs) / 2.0) * phycon.WAVEN2EH # Check imaginary frequencies and set freqs if zrxn is not None: if len(imag_freqs) > 1: ioprinter.warning_message('Saddle Point has more than', 'one imaginary frequency') imag = imag_freqs[0] else: imag = None ret = (freqs, imag, zpe, norm_coord_str) else: ret = None return ret
def _active_space_dictionary(job_path): """ Reads any active-space templates provided by the user in the directory where other input files. The function extracts the mechanism name from a top-line comment in the template file. Currently, only Molpro templates are supported. :param job_path: directory path where all input files exist :type job_path: str :rtype: tuple(str) """ def _comment_name(aspace_str): """ read the species name from a comment line in template comment line in FIRST line of template of form ! {spc_name} """ comm_line = aspace_str.splitlines()[0] comm_line.replace('!', '').strip() return comm_line aspace_dct, path_dct = {}, {} _inp_paths = _inp_file_paths(job_path) if _inp_paths: for file_path, file_name in _inp_paths: if file_path.endswith('.asp'): aspace_str = ioformat.pathtools.read_file(file_path, file_name) spc_name = _comment_name(aspace_str) if spc_name in aspace_dct: warning_message( f'Dupilicate active space geometry for {spc_name}') aspace_dct[spc_name] = aspace_str path_dct[spc_name] = file_name return aspace_dct, path_dct
def _geometry_dictionary(job_path): """ Reads any .xyz files provided by the user in the directory where other input files. The function extracts the mechanism name of the species/transition-state from the comment line of the .xyz file and indexes each .xyz string with this name. :param job_path: directory path where all input files exist :type job_path: str :rtype: dict[str: str] """ geo_dct, path_dct = {}, {} _inp_paths = _inp_file_paths(job_path) if _inp_paths: for file_path, file_name in _inp_paths: if file_path.endswith('.xyz'): xyz_str = ioformat.pathtools.read_file(file_path, file_name) spc_name = automol.geom.comment_from_xyz_string(xyz_str) geo = automol.geom.from_xyz_string(xyz_str) if spc_name in geo_dct: warning_message(f'Dupilicate xyz geometry for {spc_name}') geo_dct[spc_name] = geo path_dct[spc_name] = file_name return geo_dct, path_dct
def _reac_sep_ene(rct_info, sp_thy_info, rcts_cnf_fs, run_prefix, overwrite, sp_script_str, **kwargs): """ Determine the sum of electronic energies of two reactants specified at the level of theory described in the theory info object. Will calculate the energy if it is not currently in the SAVE filesystem. """ # get the single reference energy for each of the reactant configurations spc_enes = [] for (run_fs, save_fs, mlocs, mpath), inf in zip(rcts_cnf_fs, rct_info): # Set the modified thy info mod_sp_thy_info = tinfo.modify_orb_label(sp_thy_info, inf) # Build filesys zma_fs = autofile.fs.zmatrix(mpath) # Read the geometry and set paths zma = zma_fs[-1].file.zmatrix.read([0]) geo = save_fs[-1].file.geometry.read(mlocs) # Build the single point filesys objects sp_save_fs = autofile.fs.single_point(mpath) # Calculate the save single point energy sp.run_energy(zma, geo, inf, mod_sp_thy_info, run_fs, save_fs, mlocs, run_prefix, sp_script_str, overwrite, **kwargs) exists = sp_save_fs[-1].file.energy.exists(mod_sp_thy_info[1:4]) if not exists: ioprinter.warning_message('No ene found') ene = None else: ene = sp_save_fs[-1].file.energy.read(mod_sp_thy_info[1:4]) # Append ene to list spc_enes.append(ene) # Analyze the energies in the list inf_ene = 0.0 for ene, inf in zip(spc_enes, rct_info): if ene is not None: inf_ene += ene else: ioprinter.error_message( 'Single reference energy job fails', f'for {inf}: ', 'Energy needed to evaluate infinite separation energy') inf_ene = None if inf_ene is not None: ioprinter.info_message(f'Reactant Energy [au]: {inf_ene}') return inf_ene
def set_model_filesys(spc_dct_i, level, run_prefix, save_prefix, saddle, name=None, cnf_range='min', spc_locs=None, nprocs=1): """ Gets filesystem objects for reading many calculations """ # Set the spc_info if saddle: rxn_info = spc_dct_i['rxn_info'] spc_info = rinfo.ts_info(rxn_info) else: spc_info = sinfo.from_dct(spc_dct_i) levelp = tinfo.modify_orb_label(level, spc_info) _root = root_locs(spc_dct_i, saddle=saddle, name=name) cnf_run_fs, cnf_save_fs = build_fs(run_prefix, save_prefix, 'CONFORMER', thy_locs=levelp[1:], **_root) hbond_cutoffs = spc_dct_i['hbond_cutoffs'] if cnf_range == 'specified': min_rngs_locs = spc_locs min_rngs_path = cnf_save_fs[-1].path(min_rngs_locs) cnf_run_fs[-1].create(min_rngs_locs) elif cnf_range == 'min': min_rngs_locs, min_rngs_path = min_energy_conformer_locators( cnf_save_fs, levelp, hbond_cutoffs=hbond_cutoffs) cnf_run_fs[-1].create(min_rngs_locs) else: min_rngs_locs_lst, min_rngs_path_lst = conformer_locators( cnf_save_fs, levelp, cnf_range=cnf_range, hbond_cutoffs=hbond_cutoffs, nprocs=nprocs) for min_locs in min_rngs_locs_lst: cnf_run_fs[-1].create(min_locs) min_rngs_locs = min_rngs_locs_lst[0] min_rngs_path = min_rngs_path_lst[0] ioprinter.warning_message('Only returning first location in this list') # Create run fs if that directory has been deleted to run the jobs return [cnf_save_fs, min_rngs_path, min_rngs_locs, '', cnf_run_fs]
def _inchi_are_same(orig_ich, geo): """ Assess if a geometry has the same connectivity to saved geos evaluated in temrs of inchi """ same = False ich = automol.geom.inchi(geo) assert automol.inchi.is_complete(orig_ich), ( f'the inchi {orig_ich} orig_ich is not complete') if ich == orig_ich: same = True if not same: warning_message(f" - new inchi {ich} not the same as old {orig_ich}") return same
def electronic_energy(spc_dct_i, pf_filesystems, spc_model_dct_i, conf=None): """ get high level energy at low level optimized geometry """ ioprinter.info_message('- Calculating electronic energy') # spc_dct_i = spc_dct[spc_name] rxn_info = spc_dct_i.get('rxn_info', None) if rxn_info is not None: spc_info = rinfo.ts_info(rxn_info) else: spc_info = sinfo.from_dct(spc_dct_i) # Get the harmonic filesys information if conf: cnf_path = conf[1] else: [_, cnf_path, _, _, _] = pf_filesystems['harm'] # Get the electronic energy levels ene_levels = tuple(val[1] for key, val in spc_model_dct_i['ene'].items() if 'lvl' in key) print('ene levels', ene_levels) # Read the energies from the filesystem e_elec = None if os.path.exists(cnf_path): e_elec = 0.0 # ioprinter.info_message('lvls', ene_levels) for (coeff, level) in ene_levels: # Build SP filesys mod_thy_info = tinfo.modify_orb_label(level, spc_info) sp_save_fs = autofile.fs.single_point(cnf_path) sp_save_fs[-1].create(mod_thy_info[1:4]) # Read the energy sp_path = sp_save_fs[-1].path(mod_thy_info[1:4]) if os.path.exists(sp_path): ioprinter.reading('Energy', sp_path) ene = sp_save_fs[-1].file.energy.read(mod_thy_info[1:4]) e_elec += (coeff * ene) else: ioprinter.warning_message('No energy at path') e_elec = None break else: ioprinter.warning_message('No conformer to calculate the energy') return e_elec
def saddle_point_checker(imags): """ run things for checking Hessian """ big_imag, kick_imag = 0, 0 ioprinter.checking('the imaginary frequencies of the saddle point...') if len(imags) < 1: ioprinter.warning_message('No imaginary modes for geometry') status = 'fail' else: if len(imags) > 1: ioprinter.warning_message( 'More than one imaginary mode for geometry') status = 'fail' for idx, imag in enumerate(imags): if imag <= 50.0: ioprinter.warning_message( f'Mode {str(idx+1)} {imag} cm-1 is low,') elif 50.0 < imag <= 200.0: lowstr = f'Mode {str(idx+1)} {imag} cm-1 is low,' ioprinter.debug_message( lowstr + ' check mode and see if it should be corrected') big_imag += 1 # Adding to the kick counter kills code for good TSs # Some addditions of big species have low mode of this # ioprinter.warning_message( # lowstr + 'need a kickoff procedure to remove') # kick_imag += 1 else: ioprinter.debug_message( f'Mode {str(idx+1)} {imag} cm-1 likely fine') big_imag += 1 if big_imag > 1: ioprinter.warning_message( 'More than one imaginary mode for geometry') if kick_imag >= 1: ioprinter.debug_message('Will kickoff to get saddle point') status = 'kickoff' else: status = 'failure' elif big_imag == 1: status = 'success' elif big_imag == 0: status = 'failure' ioprinter.warning_message('Did not find any appropriate modes') return status
def _inchi_are_same(orig_ich, geo): """ Assess if a geometry has the same connectivity to saved geos evaluated in temrs of inchi """ same = False ich = automol.geom.inchi(geo) assert automol.inchi.is_complete(orig_ich), ( 'the inchi {} orig_ich is not complete'.format(orig_ich)) if ich == orig_ich: same = True if not same: ioprinter.warning_message( " - new inchi {} not the same as old {}".format(ich, orig_ich)) return same
def _is_proper_isomer(cnf_save_fs, zma): """ Check if geom is the same isomer as those in the filesys """ vma = automol.zmat.var_(zma) if cnf_save_fs[0].file.vmatrix.exists(): exist_vma = cnf_save_fs[0].file.vmatrix.read() if vma != exist_vma: ioprinter.warning_message( " - Isomer is not the same as starting isomer. Skipping...") proper_isomer = False else: proper_isomer = True else: proper_isomer = False return proper_isomer
def read_locs_harmonic_freqs(cnf_fs, harm_path, cnf_locs, run_prefix, zrxn=None): """ Read the harmonic frequencies for a specific conformer """ # probably should read freqs # Do the freqs obtain for two species for fake and pst if cnf_locs is not None: # Obtain geom and freqs from filesys geo = cnf_fs[-1].file.geometry.read(cnf_locs) hess = cnf_fs[-1].file.hessian.read(cnf_locs) ioprinter.reading('Hessian', cnf_fs[-1].path(cnf_locs)) # Build the run filesystem using locs fml_str = automol.geom.formula_string(geo) vib_path = job_path(run_prefix, 'PROJROT', 'FREQ', fml_str) # Obtain the frequencies ioprinter.info_message( 'Calling ProjRot to diagonalize Hessian and get freqs...') script_str = autorun.SCRIPT_DCT['projrot'] freqs, _, imag_freqs, _ = autorun.projrot.frequencies( script_str, vib_path, [geo], [[]], [hess]) # Calculate the zpve ioprinter.frequencies(freqs) zpe = (sum(freqs) / 2.0) * phycon.WAVEN2EH # Check imaginary frequencies and set freqs if zrxn is not None: if len(imag_freqs) > 1: ioprinter.warning_message('Saddle Point has more than', 'one imaginary frequency') imag = imag_freqs[0] else: imag = None else: ioprinter.error_message( 'Reference geometry is missing for harmonic frequencies') return freqs, imag, zpe
def _check_imaginary(spc_info, geo, mod_thy_info, run_fs, script_str, overwrite=False, **kwargs): """ check if species has an imaginary frequency """ # Initialize info has_imag = False norm_coords = [] hess = ((), ()) # Run Hessian calculation success, ret = es_runner.execute_job( job=elstruct.Job.HESSIAN, spc_info=spc_info, thy_info=mod_thy_info, geo=geo, run_fs=run_fs, script_str=script_str, overwrite=overwrite, **kwargs, ) # Check for imaginary modes if success: inf_obj, _, out_str = ret prog = inf_obj.prog hess = elstruct.reader.hessian(prog, out_str) # Calculate vibrational frequencies if hess: run_path = run_fs[-1].path([elstruct.Job.HESSIAN]) # run_path = run_fs[-1].path(['VIB']) script_str = autorun.SCRIPT_DCT['projrot'] _, _, imag_freq, _ = autorun.projrot.frequencies( script_str, run_path, [geo], [[]], [hess]) # Mode for now set the imaginary frequency check to -100: # Should decrease once freq projector functions properly if imag_freq: ioprinter.warning_message('Imaginary mode found:') norm_coords = elstruct.reader.normal_coordinates(prog, out_str) has_imag = True return has_imag, norm_coords
def _check_freqs(imags): """ Check the magnitude of the imaginary modes. """ big_imag, kick_imag = 0, 0 ioprinter.checking('the imaginary frequencies of the saddle point...') if len(imags) < 1: ioprinter.warning_message('No imaginary modes for geometry') status = 'fail' else: if len(imags) > 1: ioprinter.warning_message( 'More than one imaginary mode for geometry') status = 'fail' for idx, imag in enumerate(imags): if imag <= 50.0: ioprinter.warning_message(f'Mode {idx+1} {imag} cm-1 is low,') elif 50.0 < imag <= 200.0: lowstr = f'Mode {idx+1} {imag} cm-1 is low,' ioprinter.debug_message( lowstr + ' check mode and see if it should be corrected') big_imag += 1 # Adding to the kick counter kills code for good TSs # Some addditions of big species have low mode of this # ioprinter.warning_message( # lowstr + 'need a kickoff procedure to remove') # kick_imag += 1 else: ioprinter.debug_message( f'Mode {idx+1} {imag} cm-1 is likely fine,') big_imag += 1 if big_imag > 1: ioprinter.warning_message( 'More than one imaginary mode for geometry') if kick_imag >= 1: ioprinter.debug_message('Will kickoff to get saddle point') status = 'kick' else: status = False elif big_imag == 1: status = True elif big_imag == 0: status = False return status
def basis_energy(spc_name, spc_basis, uni_refs_dct, spc_dct, spc_model_dct_i, run_prefix, save_prefix, read_species=True): """ Reads the electronic and zero-point energies for a species and transition state and their constituent basis set. """ def _read_basis_energy(ich_name_dct, spc_dct, uni_refs_dct, spc_model_dct_i, run_prefix, save_prefix, ichs, output_queue=None): h_basis_dct = {} print(f'Process {os.getpid()} reading energy for species: {ichs}') for ich in ichs: name = ich_name_dct[ich] if name in spc_dct: spc_dct_i = spc_dct[name] prname = name elif name in uni_refs_dct: spc_dct_i = uni_refs_dct[name] prname = name if 'ts' in name or 'TS' in name: reacs, prods = ich.split('PRODS') reacs = reacs.replace('REACS', '') reacs = reacs.split('REAC') prods = prods.split('PROD') reac_lbl = 'r0' if len(reacs) > 1: reac_lbl += '+r1' prod_lbl = 'p0' if len(prods) > 1: prod_lbl += '+p1' ioprinter.info_message( f'Basis Reaction: {reac_lbl}={prod_lbl} 1 1 1 ') for i, reac in enumerate(reacs): ioprinter.info_message( f'r{i},{reac},{automol.inchi.smiles(reac)},1') for i, prod in enumerate(prods): ioprinter.info_message( f'p{i},{prod},{automol.inchi.smiles(prod)},1') ioprinter.debug_message('bases energies test:', ich, name) pf_filesystems = filesys.models.pf_filesys( spc_dct_i, spc_model_dct_i, run_prefix, save_prefix, saddle=('ts' in name or 'TS' in name), name=name) ioprinter.info_message(f'Calculating energy for basis {prname}...', newline=1) h_basis_dct[ich] = read_energy(spc_dct_i, pf_filesystems, spc_model_dct_i, run_prefix, read_ene=True, read_zpe=True, saddle='ts' in name or 'TS' in name) output_queue.put((h_basis_dct, )) # Initialize ich name dct to noe ich_name_dct = {} for ich in spc_basis: if isinstance(ich, str): ich_name_dct[ich] = None else: ich_name_dct[_ich_key_name(ich)] = None # Get names of the basis species from the respective spc dcts for ich in spc_basis: for name in spc_dct: if name != 'global' and 'ts' not in name: if ich == spc_dct[name]['inchi']: ich_name_dct[ich] = name elif name != 'global': if 'reacs' in spc_dct[name]: if _ich_in_rxn(ich, spc_dct[name]): ich_name_dct[_ich_key_name(ich)] = name for name in uni_refs_dct: if 'TS' in name: ioprinter.info_message(uni_refs_dct[name]['reacs']) if _ich_in_rxn(ich, uni_refs_dct[name]): ich_name_dct[_ich_key_name(ich)] = name elif ich == uni_refs_dct[name]['inchi']: ich_name_dct[ich] = name # Check the ich_name_dct dct_incomplete = False for ich, name in ich_name_dct.items(): if name is None: ioprinter.warning_message(f'{ich} not given in species.csv file') dct_incomplete = True if dct_incomplete: ioprinter.error_message('Job ending since basis species not specified') sys.exit() # Get the species energy if read_species: ioprinter.info_message(f'Calculating energy for species {spc_name}', newline=1) pf_filesystems = filesys.models.pf_filesys(spc_dct[spc_name], spc_model_dct_i, run_prefix, save_prefix, saddle='ts' in spc_name, name=spc_name) h_spc = read_energy(spc_dct[spc_name], pf_filesystems, spc_model_dct_i, run_prefix, read_ene=True, read_zpe=True, saddle='ts' in spc_name) if h_spc is None: ioprinter.error_message(f'No energy found for {spc_name}') sys.exit() else: h_spc = None # Get the energies of the bases h_basis = [] ichs = [*ich_name_dct.keys()] args = (ich_name_dct, spc_dct, uni_refs_dct, spc_model_dct_i, run_prefix, save_prefix) h_basis_dct_lst = execute_function_in_parallel(_read_basis_energy, ichs, args, nprocs=4) print('hbasis list', h_basis_dct_lst) for ich in ichs: for h_basis_dct in h_basis_dct_lst: if ich in h_basis_dct: h_basis.append(h_basis_dct[ich]) break print(h_basis) # Check if all the energies found no_ene_cnt = 0 for basis_ene, basis_name in zip(h_basis, ich_name_dct.values()): if basis_ene is None: ioprinter.warning_message(f'No energy found for {basis_name}') no_ene_cnt += 1 if no_ene_cnt > 1: ioprinter.error_message('Not all energies found for the basis species') sys.exit() return h_spc, h_basis
def _make_fake_mess_strs(chnl, side, fake_inf_dcts, chnl_enes, label_dct, side_label): """ write the MESS strings for the fake wells and TSs """ # Set vars based on the reacs/prods reacs, prods = chnl if side == 'reacs': well_key = 'fake_vdwr' ts_key = 'fake_vdwr_ts' prepend_key = 'FRB' side_idx = 0 elif side == 'prods': well_key = 'fake_vdwp' ts_key = 'fake_vdwp_ts' side_idx = 1 if reacs in (prods, prods[::-1]): prepend_key = 'FRB' else: prepend_key = 'FPB' # Initialize well and ts strs and data dcts fake_dat_dct = {} well_str, ts_str = '', '' # Build a fake TS dct ts_inf_dct = {'n_pst': 6.0, 'cn_pst': 10.0} # MESS string for the fake reactant side well well_dct_key = make_rxn_str(chnl[side_idx], prepend='F') well_dct_key_rev = make_rxn_str(chnl[side_idx][::-1], prepend='F') if well_dct_key in label_dct: fake_well_label = label_dct[well_dct_key] elif well_dct_key_rev in label_dct: fake_well_label = label_dct[well_dct_key_rev] else: ioprinter.warning_message(f'No label {well_dct_key} in label dict') # well_str += mess_io.writer.species_separation_str() _side_str = '+'.join(chnl[side_idx]) aux_str = f'Fake Well for {_side_str}' fake_well, well_dat = blocks.fake_species_block(*fake_inf_dcts) well_str += mess_io.writer.well(fake_well_label, fake_well, aux_id_label=aux_str, zero_ene=chnl_enes[well_key]) # MESS PST TS string for fake reactant side well -> reacs pst_dct_key = make_rxn_str(chnl[side_idx], prepend=prepend_key) pst_dct_key_rev = make_rxn_str(chnl[side_idx][::-1], prepend=prepend_key) if pst_dct_key in label_dct: pst_label = label_dct[pst_dct_key] elif pst_dct_key_rev in label_dct: pst_label = label_dct[pst_dct_key_rev] else: ioprinter.warning_message(f'No label {pst_dct_key} in label dict') pst_ts_str, pst_ts_dat = blocks.pst_block(ts_inf_dct, *fake_inf_dcts) ts_str += '\n' + mess_io.writer.ts_sadpt(pst_label, side_label, fake_well_label, pst_ts_str, aux_id_label=None, zero_ene=chnl_enes[ts_key], tunnel='') # Build the data dct if well_dat: fake_dat_dct.update(well_dat) if pst_ts_dat: fake_dat_dct.update(pst_ts_dat) return well_str, ts_str, fake_well_label, fake_dat_dct
def _make_channel_mess_strs(tsname, reacs, prods, pesgrp_num, spc_dct, label_dct, written_labels, pes_param_dct, chnl_infs, chnl_enes, spc_model_dct_i, unstable_chnl=False): """ For each reaction channel on the PES: take all of the pre-read and pre-processed information from the save filesys for the reactants, products, and transition state and write the appropriately formatted MESS input strings that will eventually be combined into the entire MESS input file. Also returns dictionary for all additional auxiliary data files, formatted as {file name: file string}, required by MESS. List of labels corresponding to MESS strings that have already been written and added to master string, meaning that species string does not need to be written again. Required since species appear on multiple channels. :param tsname: mechanism name of the transition state :param reacs: mechanisms name for the reactants of the reaction channel :type reacs: tuple(str) :param prods: mechanisms name for the products of the reaction channel :type prods: tuple(str) :param label_dct: mapping between mechanism name and MESS input label :type label_dct: dict[str: str] :param written_labels: :type written_labels: :param chnl_infs: collated molecular info obtained from save filesys :type chnl_infs: dict[str:__] :param chnl_enes: energies for channel, relative to PES reference :type chnl_enes: dict[str:float] :rtype: (str, str, str), str, dict[str:str] """ # Initialize empty strings bi_str, well_str, ts_str = '', '', '' full_dat_dct = {} # Write the MESS string for the channel reactant(s) and product(s) for side in ('reacs', 'prods'): # Get information from relevant dictionaries rgt_names = reacs if side == 'reacs' else prods rgt_infs = chnl_infs[side] rgt_ene = chnl_enes[side] # Build the species string for reactant(s)/product(s) # Skip molec string building for termolecular species (may need agn) spc_strs = [] if len(rgt_names) < 3: for inf in rgt_infs: spc_str, dat_dct = _make_spc_mess_str(inf) spc_strs.append(spc_str) full_dat_dct.update(dat_dct) # Set the labels to put into the file spc_labels = () for name in rgt_names: if name in label_dct: spc_labels += (label_dct[name], ) else: spc_labels += (name, ) aux_labels = tuple( automol.inchi.smiles(spc_dct[name]['inchi']) for name in rgt_names) # spc_label = [automol.inchi.smiles(spc_dct[name]['inchi']) # for name in rgt_names] _rxn_str = make_rxn_str(rgt_names) _rxn_str_rev = make_rxn_str(rgt_names[::-1]) if _rxn_str in label_dct: chn_label = label_dct[_rxn_str] elif _rxn_str_rev in label_dct: chn_label = label_dct[_rxn_str_rev] else: ioprinter.warning_message(f'no {_rxn_str} in label dct') # Write the strings if chn_label not in written_labels: written_labels.append(chn_label) if len(rgt_names) == 3: aux_str = f'{spc_labels[0]}+{spc_labels[1]}+{spc_labels[2]}' bi_str += mess_io.writer.dummy(chn_label, aux_id_label=aux_str, zero_ene=rgt_ene) elif len(rgt_names) == 2: # Determine if product densities should be calc'd if side == 'prods': calc_dens = set_prod_density_param(rgt_names, pesgrp_num, pes_param_dct) else: calc_dens = (False, False) aux_str = f'{spc_labels[0]} + {spc_labels[1]}' bi_str += mess_io.writer.bimolecular( chn_label, spc_labels[0], spc_strs[0], spc_labels[1], spc_strs[1], rgt_ene, auxbimol_id_label=aux_str, aux1_id_label=aux_labels[0], aux2_id_label=aux_labels[1], calc_spc1_density=calc_dens[0], calc_spc2_density=calc_dens[1]) + '\n' else: edown_str = rgt_infs[0].get('edown_str', None) collid_freq_str = rgt_infs[0].get('collid_freq_str', None) well_str += mess_io.writer.well( chn_label, spc_strs[0], aux_id_label=aux_labels[0], zero_ene=rgt_ene, edown_str=edown_str, collid_freq_str=collid_freq_str) + '\n' # Initialize the reactant and product MESS label if side == 'reacs': reac_label = chn_label inner_reac_label = chn_label else: prod_label = chn_label inner_prod_label = chn_label # For abstractions: Write MESS strings for fake reac and prod wells and TS if chnl_infs.get('fake_vdwr', None) is not None: # Write all the MESS Strings for Fake Wells and TSs fwell_str, fts_str, fake_lbl, fake_dct = _make_fake_mess_strs( (reacs, prods), 'reacs', chnl_infs['fake_vdwr'], chnl_enes, label_dct, reac_label) # Append the fake strings to overall strings well_str += fwell_str + '\n' ts_str += fts_str # Re-set the reactant label for the inner transition state inner_reac_label = fake_lbl # Update the data string dct if necessary full_dat_dct.update(fake_dct) if chnl_infs.get('fake_vdwp', None) is not None: # Write all the MESS Strings for Fake Wells and TSs fwell_str, fts_str, fake_lbl, fake_dct = _make_fake_mess_strs( (reacs, prods), 'prods', chnl_infs['fake_vdwp'], chnl_enes, label_dct, prod_label) # Append the fake strings to overall strings well_str += fwell_str + '\n' ts_str += fts_str # Reset the product labels for the inner transition state inner_prod_label = fake_lbl # Update the data string dct if necessary full_dat_dct.update(fake_dct) # Write MESS string for the inner transition state; append full # Label has to correspond only to base name (ignores configuration) ts_label = label_dct[tsname] rclass = spc_dct[tsname + '_0']['class'] sts_str, ts_dat_dct = _make_ts_mess_str(chnl_infs, chnl_enes, spc_model_dct_i, rclass, ts_label, inner_reac_label, inner_prod_label, unstable_chnl=unstable_chnl) ts_str += sts_str full_dat_dct.update(ts_dat_dct) return ((well_str, bi_str, ts_str), full_dat_dct, written_labels)
def run_tau(zma, spc_info, thy_info, nsamp, tors_range_dct, tau_run_fs, tau_save_fs, script_str, overwrite, saddle, **kwargs): """ run sampling algorithm to find tau dependent geometries """ if not tors_range_dct: ioprinter.info_message( "No torsional coordinates. Setting nsamp to 1.") nsamp = 1 tau_save_fs[0].create() vma = automol.zmat.var_(zma) if tau_save_fs[0].file.vmatrix.exists(): existing_vma = tau_save_fs[0].file.vmatrix.read() assert vma == existing_vma tau_save_fs[0].file.vmatrix.write(vma) idx = 0 nsamp0 = nsamp inf_obj = autofile.schema.info_objects.tau_trunk(0, tors_range_dct) while True: if tau_save_fs[0].file.info.exists(): inf_obj_s = tau_save_fs[0].file.info.read() nsampd = inf_obj_s.nsamp elif tau_run_fs[0].file.info.exists(): inf_obj_r = tau_run_fs[0].file.info.read() nsampd = inf_obj_r.nsamp else: nsampd = 0 nsamp = nsamp0 - nsampd if nsamp <= 0: ioprinter.info_message( 'Reached requested number of samples. ', 'Tau sampling complete.') break ioprinter.info_message("New nsamp is {:d}.".format(nsamp), indent=1) samp_zma, = automol.zmat.samples(zma, 1, tors_range_dct) tid = autofile.schema.generate_new_tau_id() locs = [tid] tau_run_fs[-1].create(locs) tau_run_prefix = tau_run_fs[-1].path(locs) run_fs = autofile.fs.run(tau_run_prefix) idx += 1 ioprinter.info_message("Run {}/{}".format(idx, nsamp0)) ioprinter.debug_message( 'Checking if ZMA has high repulsion...', newline=1) geo = automol.zmat.geometry(zma) samp_geo = automol.zmat.geometry(samp_zma) if automol.pot.low_repulsion_struct(geo, samp_geo): ioprinter.debug_message('ZMA fine.') es_runner.run_job( job=elstruct.Job.OPTIMIZATION, script_str=script_str, run_fs=run_fs, geo=samp_zma, spc_info=spc_info, thy_info=thy_info, saddle=saddle, overwrite=overwrite, frozen_coordinates=tors_range_dct.keys(), **kwargs ) else: ioprinter.warning_message('repulsive ZMA:') inp_str = elstruct.writer.optimization( geo=samp_zma, charge=spc_info[1], mult=spc_info[2], method=thy_info[1], basis=thy_info[2], prog=thy_info[0], orb_type=thy_info[3], mol_options=['nosym'], frozen_coordinates=tors_range_dct.keys(), ) tau_run_fs[-1].file.geometry_input.write(inp_str, locs) ioprinter.warning_message( 'geometry for bad ZMA at', tau_run_fs[-1].path(locs)) # nsampd += 1 # inf_obj.nsamp = nsampd # tau_save_fs[0].file.info.write(inf_obj) # tau_run_fs[0].file.info.write(inf_obj) if tau_save_fs[0].file.info.exists(): inf_obj_s = tau_save_fs[0].file.info.read() nsampd = inf_obj_s.nsamp elif tau_run_fs[0].file.info.exists(): inf_obj_r = tau_run_fs[0].file.info.read() nsampd = inf_obj_r.nsamp nsampd += 1 inf_obj.nsamp = nsampd tau_save_fs[0].file.info.write(inf_obj) tau_run_fs[0].file.info.write(inf_obj)
def _make_channel_mess_strs(tsname, reacs, prods, spc_dct, label_dct, written_labels, chnl_infs, chnl_enes, spc_model_dct_i): """ make the partition function strings for each of the channels includes strings for each of the unimolecular wells, bimolecular fragments, and transition states connecting them. It also includes a special treatment for abstraction to include phase space blocks and coupling bimolecular fragments to fake van der Waals wells """ # Initialize empty strings bi_str, well_str, ts_str = '', '', '' full_dat_dct = {} # Write the MESS string for the channel reactant(s) and product(s) for side in ('reacs', 'prods'): # Get information from relevant dictionaries rgt_names = reacs if side == 'reacs' else prods rgt_infs = chnl_infs[side] rgt_ene = chnl_enes[side] # Build the species string for reactant(s)/product(s) # Skip molec string building for termolecular species (may need agn) spc_strs = [] if len(rgt_names) < 3: for inf in rgt_infs: spc_str, dat_dct = _make_spc_mess_str(inf) spc_strs.append(spc_str) full_dat_dct.update(dat_dct) # Set the labels to put into the file spc_label = [automol.inchi.smiles(spc_dct[name]['inchi']) for name in rgt_names] _rxn_str = make_rxn_str(rgt_names) _rxn_str_rev = make_rxn_str(rgt_names[::-1]) if _rxn_str in label_dct: chn_label = label_dct[_rxn_str] elif _rxn_str_rev in label_dct: chn_label = label_dct[_rxn_str_rev] else: ioprinter.warning_message('no {} in label dct'.format(_rxn_str)) # Write the strings if chn_label not in written_labels: written_labels.append(chn_label) if len(rgt_names) == 3: bi_str += '\n! {} + {} + {}\n'.format( rgt_names[0], rgt_names[1], rgt_names[2]) bi_str += mess_io.writer.dummy(chn_label, zero_ene=rgt_ene) # bi_str += '\n! DUMMY FOR UNSTABLE SPECIES\n' # bi_str += mess_io.writer.dummy(chn_label, zero_ene=None) elif len(rgt_names) == 2: # bi_str += mess_io.writer.species_separation_str() bi_str += '\n! {} + {}\n'.format(rgt_names[0], rgt_names[1]) bi_str += mess_io.writer.bimolecular( chn_label, spc_label[0], spc_strs[0], spc_label[1], spc_strs[1], rgt_ene) else: edown_str = rgt_infs[0].get('edown_str', None) collid_freq_str = rgt_infs[0].get('collid_freq_str', None) # well_str += mess_io.writer.species_separation_str() well_str += '\n! {}\n'.format(rgt_names[0]) well_str += mess_io.writer.well( chn_label, spc_strs[0], zero_ene=rgt_ene, edown_str=edown_str, collid_freq_str=collid_freq_str) # Initialize the reactant and product MESS label if side == 'reacs': reac_label = chn_label inner_reac_label = chn_label else: prod_label = chn_label inner_prod_label = chn_label # For abstractions: Write MESS strings for fake reac and prod wells and TS if chnl_infs.get('fake_vdwr', None) is not None: # Write all the MESS Strings for Fake Wells and TSs fwell_str, fts_str, fake_lbl, fake_dct = _make_fake_mess_strs( (reacs, prods), 'reacs', chnl_infs['fake_vdwr'], chnl_enes, label_dct, reac_label) # Append the fake strings to overall strings well_str += fwell_str ts_str += fts_str # Re-set the reactant label for the inner transition state inner_reac_label = fake_lbl # Update the data string dct if necessary full_dat_dct.update(fake_dct) if chnl_infs.get('fake_vdwp', None) is not None: # Write all the MESS Strings for Fake Wells and TSs fwell_str, fts_str, fake_lbl, fake_dct = _make_fake_mess_strs( (reacs, prods), 'prods', chnl_infs['fake_vdwp'], chnl_enes, label_dct, prod_label) # Append the fake strings to overall strings well_str += fwell_str ts_str += fts_str # Reset the product labels for the inner transition state inner_prod_label = fake_lbl # Update the data string dct if necessary full_dat_dct.update(fake_dct) # Write MESS string for the inner transition state; append full ts_label = label_dct[tsname] rclass = spc_dct[tsname+'_0']['class'] sts_str, ts_dat_dct = _make_ts_mess_str( chnl_infs, chnl_enes, spc_model_dct_i, rclass, ts_label, inner_reac_label, inner_prod_label) ts_str += sts_str full_dat_dct.update(ts_dat_dct) return [well_str, bi_str, ts_str], full_dat_dct, written_labels
def _write_varecof_input(ref_zma, ts_info, ts_formula, high_mul, rct_ichs, rct_info, rct_zmas, active_space, mod_var_sp1_thy_info, npot, inf_sep_ene, min_idx, max_idx, vrc_dct, vrc_path, script_str): """ prepare all the input files for a vrc-tst calculation """ r1dists_lr = vrc_dct['r1dists_lr'] r1dists_sr = vrc_dct['r1dists_sr'] r2dists_sr = vrc_dct['r2dists_sr'] d1dists = vrc_dct['d1dists'] d2dists = vrc_dct['d2dists'] conditions = vrc_dct['conditions'] nsamp_max = vrc_dct['nsamp_max'] nsamp_min = vrc_dct['nsamp_min'] flux_err = vrc_dct['flux_err'] pes_size = vrc_dct['pes_size'] base_name = vrc_dct['base_name'] # exe_path = vrc_dct['exe_path'] # Build geometries needed for the varecof run total_geom, frag_geoms, frag_geoms_wdummy = fragment_geometries( ref_zma, rct_zmas, min_idx, max_idx) # Set information for the pivot points needed in divsur.inp frames, npivots = build_pivot_frames(min_idx, max_idx, total_geom, frag_geoms, frag_geoms_wdummy) pivot_angles = calc_pivot_angles(frag_geoms, frag_geoms_wdummy, frames) pivot_xyzs = calc_pivot_xyzs(min_idx, max_idx, total_geom, frag_geoms) # Write the long- and short-range divsur input files lrdivsur_inp_str = varecof_io.writer.input_file.divsur( r1dists_lr, 1, 1, [0.0, 0.0, 0.0], [0.0, 0.0, 0.0]) # Write the short-range divsur files t1angs = [pivot_angles[0]] if pivot_angles[0] is not None else [] t2angs = [pivot_angles[1]] if pivot_angles[1] is not None else [] if automol.geom.is_atom(frag_geoms[0]): d1dists = [] t1angs = [] if automol.geom.is_atom(frag_geoms[1]): d2dists = [] t2angs = [] if automol.geom.is_linear(frag_geoms[0]): d1dists = [0.] t1angs = [] if automol.geom.is_linear(frag_geoms[1]): d2dists = [0.] t2angs = [] if all(npiv > 1 for npiv in npivots): r2dists = r2dists_sr else: r2dists = [] ioprinter.warning_message('no r2dist') srdivsur_inp_str = varecof_io.writer.input_file.divsur(r1dists_sr, npivots[0], npivots[1], pivot_xyzs[0], pivot_xyzs[1], frame1=frames[0], frame2=frames[1], d1dists=d1dists, d2dists=d2dists, t1angs=t1angs, t2angs=t2angs, r2dists=r2dists, **conditions) # Build the structure input file string struct_inp_str = varecof_io.writer.input_file.structure( frag_geoms_wdummy[0], frag_geoms_wdummy[1]) # Write the structure and divsur files to get the divsur out file inp = ((struct_inp_str, 'structure.inp'), (srdivsur_inp_str, 'divsur.inp')) _write_varecof_inp(inp, vrc_path) # Obtain the divsur.out file with divsur-frame fragment geoms divsur_out_str = build_divsur_out_file(vrc_path, os.getcwd()) # Write the tst.inp file faces, faces_symm = assess_face_symmetries(divsur_out_str) tst_inp_str = varecof_io.writer.input_file.tst(nsamp_max, nsamp_min, flux_err, pes_size, faces=faces, faces_symm=faces_symm) # Write the molpro executable and potential energy surface input string els_inp_str = varecof_io.writer.input_file.elec_struct( vrc_path, base_name, npot, dummy_name='dummy_corr_', lib_name='libcorrpot.so', exe_name='molpro.sh', geom_ptt='GEOMETRY_HERE', ene_ptt='molpro_energy') # Write the electronic structure template file tml_inp_str = _build_molpro_template_str(ref_zma, ts_info, ts_formula, high_mul, rct_ichs, rct_info, active_space, mod_var_sp1_thy_info, inf_sep_ene) # Write the mc_flux.inp input string mc_flux_inp_str = varecof_io.writer.input_file.mc_flux() # Write the convert.inp input string conv_inp_str = varecof_io.writer.input_file.convert() # Write machines file to set compute nodes machine_file_str = build_machinefile_str() # Collate the input strings and write the remaining files input_strs = (lrdivsur_inp_str, tst_inp_str, els_inp_str, tml_inp_str, mc_flux_inp_str, conv_inp_str, machine_file_str, script_str) input_names = ('lr_divsur.inp', 'tst.inp', 'molpro.inp', 'mol.tml', 'mc_flux.inp', 'convert.inp', 'machines', 'molpro.sh') inp = tuple(zip(input_strs, input_names)) _write_varecof_inp(inp, vrc_path)
def conformer_sampling(zma, spc_info, thy_info, cnf_run_fs, cnf_save_fs, rid, script_str, overwrite, nsamp_par=(False, 3, 3, 1, 50, 50), tors_names=(), zrxn=None, two_stage=False, retryfail=False, resave=False, **kwargs): """ run sampling algorithm to find conformers """ # Check if any saving needs to be done before hand if resave: _presamp_save(spc_info, cnf_run_fs, cnf_save_fs, thy_info, zrxn=zrxn, rid=rid) # Build filesys cnf_save_fs[1].create([rid]) inf_obj = autofile.schema.info_objects.conformer_branch(0) # Set the samples nsamp, tors_range_dct = _calc_nsamp(tors_names, nsamp_par, zma, zrxn=zrxn) nsamp0 = nsamp nsampd = _calc_nsampd(cnf_save_fs, cnf_run_fs, rid) tot_samp = nsamp - nsampd brk_tot_samp = nsamp * 5 ioprinter.info_message( ' - Number of samples that have been currently run:', nsampd) ioprinter.info_message(' - Number of samples requested:', nsamp) if nsamp - nsampd > 0: ioprinter.info_message('Running {} samples...'.format(nsamp - nsampd), newline=1) samp_idx = 1 samp_attempt_idx = 1 while True: nsamp = nsamp0 - nsampd # Break the while loop if enough sampls completed if nsamp <= 0: ioprinter.info_message( 'Requested number of samples have been completed.', 'Conformer search complete.') break if samp_attempt_idx == brk_tot_samp: ioprinter.info_message( 'Max sample num: 5*{} attempted, ending search'.format(nsamp), 'Run again if more samples desired.') break # Run the conformer sampling if nsampd > 0: samp_zma, = automol.zmat.samples(zma, 1, tors_range_dct) else: samp_zma = zma bad_geom_count = 0 geo = automol.zmat.geometry(zma) samp_geo = automol.zmat.geometry(samp_zma) while (not automol.pot.low_repulsion_struct(geo, samp_geo) and bad_geom_count < 1000): ioprinter.warning_message('ZMA has high repulsion.', indent=1 / 2.) ioprinter.warning_message('Generating new sample ZMA', indent=1 / 2., newline=1) samp_zma, = automol.zmat.samples(zma, 1, tors_range_dct) samp_geo = automol.zmat.geometry(samp_zma) bad_geom_count += 1 ioprinter.debug_message('ZMA is fine...', indent=1 / 2.) cid = autofile.schema.generate_new_conformer_id() locs = [rid, cid] cnf_run_fs[-1].create(locs) cnf_run_path = cnf_run_fs[-1].path(locs) run_fs = autofile.fs.run(cnf_run_path) ioprinter.info_message("Run {}/{}".format(samp_idx, tot_samp)) tors_names = tuple(tors_range_dct.keys()) if two_stage and tors_names: frozen_coords_lst = ((), tors_names) success, ret = es_runner.multi_stage_optimization( script_str=script_str, run_fs=run_fs, geo=samp_zma, spc_info=spc_info, thy_info=thy_info, frozen_coords_lst=frozen_coords_lst, overwrite=overwrite, saddle=bool(zrxn is not None), retryfail=retryfail, **kwargs) else: success, ret = es_runner.execute_job(job=elstruct.Job.OPTIMIZATION, script_str=script_str, run_fs=run_fs, geo=samp_zma, spc_info=spc_info, thy_info=thy_info, overwrite=overwrite, saddle=bool(zrxn is not None), retryfail=retryfail, **kwargs) # save function added here if success: _save_conformer(ret, cnf_save_fs, locs, thy_info, zrxn=zrxn, orig_ich=spc_info[0], rid_traj=True, init_zma=samp_zma) nsampd = _calc_nsampd(cnf_save_fs, cnf_run_fs, rid) nsampd += 1 samp_idx += 1 inf_obj.nsamp = nsampd cnf_save_fs[1].file.info.write(inf_obj, [rid]) cnf_run_fs[1].file.info.write(inf_obj, [rid]) # Increment attempt counter samp_attempt_idx += 1
def _read_potentials(scan_inf_dct, thy_inf_dct, savefs_dct): """ Read values form the filesystem to get the values to correct ht MEP # Read the energies from the full and constrained opts along MEP """ scn_save_fs = savefs_dct['vscnlvl_scn'] cscn_save_fs = savefs_dct['vscnlvl_cscn'] mod_var_scn_thy_info = thy_inf_dct['mod_var_scnlvl'] mod_var_sp1_thy_info = thy_inf_dct['mod_var_splvl1'] coord_name = scan_inf_dct['coord_names'][0] full_grid = scan_inf_dct['full_grid'] constraint_dct = scan_inf_dct['constraint_dct'] grid_val_for_zma = scan_inf_dct['grid_val_for_zma'] # build objects for loops smp_pot, const_pot, sp_pot = [], [], [] scans = ( (scn_save_fs, mod_var_scn_thy_info), (cscn_save_fs, mod_var_scn_thy_info) ) if mod_var_sp1_thy_info is not None: scans += ((scn_save_fs, mod_var_sp1_thy_info),) for idx, (scn_fs, thy_info) in enumerate(scans): for grid_val in full_grid: if idx in (0, 2): locs = [[coord_name], [grid_val]] else: locs = [constraint_dct, [coord_name], [grid_val]] sp_ene = filesys.read.energy(scn_fs, locs, thy_info) # Store the energy in a lst if idx == 0: smp_pot.append(sp_ene) elif idx == 1: const_pot.append(sp_ene) elif idx == 2: sp_pot.append(sp_ene) print('pots test') print(smp_pot) print(const_pot) print(sp_pot) sp_corr_inf = (sp_pot[-1] - smp_pot[-1]) print('inf sp_corr test', sp_corr_inf) # Calculate each of the correction potentials relax_corr_pot, sp_corr_pot, full_corr_pot = [], [], [] for i, _ in enumerate(smp_pot): # We assume relax_corr_inf = 0 relax_corr = (smp_pot[i] - const_pot[i]) * phycon.EH2KCAL relax_corr_pot.append(relax_corr) if all(ene is not None for ene in sp_pot): sp_corr = ((sp_pot[i] - smp_pot[i]) - sp_corr_inf) * phycon.EH2KCAL sp_corr_pot.append(sp_corr) else: warning_message("No single point correction applied to potential") sp_corr = 0.0 full_corr_pot.append(relax_corr + sp_corr) # Collate the potentials together in a list if sp_pot: potentials = [relax_corr_pot, sp_corr_pot, full_corr_pot] potential_labels = ['relax', 'sp', 'full'] else: potentials = [relax_corr_pot, full_corr_pot] potential_labels = ['relax', 'full'] # Get zma used to make structure.inp and divsur.inp inp_zma_locs = [[coord_name], [grid_val_for_zma]] if scn_save_fs[-1].file.zmatrix.exists(inp_zma_locs): zma_for_inp = scn_save_fs[-1].file.zmatrix.read(inp_zma_locs) else: zma_for_inp = None return potentials, potential_labels, zma_for_inp
def run_tau(zma, spc_info, thy_info, tau_run_fs, tau_save_fs, script_str, overwrite, nsamp_par=(False, 3, 3, 1, 50, 50), tors_names=(), repulsion_thresh=40.0, zrxn=None, **kwargs): """ run sampling algorithm to find tau dependent geometries """ # Set the filesystem objects tau_save_fs[0].create() # Check if the structure is consistent with the filesystem _check_vma(zma, tau_save_fs) # Set the samples nsamp, tors_range_dct = util.calc_nsamp( tors_names, nsamp_par, zma, zrxn=zrxn) nsamp0 = nsamp nsampd = util.calc_nsampd(tau_save_fs, tau_run_fs, rid=None) num_to_samp = nsamp - nsampd info_message( ' - Number of samples that have been currently run:', nsampd) info_message(' - Number of samples requested:', nsamp) if num_to_samp > 0: info_message( f'Running {num_to_samp} samples...', newline=1) samp_idx = 1 # Set the filesystem objects inf_obj = autofile.schema.info_objects.tau_trunk(0, tors_range_dct) while True: nsamp = nsamp0 - nsampd # Break the while loop if enough sampls completed if nsamp <= 0: info_message( 'Reached requested number of samples. ', 'Tau sampling complete.') break samp_zma, = automol.zmat.samples(zma, 1, tors_range_dct) tid = autofile.schema.generate_new_tau_id() locs = [tid] tau_run_fs[-1].create(locs) tau_run_prefix = tau_run_fs[-1].path(locs) run_fs = autofile.fs.run(tau_run_prefix) info_message(f"\nRun {samp_idx}/{num_to_samp}") samp_idx += 1 info_message( 'Generating sample Z-Matrix that does not have', 'high intramolecular repulsion...') ref_pot = automol.pot.intramol_interaction_potential_sum( automol.zmat.geometry(zma)) samp_pot = automol.pot.intramol_interaction_potential_sum( automol.zmat.geometry(samp_zma)) if samp_pot-ref_pot < repulsion_thresh: debug_message('ZMA fine.') es_runner.run_job( job=elstruct.Job.OPTIMIZATION, script_str=script_str, run_fs=run_fs, geo=samp_zma, spc_info=spc_info, thy_info=thy_info, saddle=bool(zrxn is not None), overwrite=overwrite, frozen_coordinates=tors_range_dct.keys(), **kwargs ) else: warning_message('repulsive ZMA:') inp_str = elstruct.writer.optimization( geo=samp_zma, charge=spc_info[1], mult=spc_info[2], method=thy_info[1], basis=thy_info[2], prog=thy_info[0], orb_type=thy_info[3], mol_options=['nosym'], frozen_coordinates=tors_range_dct.keys(), ) tau_run_fs[-1].file.geometry_input.write(inp_str, locs) warning_message( 'geometry for bad ZMA at', tau_run_fs[-1].path(locs)) if tau_save_fs[0].file.info.exists(): inf_obj_s = tau_save_fs[0].file.info.read() nsampd = inf_obj_s.nsamp elif tau_run_fs[0].file.info.exists(): inf_obj_r = tau_run_fs[0].file.info.read() nsampd = inf_obj_r.nsamp nsampd += 1 inf_obj.nsamp = nsampd tau_save_fs[0].file.info.write(inf_obj) tau_run_fs[0].file.info.write(inf_obj)
def tors_projected_freqs(pf_filesystems, mess_hr_str, projrot_hr_str, prefix, zrxn=None, conf=None): """ Get frequencies from one version of ProjRot """ run_prefix = pf_filesystems['run_prefix'] # Build the filesystems [harm_cnf_fs, _, harm_min_locs, _, _] = pf_filesystems['harm'] [tors_cnf_fs, _, tors_min_locs, _, _] = pf_filesystems['tors'] if conf: harm_min_locs = conf[1] harm_cnf_fs = conf[2] # Read info from the filesystem that is needed harm_geo = harm_cnf_fs[-1].file.geometry.read(harm_min_locs) hess = harm_cnf_fs[-1].file.hessian.read(harm_min_locs) tors_geo = tors_cnf_fs[-1].file.geometry.read(tors_min_locs) ioprinter.reading('Hessian', harm_cnf_fs[-1].path(harm_min_locs)) fml_str = automol.geom.formula_string(harm_geo) vib_path = job_path(run_prefix, 'PROJROT', 'PROJFREQ', fml_str, print_path=True) # Read info for the hindered rotors and calculate the ZPVE ioprinter.info_message(' - Calculating the torsional ZPVES using MESS...') script_str = autorun.SCRIPT_DCT['messpf'] tors_freqs, _ = autorun.mess.torsions(script_str, vib_path, tors_geo, mess_hr_str) tors_zpe = (sum(tors_freqs) / 2.0) * phycon.WAVEN2EH ioprinter.info_message( ' - Calculating the RT and RT-rotor projected frequencies ProjRot') # NEW projrot writing script_str = autorun.SCRIPT_DCT['projrot'] dist_cutoff_dct1 = {('H', 'O'): 2.26767, ('H', 'C'): 2.26767} dist_cutoff_dct2 = { ('H', 'O'): 2.83459, ('H', 'C'): 2.83459, ('C', 'O'): 3.7807 } rotor_dist1_str = projrot_io.writer.projection_distance_aux( dist_cutoff_dct=dist_cutoff_dct1) rotor_dist2_str = projrot_io.writer.projection_distance_aux( dist_cutoff_dct=dist_cutoff_dct2) aux_dct1 = {'dist_rotpr.dat': rotor_dist1_str} aux_dct2 = {'dist_rotpr.dat': rotor_dist2_str} rt_freqs1, rth_freqs1, rt_imag1, _ = autorun.projrot.frequencies( script_str, vib_path, [harm_geo], [[]], [hess], rotors_str=projrot_hr_str, aux_dct=aux_dct1) _, rth_freqs2, rt_imag2, _ = autorun.projrot.frequencies( script_str, vib_path, [harm_geo], [[]], [hess], rotors_str=projrot_hr_str, aux_dct=aux_dct2) # Calculate harmonic ZPVE from all harmonic freqs, including torsionals harm_zpe = (sum(rt_freqs1) / 2.0) * phycon.WAVEN2EH ioprinter.info_message('harmonic zpe is {} kcal/mol'.format(harm_zpe)) # Calculate harmonic ZPVE from freqs where torsions have been projected out # Value from both projrot versions, which use different projection schemes harm_zpe_notors_1 = (sum(rth_freqs1) / 2.0) * phycon.WAVEN2EH harm_zpe_notors_2 = (sum(rth_freqs2) / 2.0) * phycon.WAVEN2EH # Calcuate the difference in the harmonic ZPVE from projecting out torsions harm_tors_zpe = harm_zpe - harm_zpe_notors_1 harm_tors_zpe_2 = harm_zpe - harm_zpe_notors_2 # Check to see which of the above ZPVEs match more closely with tors ZPVE # calculated directly by treating the torsions in MESS diff_tors_zpe = harm_tors_zpe - tors_zpe diff_tors_zpe_2 = harm_tors_zpe_2 - tors_zpe if diff_tors_zpe <= diff_tors_zpe_2: freqs = rth_freqs1 imag_freqs = rt_imag1 proj_zpe = harm_zpe_notors_1 else: freqs = rth_freqs2 imag_freqs = rt_imag2 proj_zpe = harm_zpe_notors_2 # Check imaginary frequencies and set freqs if zrxn is not None: if len(imag_freqs) > 1: ioprinter.warning_message( 'There is more than one imaginary frequency') imag = max(imag_freqs) else: imag = None # NEW autorun function for the frequencies # mess_script_str = autorun.SCRIPT_DCT['messpf'] # projrot_script_str = autorun.SCRIPT_DCT['projrot'] # proj_freqs, proj_imags, proj_zpe, harm_freqs, tors_freqs = autorun.projected_frequencies( # mess_script_str, projrot_script_str, vib_path, # mess_hr_str, projrot_hr_str, # tors_geo, harm_geo, hess) # if saddle: # proj_imag = proj_imags[0] # else: # proj_imag = [] # NEW scale factor functions # scale_factor = automol.prop.freq.rotor_scale_factor_from_harmonics( # harm_freqs, tors_freqs) # Create a scaling factor for the frequencies # First sort tors frequencies in ascending order sort_tors_freqs = sorted(tors_freqs) # keep only freqs whose RRHO freqs are above a threshold freq_thresh = 50. log_rt_freq = 0.0 nfreq_remove = 0 for freq in rt_freqs1: if freq > freq_thresh: log_rt_freq += numpy.log(freq) else: nfreq_remove += 1 log_freq = [numpy.log(freq) for freq in freqs] log_freq = sum(log_freq) log_tors_freq = 0.0 idx_remove = [] for idx, freq in enumerate(sort_tors_freqs): if idx + 1 > nfreq_remove: log_tors_freq += numpy.log(freq) else: idx_remove.append(tors_freqs.index(freq)) # log_rt_freq = [numpy.log(freq) for freq in rt_freqs1] # log_rt_freq = sum(log_rt_freq) # log_tors_freq = [numpy.log(freq) for freq in tors_freqs] # log_tors_freq = sum(log_tors_freq) #unproj_prod = numpy.prod(rt_freqs1) #proj_prod = numpy.prod(freqs) * numpy.prod(tors_freqs) #print('proj_prod test:', unproj_prod, proj_prod) # ioprinter.info_message('log_freq_tests:', log_rt_freq, log_freq, log_tors_freq) #scale_factor = unproj_prod / proj_prod # generate the scaling factor factor = numpy.exp(log_rt_freq - log_freq - log_tors_freq) ioprinter.info_message('freq test:', freqs, tors_freqs, rt_freqs1) tau_factor = numpy.exp(log_rt_freq - log_freq) tau_factor_mode = tau_factor # generate the set of indices for torsions that are two be scales scale_factor = (idx_remove, factor) ioprinter.info_message('scale fact test', scale_factor) ioprinter.info_message('TAU FACTOR {:4.6f} \t {:g} \t {:3.6f} {} '.format( tau_factor_mode, len(tors_freqs), factor, '-'.join([str(ridx) for ridx in idx_remove]))) # Check if there are significant differences caused by the rotor projection diff_tors_zpe *= phycon.EH2KCAL diff_tors_zpe_2 *= phycon.EH2KCAL if abs(diff_tors_zpe) > 0.2 and abs(diff_tors_zpe_2) > 0.2: ioprinter.warning_message( 'There is a difference of ', '{0:.2f} and {1:.2f}'.format(diff_tors_zpe, diff_tors_zpe_2), 'kcal/mol between harmonic and hindered torsional ZPVEs') return (proj_freqs, proj_imag, proj_zpe, scale_factor, tors_freqs, harm_freqs)
def _hrpot_spline_fitter(pot_dct, min_thresh=-0.0001, max_thresh=50.0): """ Get a physical hindered rotor potential via a series of spline fits """ pot = list(pot_dct.values()) # Initialize a variable for the size of the potential lpot = len(pot)+1 pot.append(0.0) # Print warning messages print_pot = False if any(val > max_thresh for val in pot): print_pot = True max_pot = max(pot) ioprinter.warning_message( 'Found pot val of {0:.2f}'.format(max_pot), ' which is larger than', 'the typical maximum for a torsional potential') # reset any negative values for the first grid point to 0. if pot[0] < 0.: ioprinter.error_message('The first potential value is {} it should be 0.'.format(pot[0])) pot[0] = 0. if any(val < min_thresh for val in pot): print_pot = True min_pot = min(pot) ioprinter.warning_message( 'Found pot val of {0:.2f}'.format(min_pot), ' which is below', '{0} kcal. Refit w/ positives'.format(min_thresh)) if print_pot: ioprinter.debug_message('Potential before spline:', pot) # Build a potential list from only successful calculations # First replace high potential values with max_thresh # Then replace any negative potential values cubic spline fit values idx_success = [] pot_success = [] for idx in range(lpot): if pot[idx] < 600. and pot[idx] > min_thresh: idx_success.append(idx) if pot[idx] < max_thresh: pot_success.append(pot[idx]) else: pot_success.append(max_thresh) if len(pot_success) > 3: # Build a new potential list using a spline fit of the HR potential pot_spl = interp1d( numpy.array(idx_success), numpy.array(pot_success), kind='cubic') for idx in range(lpot): pot[idx] = float(pot_spl(idx)) # Do second spline fit of only positive values if any negative values found if any(val < min_thresh for val in pot): ioprinter.warning_message( 'Still found negative potential values after first spline') ioprinter.debug_message('Potential after spline:', pot) if len(pot_success) > 3: x_pos = numpy.array([i for i in range(lpot) if pot[i] >= min_thresh]) y_pos = numpy.array([pot[i] for i in range(lpot) if pot[i] >= min_thresh]) pos_pot_spl = interp1d(x_pos, y_pos, kind='cubic') pot_pos_fit = [] for idx in range(lpot): pot_pos_fit.append(pos_pot_spl(idx)) else: pot_pos_fit = [] for idx in range(lpot): pot_pos_fit.append(pot[idx]) ioprinter.debug_message('Potential after spline:', pot_pos_fit) # Perform second check to see if negative potentials have been fixed if any(val < min_thresh for val in pot_pos_fit): ioprinter.warning_message( 'Still found negative potential values after second spline') ioprinter.info_message( 'Replace with linear interpolation of positive values') neg_idxs = [i for i in range(lpot) if pot_pos_fit[i] < min_thresh] clean_pot = [] for i in range(lpot): if i in neg_idxs: # Find the indices for positive vals around negative value idx_0 = i - 1 while idx_0 in neg_idxs: idx_0 = idx_0 - 1 for j in range(i, lpot): if pot_pos_fit[j] >= min_thresh: idx_1 = j break # Get a new value for this point on the potential by # doing a linear interp of positives interp_val = ( pot_pos_fit[idx_0] * (1.0-((i-idx_0)/(idx_1-idx_0))) + pot_pos_fit[idx_1] * ((i-idx_0)/(idx_1-idx_0)) ) clean_pot.append(interp_val) else: clean_pot.append(pot_pos_fit[i]) final_potential = clean_pot.copy() else: final_potential = pot_pos_fit.copy() else: final_potential = pot.copy() final_potential = final_potential[:-1] fin_dct = {} for i, val in enumerate(final_potential): val_fin = min(val, max_thresh) fin_dct[(i,)] = val_fin return fin_dct
def inf_sep_ene(ts_dct, thy_inf_dct, thy_method_dct, mref_params, savefs_dct, runfs_dct, es_keyword_dct): """ Determine the total electronic energy of two reacting species that at infinite separation. """ ioprinter.info_message('Calculating the Infinite Separation Energy...') ioprinter.info_message('') # Get info from the reactants rct_info = thy_inf_dct['rct_info'] overwrite = es_keyword_dct['overwrite'] # Get thy_inf_dct stuff thy_info = thy_inf_dct['runlvl'] var_scn_thy_info = thy_inf_dct['var_scnlvl'] var_sp1_thy_info = thy_inf_dct['var_splvl1'] var_sp2_thy_info = thy_inf_dct['var_splvl2'] hs_var_sp1_thy_info = thy_inf_dct['hs_var_splvl1'] hs_var_sp2_thy_info = thy_inf_dct['hs_var_splvl2'] vscn_method_dct = thy_method_dct['var_scnlvl'] var_sp1_method_dct = thy_method_dct['var_splvl1'] var_sp2_method_dct = thy_method_dct['var_splvl2'] # Get the filesys stuff rcts_cnf_fs = savefs_dct['rcts_cnf'] vscnlvl_scn_run_fs = runfs_dct['vscnlvl_scn'] vscnlvl_scn_save_fs = savefs_dct['vscnlvl_scn'] run_prefix = runfs_dct['prefix'] # Get kwargs for the calculation rxn_class = ts_dct['class'] var = (automol.par.is_radrad(rxn_class) and automol.par.is_low_spin(rxn_class)) if var: ts_zma, zrxn = ts_dct['zma'], ts_dct['zrxn'] rxn_info = ts_dct['rxn_info'] ts_info = rinfo.ts_info(rxn_info) high_mult = rinfo.ts_mult(rxn_info, rxn_mul='high') hs_ts_info = (ts_info[0], ts_info[1], high_mult) # Build grid and names appropriate for reaction type names, _, grids, _ = automol.reac.build_scan_info(zrxn, ts_zma, var=var) inf_locs = (names, (grids[1][-1], )) cas_kwargs = mref_params['var_scnlvl'] _inf_sep_ene = _multiref_inf_sep_ene(hs_ts_info, ts_zma, rct_info, rcts_cnf_fs, run_prefix, thy_info, var_scn_thy_info, var_sp1_thy_info, var_sp2_thy_info, hs_var_sp1_thy_info, hs_var_sp2_thy_info, var_sp1_method_dct, var_sp2_method_dct, vscnlvl_scn_run_fs, vscnlvl_scn_save_fs, inf_locs, overwrite=overwrite, **cas_kwargs) else: vscn_thy_info = thy_inf_dct['var_scnlvl'] _inf_sep_ene = _singleref_inf_sep_ene(rct_info, thy_info, vscn_thy_info, rcts_cnf_fs, run_prefix, vscn_method_dct, overwrite) if _inf_sep_ene is not None: ioprinter.energy(_inf_sep_ene) else: ioprinter.warning_message('Infinite separation ene not computed') return _inf_sep_ene
def initial_conformer(spc_dct_i, spc_info, ini_method_dct, method_dct, ini_cnf_save_fs, cnf_run_fs, cnf_save_fs, es_keyword_dct): """ Assess if a conformer layer with a geometry exists in the save filesys for the given species. If not, attempt to generate some guess structure using InChI strings or input geom from user. and optimize it with input method. Then assess if the optimized structure corresponds to genuine minimum on the PES via a frequency calculation. If a minimum is found, save the conformer geometry, zmatrix, energy, and torsions to the save filesys. Also, the function assessess if the species is unstable and will save the appropriate information. """ ini_thy_info = tinfo.from_dct(ini_method_dct) thy_info = tinfo.from_dct(method_dct) mod_thy_info = tinfo.modify_orb_label(thy_info, spc_info) mod_ini_thy_info = tinfo.modify_orb_label(ini_thy_info, spc_info) [kickoff_size, kickoff_backward] = spc_dct_i['kickoff'] _, cnf_path = filesys.mincnf.min_energy_conformer_locators( cnf_save_fs, mod_thy_info) overwrite = es_keyword_dct['overwrite'] if not cnf_path: ioprinter.info_message( 'No conformer found in save filesys. Checking for running jobs...') if _init_geom_is_running(cnf_run_fs) and not overwrite: _run = False else: ioprinter.info_message( 'No conformers are running in run filesys.' + 'Proceeding with optimization...') _run = True elif overwrite: ioprinter.info_message( 'User specified to overwrite energy with new run...') _run = True else: _run = False if _run: ioprinter.info_message('Obtaining some initial guess geometry.') geo_init = _obtain_ini_geom(spc_dct_i, ini_cnf_save_fs, mod_ini_thy_info, overwrite) if geo_init is not None: ioprinter.debug_message( 'Assessing if there are any functional groups', 'that cause instability') ioprinter.debug_message('geo str\n', automol.geom.string(geo_init)) zma_init = automol.geom.zmatrix(geo_init) rid = autofile.schema.generate_new_ring_id() cid = autofile.schema.generate_new_conformer_id() # Determine if there is an instability, if so return prods instab_zmas = automol.reac.instability_product_zmas(zma_init) if not instab_zmas: # Build a cid and a run fs cnf_run_fs[-1].create((rid, cid)) run_fs = autofile.fs.run(cnf_run_fs[-1].path((rid, cid))) if not automol.geom.is_atom(geo_init): geo_found = _optimize_molecule( spc_info, zma_init, method_dct, cnf_save_fs, (rid, cid), run_fs, overwrite, kickoff_size=kickoff_size, kickoff_backward=kickoff_backward) else: geo_found = _optimize_atom(spc_info, zma_init, method_dct, cnf_save_fs, (rid, cid), run_fs, overwrite) else: ioprinter.info_message( 'Found functional groups that cause instabilities') filesys.save.instability(zma_init, instab_zmas, cnf_save_fs, rng_locs=(rid, ), tors_locs=(cid, ), zma_locs=(0, )) geo_found = True else: geo_found = False ioprinter.warning_message( 'Unable to obtain an initial guess geometry') else: ioprinter.existing_path('Initial geometry', cnf_path) geo_found = True return geo_found
def run_vpt2(zma, geo, spc_info, thy_info, geo_run_fs, geo_save_fs, locs, run_prefix, script_str, overwrite, zrxn=None, retryfail=True, method_dct=None, ref_val=None, **kwargs): """ Perform vpt2 analysis for the geometry in the given location """ # Set unneeded vals _, _, _, _ = zrxn, method_dct, ref_val, run_prefix # Set the run filesystem information geo_run_path = geo_run_fs[-1].path(locs) geo_save_path = geo_save_fs[-1].path(locs) run_fs = autofile.fs.run(geo_run_path) # Assess if symmetry needs to be broken for the calculation # Add porgram check because might only be issue for gaussian if spc_info[0] in symm.HIGH: if zma is not None: disp = symm.HIGH[spc_info[0]] * phycon.ANG2BOHR vals = automol.zmat.value_dictionary(zma) job_geo = automol.zmat.set_values_by_name( zma, {'R1': vals['R1'] + disp}) is_atom = automol.geom.is_atom(automol.zmat.geometry(job_geo)) else: ioprinter.warning_message( f'Need a zma for high-symmetry of {spc_info[0]}.', 'Skipping task') job_geo = None is_atom = False else: if zma is not None: job_geo = zma is_atom = automol.geom.is_atom(automol.zmat.geometry(job_geo)) else: job_geo = geo is_atom = automol.geom.is_atom(job_geo) if is_atom: ioprinter.info_message('Species is an atom, Skipping VPT2 task.') if job_geo is not None and not is_atom: exists = geo_save_fs[-1].file.anharmonicity_matrix.exists(locs) if not exists: ioprinter.info_message( 'No X-Matrix found in save filesys. Running VPT2...') _run = True elif overwrite: ioprinter.info_message( 'User specified to overwrite VPT2 info with new run...') _run = True else: _run = False if _run: success, ret = es_runner.execute_job( job=elstruct.Job.VPT2, script_str=script_str, run_fs=run_fs, geo=job_geo, spc_info=spc_info, thy_info=thy_info, zrxn=zrxn, overwrite=overwrite, retryfail=retryfail, **kwargs, ) if success: inf_obj, inp_str, out_str = ret ioprinter.info_message( " - Reading anharmonicities from output...") vpt2_dct = elstruct.reader.vpt2(inf_obj.prog, out_str) ioprinter.save_anharmonicity(geo_save_path) geo_save_fs[-1].file.vpt2_input.write(inp_str, locs) geo_save_fs[-1].file.anharmonic_frequencies.write( vpt2_dct['freqs'], locs) geo_save_fs[-1].file.anharmonic_zpve.write( vpt2_dct['zpve'], locs) geo_save_fs[-1].file.vibro_rot_alpha_matrix.write( vpt2_dct['vibrot_mat'], locs) geo_save_fs[-1].file.quartic_centrifugal_dist_consts.write( vpt2_dct['cent_dist_const'], locs) geo_save_fs[-1].file.anharmonicity_matrix.write( vpt2_dct['x_mat'], locs) geo_save_fs[-1].file.cubic_force_constants.write( vpt2_dct['cubic_fc'], locs) geo_save_fs[-1].file.quartic_force_constants.write( vpt2_dct['quartic_fc'], locs) else: ioprinter.existing_path('VPT2 information', geo_save_path)
def _optimize_molecule(spc_info, zma_init, method_dct, cnf_save_fs, locs, run_fs, overwrite, kickoff_size=0.1, kickoff_backward=False): """ Optimize a proper geometry """ thy_info = tinfo.from_dct(method_dct) mod_thy_info = tinfo.modify_orb_label(thy_info, spc_info) script_str, kwargs = es_runner.qchem_params(method_dct, job=elstruct.Job.OPTIMIZATION) # Call the electronic structure optimizer success, ret = es_runner.execute_job(job=elstruct.Job.OPTIMIZATION, script_str=script_str, run_fs=run_fs, geo=zma_init, spc_info=spc_info, thy_info=mod_thy_info, overwrite=overwrite, **kwargs) # read the geometry if success: inf_obj, _, out_str = ret geo = elstruct.reader.opt_geometry(inf_obj.prog, out_str) zma = elstruct.reader.opt_zmatrix(inf_obj.prog, out_str) if zma is None: zma = automol.geom.zmatrix(geo) geo_conn = bool(automol.geom.connected(geo)) # If connected, check for imaginary modes and fix them if possible if geo_conn: # Remove the imaginary mode geo, ret = remove_imag(geo, ret, spc_info, method_dct, run_fs, kickoff_size, kickoff_backward, kickoff_mode=0, overwrite=overwrite) # Recheck connectivity for imag-checked geometry if geo is not None: conf_found = True if automol.geom.connected(geo): ioprinter.info_message( 'Saving structure as the first conformer...', newline=1) filesys.save.conformer(ret, None, cnf_save_fs, mod_thy_info[1:], rng_locs=(locs[0], ), tors_locs=(locs[1], )) else: ioprinter.info_message('Saving disconnected species...') filesys.save.instability(zma_init, zma, cnf_save_fs, rng_locs=(locs[0], ), tors_locs=(locs[1], ), zma_locs=(0, )) else: ioprinter.warning_message('No geom found...', newline=1) conf_found = False else: ioprinter.info_message('Saving disconnected species...') conf_found = False filesys.save.instability(zma_init, zma, cnf_save_fs, rng_locs=(locs[0], ), tors_locs=(locs[1], ), zma_locs=(0, )) return conf_found