def load_save(self): self.sampling = SamplingJob(self.label, self.input_file, ncpus=self.ncpus) self.sampling.parse() self.is_QM_MM_INTERFACE = self.sampling.is_QM_MM_INTERFACE self.conformer = self.sampling.conformer self.csv_path = os.path.join( self.output_directory, '{}_samping_result.csv'.format(self.label)) self.mode_dict, self.energy_dict, self.min_elect = from_sampling_result( self.csv_path) self.zpe_of_Hohf = self.sampling.zpe e0 = self.min_elect * constants.E_h * constants.Na + self.sampling.zpe self.conformer.E0 = (e0, "J/mol") for mode in self.conformer.modes: if isinstance(mode, HarmonicOscillator): frequencies = mode.frequencies.value_si mode.frequencies = (frequencies * self.frequency_scale_factor, "cm^-1") self.spin_multiplicity = self.conformer.spin_multiplicity self.optical_isomers = self.conformer.optical_isomers self.symbols = self.sampling.symbols # Solve SE of 1-D PES and calculate E S G Cp self.polynomial_dict = cubic_spline_interpolations( self.energy_dict, self.mode_dict) # Extract whether this system is QM/MM system or not self.is_QM_MM_INTERFACE = self.sampling.is_QM_MM_INTERFACE
def main(): args = parse_command_line_arguments() input_file = args.file.split('/')[-1] ncpus = args.n protocol = args.p T = args.T nsamples = args.ns if args.ns is not None else 1000 nchains = args.nc if args.nc is not None else 5 nburn = args.nburn if args.nburn is not None else int(nsamples / 5) hpc = args.hpc if not protocol: protocol = 'TNUTS' if not T: T = 300 project_directory = os.path.abspath(os.path.dirname(args.file)) # SCRATCH directory must be set before script runs # This is done for ease of transferability to different hardware setups output_directory = os.path.expandvars('$SCRATCH') # imaginary bonds for QMMM calculation # atom indices starts from 1 imaginary_bonds = args.i if args.i is not None: imaginary_bonds_string = imaginary_bonds.strip('[').strip(']') imaginary_bonds = [] for bond in imaginary_bonds_string.split(','): atom1, atom2 = bond.split('-') imaginary_bonds.append([int(atom1), int(atom2)]) label = input_file.split('.')[0] Log = QChemLog(os.path.join(project_directory, input_file)) level_of_theory_kwargs = get_level_of_theory(Log) samp_object = SamplingJob(input_file=os.path.join(project_directory, input_file), label=label, ncpus=ncpus, output_directory=output_directory, protocol=protocol, thresh=0.5, **level_of_theory_kwargs) samp_object.parse() # With APE updates, should edit APE sampling.py to [only] sample torsions #xyz_dict, energy_dict, mode_dict = samp_obj.sampling() priors = ['umvt'] methods = ['NUTS', 'HMC', 'MH'] sampling_kwargs = dict(draws=nsamples, chains=nchains, tune=nburn) MCObj = MCMCTorsions(samp_object, T, sampling_kwargs, hpc=hpc, ncpus=ncpus) step_kwargs = dict() methods = ['NUTS'] priors = ['umvt'] for prior in priors: for method in methods: MCObj.sample(prior, method, **step_kwargs)
def species(label, *args, **kwargs): """Load a species from an input file""" global species_dict, job_list, directory if label in species_dict: raise ValueError( 'Multiple occurrences of species with label {0!r}.'.format(label)) logging.info('Loading species {0}...'.format(label)) spec = Species(label=label) species_dict[label] = spec path = None if len(args) == 1: # The argument is a path to a conformer input file path = os.path.join(directory, args[0]) spec.path = path job = SamplingJob(label=label, input_file=path, output_directory=output_directory) spec.conformer, unscaled_frequencies = QChemLog(path).load_conformer() job_list.append(job) elif len(args) > 1: raise InputError('species {0} can only have two non-keyword argument ' 'which should be the species label and the ' 'path to a quantum file.'.format(spec.label)) if len(kwargs) > 0: # The species parameters are given explicitly protocol = 'UMVT' E0 = None multiplicity = None charge = None rotors = None for key, value in kwargs.items(): if key == 'protocol': protocol = value.upper() elif key == 'E0': E0 = value elif key == 'multiplicity': multiplicity = value elif key == 'charge': charge = value elif key == 'rotors': rotors = value else: raise TypeError( 'species() got an unexpected keyword argument {0!r}.'. format(key)) spec.conformer.E0 = E0 job.protocol = protocol job.multiplicity = multiplicity job.charge = charge job.rotors = rotors return spec
def __init__(self, results_directory, T, sampT=300, samp_obj=None, model=None, P=101325, t_protocols=['PG', 'UMVT', 'HO'], t_subprotocols=['C', 'U'], sb_protocols=['HO', 'UMVT'], sb_subprotocols=['HO', 'UMVT']): self.resdir = results_directory self.model = model self.sampT = sampT self.T = T self.rat = self.sampT / self.T self.P = P ### self.acm, self.bEcm, self.Ecvar,\ self.DEcm, self.S = create_dfs(self.resdir, sampT=self.sampT) ### self.samp_obj = samp_obj # Reiteration of 'load_save' from ape.statmech # Care should be taken to move '.csv' sampling result and the # '.out' input file to the results directory if self.samp_obj is None: for f in os.listdir(self.resdir): if not '.out' in f: continue input_f = f break input_file = os.path.join(self.resdir, input_f) label = input_f.split('.')[0] self.samp_obj = SamplingJob(label, input_file, protocol='UMVT') self.samp_obj.parse() self.conformer = self.samp_obj.conformer self.label = self.samp_obj.label self.Thermo = ThermoJob(self.label, self.samp_obj.input_file, output_directory=self.resdir, P=self.P) self.Thermo.load_save() #self.tmodes = dicts_to_NModes(self.Thermo.mode_dict, # self.Thermo.energy_dict, # xyz_dict=None, # just_tors=True) #self.NModes self.t_protocols = np.atleast_1d(t_protocols) self.t_subprotocols = np.atleast_1d(t_subprotocols) self.sb_protocols = np.atleast_1d(sb_protocols) self.sb_subprotocols = np.atleast_1d(sb_subprotocols) # Data frame initialization # label | mode | prot | sub | sbprot | sbsub | E0 | E | S | Cv | Q # ----- | ---- | ---- | --- | ------ | ----- | -- | - | - | -- | - # Ex. | | | | | | | | | | # "s15" | "sb" | NaN | NaN| "ho" | "umvt"| α | β | γ | δ | ε # "s15" |"tors"| "pg" | "cc"| "umvt" | NaN | Α | B | Γ | Δ | Ε # "s15" | "rot"| NaN | NaN| NaN | NaN | ζ | η | θ | ι | κ # "s15" |"trns"| NaN | NaN| NaN | NaN | Ζ | Η | Θ | Ι | Κ # "s20" |"tors"| "pg" | "uu"| "ho" | "ho" | λ | μ | ν | ξ | π # "s1" |"tors"|"umvt"| NaN| NaN | NaN | Λ | Μ | Ν | Ξ | Π self.csv = os.path.join( self.resdir, "thermo{T}_{label}.csv".format(T=self.T, label=self.label)) self.total_thermo = get_data_frame(self.csv) self.data_frame = pd.DataFrame({'mode': []})
class MCThermoJob: """ The class to calculate thermodynamic properties Units are finalized in kcal/mol or cal/mol, with inputs in Hartree ZPE [=] kcal/mol E [=] kcal/mol S [=] cal/mol.K Cv [=] cal/mol.K """ def __init__(self, results_directory, T, sampT=300, samp_obj=None, model=None, P=101325, t_protocols=['PG', 'UMVT', 'HO'], t_subprotocols=['C', 'U'], sb_protocols=['HO', 'UMVT'], sb_subprotocols=['HO', 'UMVT']): self.resdir = results_directory self.model = model self.sampT = sampT self.T = T self.rat = self.sampT / self.T self.P = P ### self.acm, self.bEcm, self.Ecvar,\ self.DEcm, self.S = create_dfs(self.resdir, sampT=self.sampT) ### self.samp_obj = samp_obj # Reiteration of 'load_save' from ape.statmech # Care should be taken to move '.csv' sampling result and the # '.out' input file to the results directory if self.samp_obj is None: for f in os.listdir(self.resdir): if not '.out' in f: continue input_f = f break input_file = os.path.join(self.resdir, input_f) label = input_f.split('.')[0] self.samp_obj = SamplingJob(label, input_file, protocol='UMVT') self.samp_obj.parse() self.conformer = self.samp_obj.conformer self.label = self.samp_obj.label self.Thermo = ThermoJob(self.label, self.samp_obj.input_file, output_directory=self.resdir, P=self.P) self.Thermo.load_save() #self.tmodes = dicts_to_NModes(self.Thermo.mode_dict, # self.Thermo.energy_dict, # xyz_dict=None, # just_tors=True) #self.NModes self.t_protocols = np.atleast_1d(t_protocols) self.t_subprotocols = np.atleast_1d(t_subprotocols) self.sb_protocols = np.atleast_1d(sb_protocols) self.sb_subprotocols = np.atleast_1d(sb_subprotocols) # Data frame initialization # label | mode | prot | sub | sbprot | sbsub | E0 | E | S | Cv | Q # ----- | ---- | ---- | --- | ------ | ----- | -- | - | - | -- | - # Ex. | | | | | | | | | | # "s15" | "sb" | NaN | NaN| "ho" | "umvt"| α | β | γ | δ | ε # "s15" |"tors"| "pg" | "cc"| "umvt" | NaN | Α | B | Γ | Δ | Ε # "s15" | "rot"| NaN | NaN| NaN | NaN | ζ | η | θ | ι | κ # "s15" |"trns"| NaN | NaN| NaN | NaN | Ζ | Η | Θ | Ι | Κ # "s20" |"tors"| "pg" | "uu"| "ho" | "ho" | λ | μ | ν | ξ | π # "s1" |"tors"|"umvt"| NaN| NaN | NaN | Λ | Μ | Ν | Ξ | Π self.csv = os.path.join( self.resdir, "thermo{T}_{label}.csv".format(T=self.T, label=self.label)) self.total_thermo = get_data_frame(self.csv) self.data_frame = pd.DataFrame({'mode': []}) def execute(self): self.calcThermo(write=True) return self.data_frame, self.csv def calcThermo(self, write=True, print_output=True): """ Calculate component thermodynamic quantities. Unit conversions performed by each operation. Stored in self.data_frame data frame. """ self.calcTransThermo() self.calcRotThermo() for sb_protocol in self.sb_protocols: self.calcSBThermo(protocol=sb_protocol) for t_protocol in self.t_protocols: if t_protocol == 'PG': ############ Pitzer-Gwinn Methods ############ for t_subprotocol in self.t_subprotocols: ######### PG Classical Partition ######### for sb_protocol in self.sb_protocols: ######### Pitzer-Gwinn Factor ######### if sb_protocol == 'HO': # PG factor = HO ######### HO ω's ######### for sb_subprotocol in self.sb_subprotocols: self.calcTThermo(protocol=t_protocol, subprotocol=t_subprotocol, sb_protocol=sb_protocol, sb_subprotocol=sb_subprotocol) else: # PG factor = UMVT / MC self.calcTThermo(protocol=t_protocol, subprotocol=t_subprotocol, sb_protocol=sb_protocol, sb_subprotocol=None) else: # method is not PG (is UMVT) self.calcTThermo(protocol=t_protocol, subprotocol=None, sb_protocol=None, sb_subprotocol=None) self.total_thermo = pd.concat([self.data_frame], keys=[self.label], names=['species']) if write: self.total_thermo.to_csv(self.csv) if print_output: pass def calcTClassical(self, subprotocol): """ Calculate the classical torsional thermo properties using statistical mechanics. Unit conversions in situ performed before returning. """ if not subprotocol in ['C', 'U']: raise TypeError("Invalid subprotocol") ntors = self.samp_obj.n_rotors beta = 1 / (constants.kB * self.T) * constants.E_h # beta in Hartree #betac = beta*self.rat print("Ratio of temperatures is", self.rat) # Unit conversion constants: J2cal = 1. / 4.184 # 1cal / 4.184J Hartree2kcal = constants.E_h*\ constants.Na*J2cal/1000 # (J/H)*(1cal/4.184J)*1k ################################################# # Calculate torsional kinetic (T) contribution # ################################################# D = get_mass_matrix(None, self.T, self.Thermo.mode_dict, protocol="uncoupled") # SI R = 1.985877534e-3 # kcal/mol.K beta_si = 1. / (constants.kB * self.T) prefactor = 1. / (2 * np.pi * beta_si * np.power(constants.hbar, 2.)) QT = np.power(prefactor, ntors / 2) * np.power(np.linalg.det(D), 0.5) ET = 0.5 * ntors * R * self.T # ET in kcal/mol ST = R * (np.log(QT) + ntors / 2.) * 1000 #S in cal/mol.K CvT = R * ntors / 2. * 1000 # Cv in cal/mol.K EtclassU, StclassU, CvtclassU, QtclassU, Qv = 0, 0, 0, 1, 1 for mode in sorted(self.Thermo.mode_dict.keys()): if self.Thermo.mode_dict[mode]['mode'] != 'tors': continue # Calculate classical properties NMode = dict_to_NMode(mode, self.Thermo.mode_dict, self.Thermo.energy_dict, [], [], self.samp_obj) ec, sc, qc, qv, cvc =\ solvUMClass(NMode, self.T) EtclassU += ec StclassU += sc CvtclassU += cvc QtclassU *= qc Qv *= qv if "U" in subprotocol: return EtclassU, StclassU, CvtclassU, QtclassU elif 'C' in subprotocol: # Calculate coupled torsional PES contribution for all tors #QV = Qv*np.power(np.mean(self.trace.a), self.rat) #print("Kinetic pf, coupled:", QT) #print("Potential partition function, un/coupled:", Qv, QV) #print("Product:", QT*QV) ############# CHECK THIS #################### #EV = np.mean(self.trace.bE*self.rat)*R*self.T # E in kcal/mol #SV = R*(np.log(QV) + np.mean(self.trace.bE)*self.rat)*1000\ # # S in cal/mol.K #CvV = beta/self.T*\ # (np.var(self.trace.bE/betac))\ # *Hartree2kcal*1000 # Cv in cal/mol.K #QtclassC = QV*QT #EtclassC = EV+ET #StclassC = SV+ST #CvtclassC = CvV+CvT ############################################ # CUMULATIVE MEAN ########################## ############################################ Qcm = Qv * np.power(self.acm, self.rat) #* QT print("partition fn, fperturb;", Qcm) print("prior partition:", Qv) #Qcm = self.qcm * QT #print(Qcm) #print(np.log(Qcm)) Qcm.columns = Qcm.columns.str.replace("a", "E") Ecm = self.bEcm * self.rat * R * self.T + ET Scm = R * (np.log(Qcm).add( self.bEcm * self.rat)) * 1000 + ST # cal/mol.K print("Entropy is:", Scm) print("kinetic entropy is", ST) #Scm = R*self.S*1000 #Qcm = np.exp(-self.S - Ecm) #print(Scm) Cvcm = beta/self.T*\ self.Ecvar*Hartree2kcal*1000 + CvT # cal/mol.K return Ecm, Scm, Cvcm, Qcm return EtclassC, StclassC, CvtclassC, QtclassC def calcPGFactor(self, sb_protocol, sb_subprotocol): """ Calculate the thermodynamics associated with PG Q/Cl ratio. F refers to the ratio of q/cl partition fns, NOT Helmholtz f.e. This carries a quantum term and therefore a ZPE. Unit conversions performed by operations prior to returning. """ if not sb_protocol in ["HO", "UMVT"]: raise TypeError( "Invalid protocol for stretches/bends: valid options\ are 'HO' or 'UMVT'.") Qc, F, E0, DE, DS, DCv = 1, 1, 0, 0, 0, 0 if sb_protocol == "HO": if not sb_subprotocol in ["HO", "UMVT", "MC"]: raise TypeError( "Invalid subprotocol for stretches/bends: valid options\ are 'HO', 'UMVT', or 'MC'.") kwargs = { 'samp_obj': self.samp_obj, 'T': self.T, 'Thermo_obj': self.Thermo } ws = get_tors_freqs(protocol=sb_subprotocol, **kwargs) # s^-1 for i, w in enumerate(ws): # Calculate quantum HO properties e0, e, s, q, cv =\ solvHO(w, self.T) ec, sc, qc, cvc =\ solvCHO(w, self.T) F *= q / qc Qc *= qc E0 += e0 DE += e - ec DS += s - sc DCv += cv - cvc elif sb_protocol == "UMVT": for mode in sorted(self.Thermo.mode_dict.keys()): if self.Thermo.mode_dict[mode]['mode'] != 'tors': continue # Calculate quantum properties v, e0, e, s, f, q, cv =\ self.Thermo.SolvEig(mode, self.T) # Calculate classical properties NMode = dict_to_NMode(mode, self.Thermo.mode_dict, self.Thermo.energy_dict, [], [], self.samp_obj) ec, sc, qc, qv, cvc =\ solvUMClass(NMode, self.T) F *= q / qc Qc *= qc E0 += e0 DE += e - ec DS += s - sc DCv += cv - cvc v = None return v, Qc, F, E0, DE, DS, DCv def calcTThermo(self, protocol="PG", subprotocol="CC",\ sb_protocol="HO", sb_subprotocol="HO"): """ Calculate thermodynamics of internal rotations (torsions). Two possibilities: 1. Pitzer-Gwinn - Classical partition function 1. Coupled 2. Uncoupled 3. Hybrid - PG factor 1. Q_HO/Q_CHO(ω) 1. ωΗΟ 2. ωUMVT 3. ωMC 2. Q_UMVT/Q_UMC 2. UM-VT Unit conversions done by operations prior to being returned. """ # Calc of torsions follows according to supplied protocol: if "PG" in protocol: # Calculate class torsional partition function Etclass, Stclass, Cvtclass, Qtclass =\ self.calcTClassical(subprotocol) # Calculate SB Ratio (F) v, qc, F, E0, DE, DS, DCv =\ self.calcPGFactor(sb_protocol, sb_subprotocol) ### Data frames if coupled E0 = E0 E = DE + Etclass S = DS + Stclass Cv = DCv + Cvtclass Q = F * Qtclass Qc = Qtclass ### # SAVE THEM!!! if subprotocol == 'C': if sb_protocol == 'UMVT': print("PG Entropy is", DS) R = 1.985877534e-3 # kcal/mol.K Hcm = E - E0 + self.trans_dict['e'] + self.rot_dict[ 'e'] + R * self.T Hcm += self.ho_dict['e'] - self.ho_dict['e0'] Scm = S + self.trans_dict['s'] + self.rot_dict['s'] +\ self.ho_dict['s'] Cvcm = Cv + self.trans_dict['cv'] + self.rot_dict['cv'] +\ self.ho_dict['cv'] Qpgcm = Q * self.ho_dict['q'] name = os.path.join(self.resdir, '{}_{}K.csv') Hcm.to_csv(name.format('H', self.T)) Scm.to_csv(name.format('S', self.T)) Cvcm.to_csv(name.format('Cv', self.T)) Qpgcm.to_csv(name.format('Qpg', self.T)) #### #For storing in data frame E = np.mean(E.iloc[-1]) S = np.mean(S.iloc[-1]) Cv = np.mean(Cv.iloc[-1]) Q = np.mean(Q.iloc[-1]) Qc = np.mean(Qtclass.iloc[-1]) #### elif protocol == "UMVT": E0, E, S, Cv, Q, F, Qc = 0, 0, 0, 0, 1, None, None for mode in sorted(self.Thermo.mode_dict.keys()): if self.Thermo.mode_dict[mode]['mode'] != 'tors': continue v, e0, e, s, f, q, cv =\ self.Thermo.SolvEig(mode, self.T) E0 += e0 E += e S += s Cv += cv Q *= q elif protocol == 'HO': E0, E, S, Cv, Q, F, Qc = 0, 0, 0, 0, 1, None, None kwargs = { 'samp_obj': self.samp_obj, 'T': self.T, 'Thermo_obj': self.Thermo } ws = get_tors_freqs(protocol=protocol, **kwargs) # s^-1 for i, w in enumerate(ws): # Calculate quantum HO properties e0, e, s, q, cv =\ solvHO(w, self.T) E0 += e0 E += e S += s Cv += cv Q *= q t_dict = { 'mode': 'tors', 'T': self.T, 'protocol': protocol, 'subprotocol': subprotocol, 'sb_protocol': sb_protocol, 'sb_subprotocol': sb_subprotocol, 'e0': E0, 'e': E, 's': S, 'cv': Cv, 'q': Q, 'qc': Qc, 'f': F } self.data_frame = self.data_frame.append(t_dict, ignore_index=True) def calcSBThermo(self, protocol): """ Calculate thermodynamics of stretches and bends, distinct from tors. Relies heavily on outside methods (Yi-Pei Li) Two methods, which match PG protocol: 1. HO - use harmonic approximation 2. UMVT - from anharmonic sampling (done prior to MC) Unit conversions done in situ before returning """ ZPE, E_int, S_int, Q_int, Cv_int = 0, 0, 0, 1, 0 if protocol == "HO": # Calculate HO thermo for stretches/bends freqs = get_sb_freqs(self.Thermo.mode_dict) for w in freqs: e0, e, s, q, cv =\ solvHO(w, self.T) ZPE += e0 E_int += e S_int += s Q_int *= q Cv_int += cv elif protocol == "UMVT": # Calculate UMVT thermo for stretches/bends for mode in sorted(self.Thermo.mode_dict.keys()): if self.Thermo.mode_dict[mode][ 'mode'] == 'tors': # skip torsions continue v, e0, E, S, F, Q, Cv = self.Thermo.SolvEig(mode, self.T) ZPE += e0 E_int += E S_int += S Q_int *= Q Cv_int += Cv sb_dict = { 'mode': 'sb', 'T': self.T, 'sb_protocol': protocol, 'e0': ZPE, 'e': E_int, 's': S_int, 'cv': Cv_int, 'q': Q_int } if protocol == 'UMVT': self.umn_dict = sb_dict elif protocol == 'HO': self.ho_dict = sb_dict self.data_frame = self.data_frame.append(sb_dict, ignore_index=True) def calcTransThermo(self): # Calculate global translation (ideal gas, Sackur-Tetrode) # Unit conversion included E_trans = 1.5 * constants.R * self.T / 4184 S_trans = self.conformer.modes[0].get_entropy( self.T) / 4.184 - constants.R * math.log(self.P / 101325) / 4.184 Cv_trans = 1.5 * constants.R / 4184 * 1000 Q_trans = self.conformer.modes[0].get_partition_function(self.T) self.trans_dict = { 'mode': 'trans', 'T': self.T, 'e': E_trans, 's': S_trans, 'cv': Cv_trans, 'q': Q_trans } self.data_frame = self.data_frame.append(self.trans_dict, ignore_index=True) def calcRotThermo(self): # Calculate global rotation (rigid rotor) # Unit conversion included E_rot = self.conformer.modes[1].get_enthalpy(self.T) / 4184 S_rot = self.conformer.modes[1].get_entropy(self.T) / 4.184 Cv_rot = self.conformer.modes[1].get_heat_capacity(self.T) / 4.184 Q_rot = self.conformer.modes[1].get_partition_function(self.T) self.rot_dict = { 'mode': 'rot', 'T': self.T, 'e': E_rot, 's': S_rot, 'cv': Cv_rot, 'q': Q_rot } self.data_frame = self.data_frame.append(self.rot_dict, ignore_index=True) def calcVibThermo(self, sb_protocol="HO", t_protocol="PG", t_subprotocol="CC", sb_subprotocol="MC"): """ Calculate component thermo quantities for internal modes. Internal modes separted into: Torsions protocols: 'UMVT', 'PG'(coupled, u/c, uncoupled) Stretches / Bends protocols: 'HO'(ω: ho, umvt, mc), 'UMVT' Unit conversions performed by each operation before returning. """ E0_sb, E_sb, S_sb, Cv_sb, Q_sb =\ self.calcSBThermo(protocol=sb_protocol) # for non-torsions E0_t, E_t, S_t, Cv_t, Q_t =\ self.calcTThermo(protocol=t_protocol, subprotocol=t_subprotocol, sb_protocol=sb_protocol, sb_subprotocol=sb_subprotocol) # for torsions return (E0_sb + E0_t), (E_sb + E_t), (S_sb + S_t), (Cv_sb + Cv_t), (Q_sb * Q_t)
def transitionState(label, *args, **kwargs): """Load a transition state from an input file""" global transition_state_dict, job_list, directory if label in transition_state_dict: raise ValueError( 'Multiple occurrences of transition state with label {0!r}.'. format(label)) logging.info('Loading transition state {0}...'.format(label)) ts = TransitionState(label=label) transition_state_dict[label] = ts if len(args) == 1: # The argument is a path to a conformer input file path = os.path.join(directory, args[0]) ts.path = path job = SamplingJob(label=label, input_file=path, output_directory=output_directory, is_ts=True) Log = QChemLog(path) ts.conformer, unscaled_frequencies = Log.load_conformer() ts.frequency = (Log.load_negative_frequency(), "cm^-1") job_list.append(job) elif len(args) == 0: # The species parameters are given explicitly E0 = None modes = [] spin_multiplicity = 1 optical_isomers = 1 frequency = None for key, value in kwargs.items(): if key == 'E0': E0 = value elif key == 'modes': modes = value elif key == 'spinMultiplicity': spin_multiplicity = value elif key == 'opticalIsomers': optical_isomers = value elif key == 'frequency': frequency = value else: raise TypeError( 'transition_state() got an unexpected keyword argument {0!r}.' .format(key)) ts.conformer = Conformer(E0=E0, modes=modes, spin_multiplicity=spin_multiplicity, optical_isomers=optical_isomers) ts.frequency = frequency else: if len(args) == 0 and len(kwargs) == 0: raise InputError( 'The transition_state needs to reference a quantum job file or contain kinetic information.' ) raise InputError( 'The transition_state can only link a quantum job or directly input information, not both.' ) if len(kwargs) > 0: # The species parameters are given explicitly protocol = 'UMVT' E0 = None rotors = None for key, value in kwargs.items(): if key == 'protocol': protocol = value.upper() elif key == 'E0': E0 = value elif key == 'rotors': rotors = value else: raise TypeError( 'species() got an unexpected keyword argument {0!r}.'. format(key)) if protocol == 'UMVT' and rotors is None: raise InputError( 'If the transition state is sampled by using UMVT algorithm, the rotors are needed to be specified.' ) job.protocol = protocol ts.conformer.E0 = E0 job.rotors = rotors return ts
class Statmech(object): """ A class to solve shrodinger equation, evaluate partition function and related properties of 1-D PES by using statistical thermodynamics """ def __init__(self, label, input_file, output_directory, Tlist=[298.15], P=100000, frequency_scale_factor=1, ncpus=None): self.label = label self.input_file = input_file self.output_directory = output_directory self.Tlist = Tlist self.P = P self.frequency_scale_factor = frequency_scale_factor self.ncpus = ncpus self.result_info = list() def load_save(self): self.sampling = SamplingJob(self.label, self.input_file, ncpus=self.ncpus) self.sampling.parse() self.is_QM_MM_INTERFACE = self.sampling.is_QM_MM_INTERFACE self.conformer = self.sampling.conformer self.csv_path = os.path.join( self.output_directory, '{}_samping_result.csv'.format(self.label)) self.mode_dict, self.energy_dict, self.min_elect = from_sampling_result( self.csv_path) self.zpe_of_Hohf = self.sampling.zpe e0 = self.min_elect * constants.E_h * constants.Na + self.sampling.zpe self.conformer.E0 = (e0, "J/mol") for mode in self.conformer.modes: if isinstance(mode, HarmonicOscillator): frequencies = mode.frequencies.value_si mode.frequencies = (frequencies * self.frequency_scale_factor, "cm^-1") self.spin_multiplicity = self.conformer.spin_multiplicity self.optical_isomers = self.conformer.optical_isomers self.symbols = self.sampling.symbols # Solve SE of 1-D PES and calculate E S G Cp self.polynomial_dict = cubic_spline_interpolations( self.energy_dict, self.mode_dict) # Extract whether this system is QM/MM system or not self.is_QM_MM_INTERFACE = self.sampling.is_QM_MM_INTERFACE def calcThermoOfEachMode(self, eig, N, mode, T): beta = 1 / (constants.kB * T) * constants.E_h Q = 0 Q_vib = 0 E = 0 dQ = 0 ddQ = 0 for i in range(N): Ei = eig[i] Q += exp(-beta * Ei) dQ += Ei * exp(-beta * Ei) * beta / T ddQ += -2 * Ei * exp(-beta * Ei) * beta / pow(T, 2) + pow( Ei, 2) * exp(-beta * Ei) * pow(beta, 2) / pow(T, 2) E += Ei * exp(-beta * Ei) if i == 0: zpve = Ei # Measuring energy relative to the zero point vibration frequency dE = Ei - zpve Q_vib += exp(-beta * dE) E /= Q is_tors = True if self.mode_dict[mode]['mode'] == 'tors' else False if is_tors: omega = self.mode_dict[mode]['symmetry_number'] Q /= omega Q_vib /= omega dQ /= omega ddQ /= omega E0 = eig[0] v = (eig[1] - eig[0]) * constants.E_h / constants.h / (constants.c * 100) #print(Q) F = -math.log(Q) / beta S = (E - F) / T Cv = (2 / Q * dQ - T * pow(dQ / Q, 2) + T / Q * ddQ) / beta return v, E0, E, S, F, Q, Q_vib, Cv def SolvEig(self, mode, T): Nbasis = 50 Nbasis_prev = 0 H_prev = None Qold = np.log(sys.float_info[0]) vold = np.log(sys.float_info[0]) converge = False while not converge: Nbasis += 1 H = SetAnharmonicH(self.polynomial_dict, self.mode_dict, self.energy_dict, mode, Nbasis, N_prev=Nbasis_prev, H_prev=H_prev) Nbasis_prev = Nbasis H_prev = deepcopy(H) eig, v = np.linalg.eigh(H) v, E0, E, S, F, Q, Q_vib, Cv = self.calcThermoOfEachMode( eig, Nbasis, mode, T) if Qold == np.log(sys.float_info[0]): self.result_info.append("# \n# \t %d \t\t-\t\t-" % Nbasis) #first run logging.debug("# \t {} \t\t-\t\t-".format(Nbasis)) else: self.result_info.append("# \n# \t %d \t\t %.10f \t\t %.10f" % (Nbasis, abs(Q - Qold), abs(v - vold))) logging.debug("# \t {:d} \t\t {:.10f} \t\t {:.10f}".format( Nbasis, abs(Q - Qold), abs(v - vold))) if ((abs(Q - Qold) < 1e-4) and (abs(v - vold) < 1e-2)): self.result_info.append("# Convergence criterion met") self.result_info.append( "# ------------------------------------") converge = True self.result_info.append("# Frequency (cm-1): %.10f" % v) self.result_info.append( "# Zero point vibrational energy (hartree): %.10f" % E0) self.result_info.append("# Energy (hartree): %.10f" % E) self.result_info.append("# Entropy (hartree/K): %.10f" % S) self.result_info.append("# Free energy (hartree): %.10f" % F) self.result_info.append("# Partition function: %.10f" % Q) hartree2kcalmol = constants.E_h * constants.Na / 4184 E0 *= hartree2kcalmol E *= hartree2kcalmol S *= hartree2kcalmol * 1000 F *= hartree2kcalmol Cv *= hartree2kcalmol * 1000 ''' print("Frequency (cm-1): ",v) print("Zero point vibrational energy (kcal/mol): ",E0) print("Energy (kcal/mol): ",E ) print("Entropy (cal/mol/K): ",S) print("Free energy (kcal/mol): ",F) print("Partition function: ",Q) ''' Qold = Q vold = v return v, E0, E, S, F, Q, Q_vib, Cv
fixed_molecule_string=samp_obj.fixed_molecule_string, opt=samp_obj.opt, number_of_fixed_atoms=samp_obj.number_of_fixed_atoms) args = (path, file_name, samp_obj.ncpus) xyz, internal = get_geometry_at(x, samp_obj) E,grad = get_energy_gradient(xyz,*args,**kwargs) B = internal.B_prim Bt_inv = np.linalg.pinv(B.dot(B.T)).dot(B) grad = Bt_inv.dot(grad)[torsion_inds] grad *= signs subprocess.Popen(['rm {input_path}/{file_name}.q.out'.format(input_path=path, file_name=file_name)], shell=True) return E,grad if __name__ == '__main__': directory = '/Users/lancebettinson/Documents/entropy/um-vt/PROPIONIC_ACID' freq_file = os.path.join(directory,'propanoic.out') label = 'propanoic' from ape.sampling import SamplingJob samp_obj = SamplingJob(label,freq_file,output_directory=directory, protocol='TNUTS') samp_obj.parse() samp_obj.sampling() xyz = get_geometry_at([26*2*np.pi/360,11*2*np.pi/360,45*2*np.pi/360], samp_obj) print(xyz)
xyz = self.transform_geometry_to(phi) coordinates = self.internal.c3d self.conformer.coordinates = (coordinates, "angstroms") I = [] for i in range(self.n_rotors): I.append( self.conformer.get_internal_reduced_moment_of_inertia( self.pivots[i], self.tops[i])*constants.Na * 1e23) # amu*Å^2 return np.array(I) if __name__ == '__main__': directory = '/Users/lancebettinson/Documents/entropy/um-vt/MeOOH' freq_file = os.path.join(directory,'MeOOH.out') label = 'MeOOH' from ape.sampling import SamplingJob samp_obj = SamplingJob(label,freq_file,output_directory=directory, protocol='TNUTS') samp_obj.parse() samp_obj.csv_path = os.path.join(directory, 'MeOOH_sampling_result.csv') xyz_dict, energy_dict, mode_dict = samp_obj.sampling() tmodes = dicts_to_NModes(mode_dict, energy_dict, xyz_dict, samp_obj=samp_obj, just_tors=True) syms = np.array([mode.get_symmetry_number() for mode in tmodes]) geom = Geometry(samp_obj, samp_obj.torsion_internal, syms) x = np.random.random((10,2)) for xi in x: print("coordinate transformation at",xi) I = geom.calc_I(xi) print(I)