def _save_unique_conformer(ret, thy_info, cnf_save_fs, locs, saddle=False, zma_locs=(0, )): """ Save the conformer in the filesystem """ # Set the path to the conformer save filesystem cnf_save_path = cnf_save_fs[-1].path(locs) # Unpack the ret object and obtain the prog and method inf_obj, inp_str, out_str = ret prog = inf_obj.prog method = inf_obj.method # Read the energy and geom from the output ene = elstruct.reader.energy(prog, method, out_str) geo = elstruct.reader.opt_geometry(prog, out_str) zma = elstruct.reader.opt_zmatrix(prog, out_str) # Read the tra and graph if saddle: ts_min_cnf_locs, ts_min_path = filesys.mincnf.min_energy_conformer_locators( cnf_save_fs, thy_info) ts_min_zma_fs = fs.zmatrix(ts_min_path) print('ts_min_path test:', ts_min_path) tra = ts_min_zma_fs[-1].file.transformation.read(zma_locs) print('zma_locs test:', zma_locs) rct_gra = ts_min_zma_fs[-1].file.reactant_graph.read(zma_locs) # Build the conformer filesystem and save the structural info print(" - Geometry is unique. Saving...") print(" - Save path: {}".format(cnf_save_path)) cnf_save_fs[-1].create(locs) cnf_save_fs[-1].file.geometry_info.write(inf_obj, locs) cnf_save_fs[-1].file.geometry_input.write(inp_str, locs) cnf_save_fs[-1].file.energy.write(ene, locs) cnf_save_fs[-1].file.geometry.write(geo, locs) # Build the zma filesystem and save the z-matrix zma_save_fs = fs.zmatrix(cnf_save_path) zma_save_fs[-1].create(zma_locs) zma_save_fs[-1].file.geometry_info.write(inf_obj, zma_locs) zma_save_fs[-1].file.geometry_input.write(inp_str, zma_locs) zma_save_fs[-1].file.zmatrix.write(zma, zma_locs) # Save the tra and gra for a saddle if saddle: zma_save_fs[-1].file.transformation.write(tra, zma_locs) zma_save_fs[-1].file.reactant_graph.write(rct_gra, zma_locs) # Saving the energy to a SP filesystem print(" - Saving energy of unique geometry...") sp_save_fs = autofile.fs.single_point(cnf_save_path) sp_save_fs[-1].create(thy_info[1:4]) sp_save_fs[-1].file.input.write(inp_str, thy_info[1:4]) sp_save_fs[-1].file.info.write(inf_obj, thy_info[1:4]) sp_save_fs[-1].file.energy.write(ene, thy_info[1:4])
def zma_fs_from_prefix(prefix, zma_idxs=(0, )): """ Build a zma filesys object """ zma_fs = fs.zmatrix(prefix) zma_fs[-1].create(zma_idxs) zma_path = zma_fs[-1].path(zma_idxs) return zma_fs, zma_path
def _ts_geo_viable(zma, zrxn, cnf_save_fs, mod_thy_info, zma_locs=(0, )): """ Perform a series of checks to assess the viability of a transition state geometry prior to saving """ # Obtain the min-ene zma and bond keys _, cnf_save_path = filesys.mincnf.min_energy_conformer_locators( cnf_save_fs, mod_thy_info) zma_save_fs = fs.zmatrix(cnf_save_path) ref_zma = zma_save_fs[-1].file.zmatrix.read(zma_locs) return automol.reac.similar_saddle_point_structure(zma, ref_zma, zrxn)
def all_rxn_bnd_keys(cnf_fs, cnf_locs, zma_locs=(0,)): """ get bond broken and formed keys for a transition state """ cnf_path = cnf_fs[-1].path(cnf_locs) zma_fs = fs.zmatrix(cnf_path) tra = zma_fs[-1].file.transformation.read(zma_locs) frm_bnd_keys, brk_bnd_keys = tra print('rxn keys') print(frm_bnd_keys) print(brk_bnd_keys) return frm_bnd_keys, brk_bnd_keys
def rxn_bnd_keys2(path, zma_locs=(0,)): """ get bond broken and formed keys for a transition state """ print('zma path', path) zma_fs = fs.zmatrix(path) tra = zma_fs[-1].file.transformation.read(zma_locs) frm_bnd_keys, brk_bnd_keys = tra if frm_bnd_keys: frm_bnd_keys = next(iter(frm_bnd_keys)) if brk_bnd_keys: brk_bnd_keys = next(iter(brk_bnd_keys)) return frm_bnd_keys, brk_bnd_keys
def rxn_bnd_keys(cnf_fs, cnf_locs, zma_locs=(0,)): """ get bond broken and formed keys for a transition state """ print('cnf locs', cnf_locs) cnf_path = cnf_fs[-1].path(cnf_locs) zma_fs = fs.zmatrix(cnf_path) tra = zma_fs[-1].file.transformation.read(zma_locs) frm_bnd_keys, brk_bnd_keys = tra print('rxn keys') print(frm_bnd_keys) print(brk_bnd_keys) if frm_bnd_keys: frm_bnd_keys = next(iter(frm_bnd_keys)) if brk_bnd_keys: brk_bnd_keys = next(iter(brk_bnd_keys)) return frm_bnd_keys, brk_bnd_keys
def build_rotors(spc_dct_i, pf_filesystems, spc_mod_dct_i, read_potentials=True): """ Add more rotor info """ run_prefix = pf_filesystems['run_prefix'] spc_info = sinfo.from_dct(spc_dct_i) spc_fml = automol.inchi.formula_string(spc_info[0]) if spc_fml is None: spc_fml = 'TS' run_path = job_path(run_prefix, 'PROJROT', 'FREQ', spc_fml, locs_idx=None) # Set up tors level filesystem and model and level tors_model = spc_mod_dct_i['tors']['mod'] tors_ene_info = spc_mod_dct_i['tors']['enelvl'][1][1] mod_tors_ene_info = tinfo.modify_orb_label( tors_ene_info, sinfo.from_dct(spc_dct_i)) rotors = None if pf_filesystems['tors'] is not None: [cnf_fs, cnf_save_path, min_cnf_locs, _, _] = pf_filesystems['tors'] # Build the rotors ref_ene = filesys.read.energy(cnf_fs, min_cnf_locs, mod_tors_ene_info) zma_fs = fs.zmatrix(cnf_fs[-1].path(min_cnf_locs)) if ( zma_fs[-1].file.torsions.exists([0]) and zma_fs[-1].file.zmatrix.exists([0]) and tors_model != 'rigid' ): rotors = automol.rotor.from_data( zma=zma_fs[-1].file.zmatrix.read([0]), tors_inf_dct=zma_fs[-1].file.torsions.read([0]), tors_names=spc_dct_i.get('tors_names', None), multi=bool('1d' in tors_model)) # Read the potential grids if read_potentials and rotors is not None: rotors = _read_potentials( rotors, spc_dct_i, run_path, cnf_save_path, ref_ene, mod_tors_ene_info, tors_model) return rotors
def save_saddle_point(opt_ret, hess_ret, freqs, imags, mod_thy_info, cnf_save_fs, ts_save_fs, ts_save_path, frm_bnd_keys, brk_bnd_keys, rcts_gra, zma_locs=(0, )): """ Optimize the transition state structure obtained from the grid search """ # Read the geom, energy, and Hessian from output opt_inf_obj, opt_inp_str, opt_out_str = opt_ret opt_prog = opt_inf_obj.prog opt_method = opt_inf_obj.method ene = elstruct.reader.energy(opt_prog, opt_method, opt_out_str) geo = elstruct.reader.opt_geometry(opt_prog, opt_out_str) zma = elstruct.reader.opt_zmatrix(opt_prog, opt_out_str) print('TS Geometry:') print(automol.geom.string(geo)) print() # Build new zma using x2z and new torsion coordinates # zma = automol.geometry.zmatrix( # geo, ts_bnd=(frm_bnd_keys, brk_bnd_keys)) print(" - Reading hessian from output...") hess_inf_obj, hess_inp_str, hess_out_str = hess_ret hess_prog = hess_inf_obj.prog hess = elstruct.reader.hessian(hess_prog, hess_out_str) freqs = sorted([-1.0 * val for val in imags] + freqs) print('TS freqs: {}'.format(' '.join(str(freq) for freq in freqs))) # Save the information into the filesystem print(" - Saving...") print(" - Save path: {}".format(ts_save_path)) # Save geom in the upper theory/TS layer ts_save_fs[0].file.geometry.write(geo) # Save this structure as first conformer locs = [autofile.schema.generate_new_conformer_id()] cnf_save_fs[-1].create(locs) cnf_save_fs[-1].file.geometry_info.write(opt_inf_obj, locs) cnf_save_fs[-1].file.geometry_input.write(opt_inp_str, locs) cnf_save_fs[-1].file.hessian_info.write(hess_inf_obj, locs) cnf_save_fs[-1].file.hessian_input.write(hess_inp_str, locs) cnf_save_fs[-1].file.energy.write(ene, locs) cnf_save_fs[-1].file.geometry.write(geo, locs) cnf_save_fs[-1].file.hessian.write(hess, locs) cnf_save_fs[-1].file.harmonic_frequencies.write(freqs, locs) cnf_save_path = cnf_save_fs[-1].path(locs) # Save the zmatrix information in a zma filesystem cnf_save_path = cnf_save_fs[-1].path(locs) zma_save_fs = fs.zmatrix(cnf_save_path) zma_save_fs[-1].create(zma_locs) zma_save_fs[-1].file.geometry_info.write(opt_inf_obj, zma_locs) zma_save_fs[-1].file.geometry_input.write(opt_inp_str, zma_locs) zma_save_fs[-1].file.zmatrix.write(zma, zma_locs) # Save the form and break keys in the filesystem tra = (frozenset({frm_bnd_keys}), frozenset({brk_bnd_keys})) zma_save_fs[-1].file.transformation.write(tra, zma_locs) zma_save_fs[-1].file.reactant_graph.write(rcts_gra, zma_locs) # Save the energy in a single-point filesystem print(" - Saving energy...") sp_save_fs = autofile.fs.single_point(cnf_save_path) sp_save_fs[-1].create(mod_thy_info[1:4]) sp_save_fs[-1].file.input.write(opt_inp_str, mod_thy_info[1:4]) sp_save_fs[-1].file.info.write(opt_inf_obj, mod_thy_info[1:4]) sp_save_fs[-1].file.energy.write(ene, mod_thy_info[1:4])
def read_hr_pot(tors_names, tors_grids, cnf_save_path, mod_tors_ene_info, ref_ene, constraint_dct, read_geom=False, read_grad=False, read_hess=False, read_zma=False): """ Get the potential for a hindered rotor """ # Build initial lists for storing potential energies and Hessians grid_points, grid_vals = set_scan_dims(tors_grids) pot, geoms, grads, hessians, zmas, paths = {}, {}, {}, {}, {}, {} # Set up filesystem information zma_fs = fs.zmatrix(cnf_save_path) zma_path = zma_fs[-1].path([0]) if constraint_dct is None: scn_fs = autofile.fs.scan(zma_path) else: scn_fs = autofile.fs.cscan(zma_path) # Read the energies and Hessians from the filesystem for point, vals in zip(grid_points, grid_vals): locs = [tors_names, vals] if constraint_dct is not None: locs = [constraint_dct] + locs ene = read_tors_ene(scn_fs, locs, mod_tors_ene_info) if ene is not None: pot[point] = (ene - ref_ene) * phycon.EH2KCAL else: pot[point] = -10.0 # print('path test in read_hr_pot:', scn_fs[-1].path(locs)) if read_geom: if scn_fs[-1].file.geometry.exists(locs): geoms[point] = scn_fs[-1].file.geometry.read(locs) else: geoms[point] = None if read_grad: if scn_fs[-1].file.gradient.exists(locs): grads[point] = scn_fs[-1].file.gradient.read(locs) else: grads[point] = None if read_hess: if scn_fs[-1].file.hessian.exists(locs): hessians[point] = scn_fs[-1].file.hessian.read(locs) else: hessians[point] = None if read_zma: if scn_fs[-1].file.zmatrix.exists(locs): zmas[point] = scn_fs[-1].file.zmatrix.read(locs) else: zmas[point] = None paths[point] = scn_fs[-1].path(locs) return pot, geoms, grads, hessians, zmas, paths
def _ts_geo_viable(zma, cnf_save_fs, rxn_class, mod_thy_info, zma_locs=(0, )): """ Perform a series of checks to assess the viability of a transition state geometry prior to saving """ # Initialize viable viable = True # Obtain the min-ene zma and bond keys min_cnf_locs, cnf_save_path = filesys.mincnf.min_energy_conformer_locators( cnf_save_fs, mod_thy_info) zma_save_fs = fs.zmatrix(cnf_save_path) ref_zma = zma_save_fs[-1].file.zmatrix.read(zma_locs) # Read the form and broken keys from the min conf # frm_bnd_keys, brk_bnd_keys = tsprep.rxn_bnd_keys( frm_bnd_keys, brk_bnd_keys = tsprep.all_rxn_bnd_keys(cnf_save_fs, min_cnf_locs, zma_locs=zma_locs) # Use the idxs to set the forming and breaking bond names # if frm_bnd_keys: # frm_name = automol.zmatrix.bond_key_from_idxs( # zma, frm_bnd_keys) # ts_bnd1, ts_bnd2 = min(frm_bnd_keys), max(frm_bnd_keys) # else: # frm_name = '' # ts_bnd1, ts_bnd2 = None, None # if brk_bnd_keys: # brk_name = automol.zmatrix.bond_key_from_idxs( # zma, brk_bnd_keys) # else: # brk_name = '' # print('frm_name', frm_name) # print('brk_name', brk_name) # Calculate the distance of bond being formed # cnf_dct = automol.zmatrix.values(zma) # ref_dct = automol.zmatrix.values(ref_zma) cnf_geo = automol.zmatrix.geometry(zma) ref_geo = automol.zmatrix.geometry(ref_zma) cnf_dist_lst = [] ref_dist_lst = [] bnd_key_lst = [] cnf_ang_lst = [] ref_ang_lst = [] for frm_bnd_key in frm_bnd_keys: print('frm_bnd_key test:', frm_bnd_key) frm_idx1, frm_idx2 = list(frm_bnd_key) cnf_dist = automol.geom.distance(cnf_geo, frm_idx1, frm_idx2) ref_dist = automol.geom.distance(ref_geo, frm_idx1, frm_idx2) cnf_dist_lst.append(cnf_dist) ref_dist_lst.append(ref_dist) bnd_key_lst.append(frm_bnd_key) for brk_bnd_key in brk_bnd_keys: brk_idx1, brk_idx2 = list(brk_bnd_key) cnf_dist = automol.geom.distance(cnf_geo, brk_idx1, brk_idx2) ref_dist = automol.geom.distance(ref_geo, brk_idx1, brk_idx2) cnf_dist_lst.append(cnf_dist) ref_dist_lst.append(ref_dist) bnd_key_lst.append(brk_bnd_key) for frm_bnd_key in frm_bnd_keys: for brk_bnd_key in brk_bnd_keys: for frm_idx in frm_bnd_key: for brk_idx in brk_bnd_key: if frm_idx == brk_idx: idx2 = frm_idx idx1 = list(frm_bnd_key - frozenset({idx2}))[0] idx3 = list(brk_bnd_key - frozenset({idx2}))[0] cnf_ang = automol.geom.central_angle( cnf_geo, idx1, idx2, idx3) ref_ang = automol.geom.central_angle( ref_geo, idx1, idx2, idx3) cnf_ang_lst.append(cnf_ang) ref_ang_lst.append(ref_ang) # cnf_dist = cnf_dct.get(frm_name, None) # ref_dist = ref_dct.get(frm_name, None) # if cnf_dist is None: # cnf_dist = cnf_dct.get(brk_name, None) # if ref_dist is None: # ref_dist = ref_dct.get(brk_name, None) print('bnd_key_list', bnd_key_lst) print('conf_dist', cnf_dist_lst) print('ref_dist', ref_dist_lst) print('conf_angle', cnf_ang_lst) print('ref_angle', ref_ang_lst) # # Calculate the central angle of reacting moiety of zma # cnf_angle = geomprep.calc_rxn_angle( # zma, frm_bnd_keys, brk_bnd_keys, rxn_class) # ref_angle = geomprep.calc_rxn_angle( # ref_zma, frm_bnd_keys, brk_bnd_keys, rxn_class) # print('conf_angle', cnf_angle) # print('ref_angle', ref_angle) # Set the maximum allowed displacement for a TS conformer max_disp = 0.6 # would be better to check for bond forming length in bond scission with ring forming if 'addition' in rxn_class: max_disp = 0.8 if 'abstraction' in rxn_class: # this was 1.4 - SJK reduced it to work for some OH abstractions max_disp = 1.0 # Check forming bond angle similar to ini config if 'elimination' not in rxn_class: for ref_angle, cnf_angle in zip(ref_ang_lst, cnf_ang_lst): if abs(cnf_angle - ref_angle) > .44: print( " - Transition State conformer has", "diverged from original structure of", "angle {:.3f} with angle {:.3f}".format( ref_angle, cnf_angle)) viable = False symbols = automol.geom.symbols(cnf_geo) lst_info = zip(ref_dist_lst, cnf_dist_lst, bnd_key_lst) for ref_dist, cnf_dist, bnd_key in lst_info: if 'add' in rxn_class or 'abst' in rxn_class: bnd_key1, bnd_key2 = min(list(bnd_key)), max(list(bnd_key)) symb1 = symbols[bnd_key1] symb2 = symbols[bnd_key2] if bnd_key in frm_bnd_keys: # Check if radical atom is closer to some atom # other than the bonding atom cls = geomprep.is_atom_closest_to_bond_atom( zma, bnd_key2, cnf_dist) if not cls: print( " - Transition State conformer has", "diverged from original structure of", "dist {:.3f} with dist {:.3f}".format( ref_dist, cnf_dist)) print(' - Radical atom now has a new nearest neighbor') viable = False # check forming bond distance if abs(cnf_dist - ref_dist) > max_disp: print( " - Transition State conformer has", "diverged from original structure of", "dist {:.3f} with dist {:.3f}".format( ref_dist, cnf_dist)) viable = False # Check distance relative to equi. bond if (symb1, symb2) in bnd.LEN_DCT: equi_bnd = bnd.LEN_DCT[(symb1, symb2)] elif (symb2, symb1) in bnd.LEN_DCT: equi_bnd = bnd.LEN_DCT[(symb2, symb1)] else: equi_bnd = 0.0 displace_from_equi = cnf_dist - equi_bnd dchk1 = abs(cnf_dist - ref_dist) > 0.1 dchk2 = displace_from_equi < 0.2 if dchk1 and dchk2: print( " - Transition State conformer has", "converged to an", "equilibrium structure with dist", " {:.3f} comp with equil {:.3f}".format( cnf_dist, equi_bnd)) viable = False else: # check forming/breaking bond distance # if abs(cnf_dist - ref_dist) > 0.4: # max disp of 0.4 causes problems for bond scission with ring forming # not sure if setting it to 0.3 will cause problems for other cases if abs(cnf_dist - ref_dist) > 0.3: print( " - Transition State conformer has", "diverged from original structure of", "dist {:.3f} with dist {:.3f}".format(ref_dist, cnf_dist)) viable = False return viable
def build_rotors(spc_dct_i, pf_filesystems, pf_models, pf_levels, rxn_class='', frm_bnd_keys=(), brk_bnd_keys=()): """ Add more rotor info """ saddle = bool(rxn_class and (frm_bnd_keys or brk_bnd_keys)) # Set up tors level filesystem and model and level print('pflvls', pf_levels) tors_model = pf_models['tors'] tors_ene_info = pf_levels['tors'][1][0] mod_tors_ene_info = filesys.inf.modify_orb_restrict( filesys.inf.get_spc_info(spc_dct_i), tors_ene_info) [cnf_fs, cnf_save_path, min_cnf_locs, _, _] = pf_filesystems['tors'] # Grab the zmatrix if min_cnf_locs is not None: zma_fs = fs.zmatrix(cnf_fs[-1].path(min_cnf_locs)) zma = zma_fs[-1].file.zmatrix.read([0]) remdummy = geomprep.build_remdummy_shift_lst(zma) geo = cnf_fs[-1].file.geometry.read(min_cnf_locs) # Read the reference energy ref_ene = torsprep.read_tors_ene(cnf_fs, min_cnf_locs, mod_tors_ene_info) # Set the tors names rotor_inf = _rotor_info(zma, spc_dct_i, cnf_fs, min_cnf_locs, tors_model, frm_bnd_keys=frm_bnd_keys, brk_bnd_keys=brk_bnd_keys) # Read the potential energy surface for the rotors num_rotors = len(rotor_inf[0]) rotors = [] for tors_names, tors_grids, tors_syms in zip(*rotor_inf): # Initialize dct to hold info for each torsion of rotor rotor_dct = {} # Read the potential along the rotors if tors_model in ('mdhr', 'mdhrv'): # Set to read additional info for vibrational adiabaticity if tors_model == 'mdhrv': read_geom, read_grad, read_hess = True, True, True else: read_geom, read_grad, read_hess = False, False, False # Read and MDHR potential for single MDHR rotor # Could be MDHR mod for sys w/ 1 Rotor if ((num_rotors > 1 and len(tors_names) > 1) or num_rotors == 1): pot, geoms, grads, hessians, _, _ = torsprep.read_hr_pot( tors_names, tors_grids, cnf_save_path, mod_tors_ene_info, ref_ene, constraint_dct=None, # No extra frozen treatments read_geom=read_geom, read_grad=read_grad, read_hess=read_hess) rotor_dct['mdhr_pot_data'] = (pot, geoms, grads, hessians) for tname, tgrid, tsym in zip(tors_names, tors_grids, tors_syms): # Build constraint dct if tors_model == '1dhrf': tname_tup = tuple([tname]) const_names = tuple(itertools.chain(*rotor_inf[0])) constraint_dct = torsprep.build_constraint_dct( zma, const_names, tname_tup) elif tors_model == '1dhrfa': coords = list(automol.zmatrix.coordinates(zma)) const_names = tuple(coord for coord in coords) tname_tup = tuple([tname]) constraint_dct = torsprep.build_constraint_dct( zma, const_names, tname_tup) else: constraint_dct = None # Call read pot for 1DHR pot, _, _, _, _, _ = torsprep.read_hr_pot([tname], [tgrid], cnf_save_path, mod_tors_ene_info, ref_ene, constraint_dct) pot = _hrpot_spline_fitter(pot, min_thresh=-0.0001, max_thresh=50.0) # Get the HR groups and axis for the rotor group, axis, pot, sym_num = torsprep.set_tors_def_info( zma, tname, tsym, pot, frm_bnd_keys, brk_bnd_keys, rxn_class, saddle=saddle) remdummy = geomprep.build_remdummy_shift_lst(zma) # Get the indices for the torsion mode_idxs = automol.zmatrix.coord_idxs(zma, tname) mode_idxs = tuple((idx + 1 for idx in mode_idxs)) # Determine the flux span using the symmetry number mode_span = 360.0 / tsym # Build dictionary for the torsion keys = [ 'group', 'axis', 'sym_num', 'remdummy', 'pot', 'atm_idxs', 'span', 'hrgeo' ] vals = [ group, axis, sym_num, remdummy, pot, mode_idxs, mode_span, geo ] rotor_dct[tname] = dict(zip(keys, vals)) # print('rotors pot test:', pot) # Append to lst rotors.append(rotor_dct) return rotors