def test_rotor_symmetry_determination(self): """ Test that the correct symmetry number is determined for rotor potential scans. """ path1 = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'NCC_NRotor.out') path2 = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'data', 'NCC_CRotor.out') scan_log1 = QChemLog(path1) scan_log2 = QChemLog(path2) v_list1, angle = scan_log1.load_scan_energies() v_list2, angle = scan_log2.load_scan_energies() symmetry1 = determine_rotor_symmetry(energies=v_list1, label='NCC', pivots=[]) symmetry2 = determine_rotor_symmetry(energies=v_list2, label='NCC', pivots=[]) self.assertEqual(symmetry1, 1) self.assertEqual(symmetry2, 3)
def sampling_along_torsion(symbols, cart_coords, mode, internal_object, conformer, int_freq, rotors_dict, scan_res, path, thresh, ncpus, charge=None, multiplicity=None, rem_variables_dict=None, gen_basis="", is_QM_MM_INTERFACE=None, QM_USER_CONNECT=None, QM_ATOMS=None, force_field_params=None, fixed_molecule_string=None, opt=None, label=None): logging.info('Sampling Mode {}'.format(mode)) XyzDictOfEachMode = {} EnergyDictOfEachMode = {} ModeDictOfEachMode = {} # Extract rotor information pivots = rotors_dict[mode]['pivots'] top = rotors_dict[mode]['top'] scan = rotors_dict[mode]['scan'] # Determine sampling step size step_size = np.pi / (180 / scan_res) # Save information of this mode ModeDictOfEachMode['mode'] = 'tors' ModeDictOfEachMode['M'] = conformer.get_internal_reduced_moment_of_inertia( pivots, top) * constants.Na * 1e23 # in amu*angstrom^2 ModeDictOfEachMode['K'] = (int_freq * (2 * np.pi * constants.c * 100))**2 # in 1/s^2 ModeDictOfEachMode['step_size'] = step_size # in radian # Create the unit vector, qk, for displacement along the kth bond torsion n_rotors = len(rotors_dict) internal = copy.deepcopy(internal_object) scan_indices = internal.B_indices[-n_rotors:] torsion_ind = len(internal.B_indices) - n_rotors + scan_indices.index( [ind - 1 for ind in scan]) B = internal.B nrow = B.shape[0] qk = np.zeros(nrow, dtype=int) qk[torsion_ind] = 1 if internal.prim_coords[torsion_ind] > 0: qk *= -1 # Start to sample 1-D PES logging.info('direction = 1') nsample = int(360 / scan_res) + 1 initial_geometry = cart_coords.copy() cart_coords = initial_geometry.copy() fail_in_torsion_sampling = False for sample in range(nsample): xyz = getXYZ(symbols, cart_coords) file_name = 'tors_{}_{}'.format(mode, sample) # Calculate electronic energy of each sampling point, and save the result if is_QM_MM_INTERFACE: e_elec = get_electronic_energy(xyz, path, file_name, ncpus, charge, multiplicity, rem_variables_dict, gen_basis, is_QM_MM_INTERFACE, QM_USER_CONNECT, QM_ATOMS, force_field_params, fixed_molecule_string, opt) else: e_elec = get_electronic_energy(xyz, path, file_name, ncpus, charge, multiplicity, rem_variables_dict, gen_basis) XyzDictOfEachMode[sample] = xyz # Take the electronic energy of stationary point as reference energy, min_elect if sample == 0: EnergyDictOfEachMode[sample] = 0 min_elect = e_elec else: logging.info('ngrid = {}'.format(sample)) EnergyDictOfEachMode[sample] = e_elec - min_elect # Check if this mode is a high-barrier hindered rotation if e_elec - min_elect > thresh: # Treat this mode as a vibrational normal mode fail_in_torsion_sampling = True logging.info( '\n***********************************************************************' ) logging.info( 'Since the torsional barrier of mode {} is higher than {} hartree.' .format(mode, thresh)) logging.info( 'This mode will be treated as vibrational mode later on.') logging.info( '***********************************************************************\n' ) return {}, {}, {}, min_elect # Update cartesian coordinate of each sampling point cart_coords += internal.transform_int_step( (qk * step_size).reshape(-1, )) * BOHR2ANG # Determine the symmetry number of this internal rotation, and save the result if fail_in_torsion_sampling is False: v_list = [ i * (constants.E_h * constants.Na) for i in EnergyDictOfEachMode.values() ] # in J/mol symmetry_number = determine_rotor_symmetry(v_list, label, pivots) # symmetry_number = 3 logging.info('\n') ModeDictOfEachMode['symmetry_number'] = symmetry_number return XyzDictOfEachMode, EnergyDictOfEachMode, ModeDictOfEachMode, min_elect