Beispiel #1
0
 def rmg_Reaction(self):
     if self.transition_state is None:
         specs = self.reactants + self.products
     else:
         specs = self.reactants + self.products + [self.transition_state]
     for spec in specs:
         thermo = ThermoJob(
             spec.label,
             spec.path,
             output_directory=self.output_directory,
             frequency_scale_factor=self.frequency_scale_factor)
         thermo.ncpus = self.ncpus
         thermo.load_save()
         spec.conformer.E0 = thermo.conformer.E0
         for mode in spec.conformer.modes:
             if isinstance(mode, HarmonicOscillator):
                 frequencies = mode.frequencies.value_si
                 mode.frequencies = (frequencies *
                                     self.frequency_scale_factor, "cm^-1")
         # only leave HarmonicOscillator in QM/MM calculation
         if thermo.is_QM_MM_INTERFACE:
             for mode in spec.conformer.modes:
                 if isinstance(mode, HarmonicOscillator):
                     spec.conformer.modes = [mode]
     rxn = rmg_Reaction(label=self.label,
                        reactants=self.reactants,
                        products=self.products,
                        transition_state=self.transition_state)
     return rxn
Beispiel #2
0
 def __init__(self, trace, T, samp_obj=None, P=101325):
     self.T = T
     self.P = P #Pa
     self.trace = trace
     self.samp_obj = samp_obj
     self.conformer = self.samp_obj.conformer
     self.Thermo = ThermoJob(self.samp_obj.label,
             self.samp_obj.input_file,
             output_directory=self.samp_obj.output_directory,
             P=self.P)
     self.Thermo.load_save()
     self.tmodes = self.samp_obj.tmodes
     self.NModes = self.samp_obj.NModes
Beispiel #3
0
 def calcThermo(self, T, P=101325):
     self.thermo_dict[T] = {}
     if self.transition_state is None:
         specs = set(self.reactants + self.products)
     else:
         specs = set(self.reactants + self.products +
                     [self.transition_state])
     for spec in specs:
         logging.debug(
             '    Calculating thermodynamics properties for {0} at {1} K'.
             format(spec.label, T))
         self.thermo_dict[T][spec.label] = {}
         thermo = ThermoJob(
             spec.label,
             spec.path,
             output_directory=self.output_directory,
             P=P,
             frequency_scale_factor=self.frequency_scale_factor)
         thermo.ncpus = self.ncpus
         thermo.load_save()
         # only leave HarmonicOscillator in QM/MM calculation
         if thermo.is_QM_MM_INTERFACE:
             E0, E, S, F, Q, Cv = thermo.calcQMMMThermo(
                 T, print_HOhf_result=False)
         else:
             E0, E, S, F, Q, Cv = thermo.calcThermo(T,
                                                    print_HOhf_result=False)
         self.thermo_dict[T][spec.label]['E0'] = E0
         self.thermo_dict[T][spec.label]['E'] = E
         self.thermo_dict[T][spec.label]['S'] = S
         self.thermo_dict[T][spec.label]['F'] = F
         self.thermo_dict[T][spec.label]['Q'] = Q
         self.thermo_dict[T][spec.label]['Cv'] = Cv
Beispiel #4
0
def thermo(label, Tlist=[298.15]):
    """Generate a thermo job"""
    global job_list, species_dict, transition_state_dict
    if label in species_dict.keys():
        spec = species_dict[label]
    elif label in transition_state_dict.keys():
        spec = transition_state_dict[label]
    else:
        raise ValueError(
            'Unknown species label {0!r} for thermo() job.'.format(label))
    for job in job_list:
        if job.label == label:
            input_file = job.input_file
    job = ThermoJob(label=label,
                    input_file=input_file,
                    output_directory=output_directory,
                    Tlist=Tlist)
    job_list.append(job)
Beispiel #5
0
 def execute(self):
     """
     Execute APE in parallel.
     """
     if os.path.exists(self.csv_path):
         os.remove(self.csv_path)
     self.parse()
     self.run()
     mode_dict, energy_dict, _ = from_sampling_result(
         csv_path=self.csv_path)
     # Solve SE of 1-D PES and calculate E S G Cp
     polynomial_dict = cubic_spline_interpolations(energy_dict, mode_dict)
     thermo = ThermoJob(self.conformer,
                        polynomial_dict,
                        mode_dict,
                        energy_dict,
                        T=298.15,
                        P=100000)
     if self.is_QM_MM_INTERFACE:
         thermo.calcQMMMThermo()
     else:
         thermo.calcThermo(print_HOhf_result=True, zpe_of_Hohf=self.zpe)
Beispiel #6
0
    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': []})
Beispiel #7
0
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)
Beispiel #8
0
class MCThermoJob:
    """
    The class to calculate thermodynamic properties, including E S G Cp
    """
    def __init__(self, trace, T, samp_obj=None, P=101325):
        self.T = T
        self.P = P #Pa
        self.trace = trace
        self.samp_obj = samp_obj
        self.conformer = self.samp_obj.conformer
        self.Thermo = ThermoJob(self.samp_obj.label,
                self.samp_obj.input_file,
                output_directory=self.samp_obj.output_directory,
                P=self.P)
        self.Thermo.load_save()
        self.tmodes = self.samp_obj.tmodes
        self.NModes = self.samp_obj.NModes

    def calcThermo(self, print_HOhf_result=True):
        self.conformer = None
        E_trans, S_trans, Cv_trans, Q_trans =\
                self.calcTransThermo()
        E_rot, S_rot, Cv_rot, Q_rot =\
                self.calcRotThermo()
        E0, E_vib, S_vib, Cv_vib, Q_vib =\
                self.calcVibThermo(protocol="PG")

    def calcVibThermo(self, sb_protocol="HO", t_protocol="PG", t_subprotocol="CC",
            sb_subprotocol="MC"):
        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 calcTClassical(self, subprotocol):
        if not subprotocol in ["CC", "UC", "UU"]:
            raise TypeError(
                "Invalid subprotocol: valid options are 'CC', 'UC', or 'UU'.")
        ntors = self.samp_obj.n_rotors
        beta = 1/(constants.kB*T)*constants.E_h # beta in Hartree
        if "C" in subprotocol:
            # Calculate coupled torsional PES (V) contribution (all torsions)
            QV = self.Z*np.mean(self.trace.a)
            EV = np.mean(self.trace.bE)/beta * Hartree2kcal # E in kcal/mol
            SV = constants.kB(np.log(QV) + np.mean(self.trace.bE))*\
                    J2cal  # S in cal/mol.K
            CvV = beta/self.T*\
                    (np.var(self.trace.bE/beta))*Hartree2kcal # Cv in kcal/mol.K
            if "CC" in subprotocol:
                # Calculate coupled torsional kinetic (T) contribution
                # NOTE NOTE NOTE NOTE
                D = self.trace.get_sampler_stats("mass_matrix") # NEED TO CONVERT TO SI
        if "U" in subprotocol:
            # Calculate uncoupled torsional kinetic (T) contribution
            #NOTE NOTE NOTE NOTE
            D = np.diag([])
            if "UU" in subprotocol:
                # Calculate uncoupled torsional PES (V) contribution
                QV, EV, SV, CvV = 1, 0, 0, 0
                pass
        beta_si = 1./(constants.kB*T)
        prefactor = 1./(2*np.pi*beta_si*np.power(constants.hbar,2.))
        QT = np.power(prefactor*np.linalg.det(D), ntors/2)
        ET = ntors/(2*beta_si) * J2cal/1000     # ET in kcal/mol
        ST = constants.kB*(np.log(QT) + ntors/2.) * J2cal   #S in cal/mol.K
        CvT = constants.kB*ntors/2. * J2cal/1000    # Cv in kcal/mol.K

        Qtclass = QV * QT
        Etclass = EV + ET
        Stclass = SV + ST
        Cvtclass = CvV + CvT
        return Etclass, Stclass, Cvtclass, Qtclass

    def calcPGFactor(self, sb_protocol, sb_subprotocol):
        if not sb_protocol in ["HO", "UMVT"]:
            raise TypeError(
                    "Invalid protocol for stretches/bends: valid options\
                            are 'HO' or 'UMVT'.")
        Qc, F, E0divQtclass, 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, 'D' : D, 'Thermo_obj' : self.Thermo}
            ws = get_tors_freq(protocol = sb_subprotocol, **kwargs) # Get freqs
            for i,w in enumerate(ws):
                # Calculate quantum HO properties
                e0, e, s, q, cv =\
                        solvHO(w, self.T)
                ec, sc, cvc, qc =\
                        solvCHO(w, self.T)
                F *= q/qc
                Qc *= qc
                E0divQtclass += e0/qc 
                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, cvc, qc =\
                        solvUMClass(NMode, self.T)
                F *= q/qc
                Qc *= qc
                E0divQtclass += 1./qc*e0
                DE += e - ec 
                DS += s - sc
                DCv += cv - cvc
        return v, Qc, F, E0divQtclass, DE, DS, DCv

    def calcTThermo(self, protocol="PG", subprotocol="CC",\
            sb_protocol="HO", sb_subprotocol="HO"):
        ntors = self.samp_obj.n_rotors
        # NOTE
        # 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, E0divQtclass, DE, DS, DCv =\
                    self.calcPGFactor(sb_protocol, sb_subprotocol)
            E0 = E0divQtclass*Qtclass
            E = DE + Etclass
            S = DS + Stclass
            Cv = DCv + Cvtclass
            Q = F*Qtclass
        elif protocol == "UMVT":
            E0, E, S, Cv, Q = 0, 0, 0, 0, 1
            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
        return E0, E, S, Cv, Q

    def calcSBThermo(self, protocol):
        ZPE, E_int, S_int, Q_int, Cv_int = 0, 0, 0, 1, 0
        if protocol="HO":
            # Calculate HO thermo for stretches/bends
            pass
        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
                self.result_info.append("\n# \t********** Mode {} **********".format(mode))
                v, e0, E, S, F, Q, Cv = self.SolvEig(mode, T)
                ZPE += e0
                E_int += E
                S_int += S
                Q_int *= Q
                Cv_int += Cv