コード例 #1
0
    def test_flag(self):
        geom = Geometry(TestGeometry.benz_NO2_Cl)

        # freeze all
        test = geom.copy()
        test.freeze()
        for a in test.atoms:
            self.assertTrue(a.flag)

        # freeze some
        test = geom.copy()
        test.freeze("C")
        for a in test.atoms:
            if a.element == "C":
                self.assertTrue(a.flag)
            else:
                self.assertFalse(a.flag)

        geom.freeze()
        # relax all
        test = geom.copy()
        test.relax()
        for a in test.atoms:
            self.assertFalse(a.flag)

        # relax some
        test = geom.copy()
        test.relax("C")
        for a in test.atoms:
            if a.element == "C":
                self.assertFalse(a.flag)
            else:
                self.assertTrue(a.flag)
コード例 #2
0
    def test_RMSD(self):
        ref = Geometry(TestGeometry.benz_NO2_Cl)

        # RMSD of copied object should be 0
        other = ref.copy()
        self.assertTrue(ref.RMSD(other) < rmsd_tol(ref, superTight=True))
        # if they are out of order, sorting should help
        res = ref.RMSD(other, longsort=True)
        self.assertTrue(res < rmsd_tol(other, superTight=True))

        # RMSD of shifted copy should be 0
        other = ref.copy()
        other.coord_shift([1, 2, 3])
        self.assertTrue(ref.RMSD(other) < rmsd_tol(ref, superTight=True))

        # RMSD of rotated copy should be 0
        other = ref.copy()
        other.rotate([1, 2, 3], 2.8)
        other.write("tmp")
        self.assertTrue(ref.RMSD(other) < rmsd_tol(ref))

        # RMSD of two different structures should not be 0
        other = Geometry(TestGeometry.pent)
        self.assertTrue(ref.RMSD(other) > 10**-2)

        # RMSD of similar molecule
        other = Geometry(TestGeometry.benzene)
        res = ref.RMSD(other, targets="C", ref_targets="C")
        self.assertTrue(res < rmsd_tol(ref))
        res = ref.RMSD(other, sort=True)
コード例 #3
0
ファイル: test_geometry.py プロジェクト: QChASM/AaronTools.py
    def test_RMSD(self):
        ref = Geometry(TestGeometry.benz_NO2_Cl)

        # RMSD of copied object should be 0
        test = ref.copy()
        self.assertTrue(validate(test, ref))

        # RMSD of shifted copy should be 0
        test = ref.copy()
        test.coord_shift([1, 2, 3])
        self.assertTrue(validate(test, ref))

        # RMSD of rotated copy should be 0
        test = ref.copy()
        test.rotate([1, 2, 3], 2.8)
        self.assertTrue(validate(test, ref))

        # RMSD of two different structures should not be 0
        test = Geometry(TestGeometry.pentane)
        self.assertFalse(validate(test, ref))

        # RMSD of similar molecule
        test = Geometry(TestGeometry.benzene)
        res = ref.RMSD(test, targets="C", ref_targets="C")
        self.assertTrue(res < rmsd_tol(ref))
コード例 #4
0
    def test_close_ring_approx(self):
        mol = Geometry(TestGeometry.benzene)

        ref1 = Geometry(TestGeometry.naphthalene)
        mol1 = mol.copy()
        mol1.ring_substitute(['7', '8'], RingFragment('benzene'))
        rmsd = mol1.RMSD(ref1, align=True)
        self.assertTrue(rmsd < rmsd_tol(ref1))

        ref2 = Geometry(TestGeometry.tetrahydronaphthalene)
        mol2 = mol.copy()
        mol2.ring_substitute(['7', '8'], RingFragment('cyclohexane-chair.1'))
        rmsd = mol2.RMSD(ref2, align=True)
        self.assertTrue(rmsd < rmsd_tol(ref2))
コード例 #5
0
 def test_equality(self):
     mol = Geometry(TestGeometry.benz_NO2_Cl)
     benz = Geometry(TestGeometry.benzene)
     # same object should be equal
     self.assertEqual(mol, mol)
     # copy should be equal
     self.assertEqual(mol, mol.copy())
     # different molecules should be unequal
     self.assertNotEqual(mol, benz)
コード例 #6
0
ファイル: test_geometry.py プロジェクト: QChASM/AaronTools.py
    def test_close_ring(self):
        mol = Geometry(TestGeometry.benzene)

        ref1 = Geometry(TestGeometry.naphthalene)
        mol1 = mol.copy()
        mol1.ring_substitute(["7", "8"], Ring("benzene"))
        self.assertTrue(validate(mol1, ref1, thresh="loose"))

        ref2 = Geometry(TestGeometry.tetrahydronaphthalene)
        mol2 = mol.copy()
        mol2.ring_substitute(["7", "8"], Ring("cyclohexane"))
        rmsd = mol2.RMSD(ref2, align=True, sort=True)
        self.assertTrue(rmsd < rmsd_tol(ref2, superLoose=True))

        mol3 = Geometry(TestGeometry.naphthalene)
        ref3 = Geometry(TestGeometry.pyrene)
        targets1 = mol3.find(["9", "15"])
        targets2 = mol3.find(["10", "16"])
        mol3.ring_substitute(targets1, Ring("benzene"))
        mol3.ring_substitute(targets2, Ring("benzene"))
        rmsd = mol3.RMSD(ref3, align=True, sort=True)
        self.assertTrue(rmsd < rmsd_tol(ref3, superLoose=True))
コード例 #7
0
    def test_update_geometry(self):
        test = Geometry(TestGeometry.benz_NO2_Cl)

        # using coordinate matrix to update
        ref = test.copy()
        ref.coord_shift([-10, 0, 0])
        tmp = test._stack_coords()
        for t in tmp:
            tmp = tmp - np.array([-10, 0, 0])
        test.update_geometry(tmp)
        self.assertEqual(ref, test)

        # using file
        ref.coord_shift([10, 0, 0])
        test.update_geometry(TestGeometry.benz_NO2_Cl)
        self.assertEqual(ref, test)
コード例 #8
0
ファイル: comp_output.py プロジェクト: Feteya/AaronTools.py
class CompOutput:
    """
    Attributes:
        geometry    the last Geometry
        opts        list of Geometry for each optimization steps
        frequency   Frequency object
        archive     a string containing the archive entry
        energy, enthalpy, free_energy, grimme_g,
        mass, temperature, rotational_temperature,
        multiplicity, charge, rotational_symmetry_number
        error, error_msg, finished,
        gradient, E_ZPVE, ZPVE
    """

    def __init__(self, fname='', get_all=True):
        self.geometry = None
        self.opts = None
        self.frequency = None
        self.archive = None

        self.gradient, self.E_ZPVE, self.ZPVE = (None, None, None)
        self.energy, self.enthalpy = (None, None)
        self.free_energy, self.grimme_g = (None, None)

        self.mass, self.temperature = (None, None)
        self.multiplicity, self.charge = (None, None)

        self.rotational_temperature = None
        self.rotational_symmetry_number = None

        self.error, self.error_msg, self.finished = (None, None, None)

        keys = ['energy', 'error', 'error_msg', 'gradient', 'finished',
                'frequency', 'mass', 'temperature', 'rotational_temperature',
                'free_energy', 'multiplicity', 'charge', 'E_ZPVE', 'ZPVE',
                'rotational_symmetry_number', 'enthalpy', 'archive']

        if isinstance(fname, str) and '.log' in fname:
            from_file = FileReader(fname, get_all, just_geom=False)
        elif isinstance(fname, tuple) and 'log' == fname[1]:
            from_file = FileReader(fname, get_all, just_geom=False)
        else:
            return

        self.geometry = Geometry(from_file)
        if 'all_geom' in from_file.other:
            self.opts = []
            for g in from_file.other['all_geom']:
                self.opts += [Geometry(g)]

        for k in keys:
            if k in from_file.other:
                self.__setattr__(k, from_file.other[k])

        if self.frequency:
            self.grimme_g = self.calc_Grimme_G()

    def get_progress(self):
        rv = ""
        grad = self.gradient
        if grad is None:
            rv += "Progress not found"
            return rv

        for name in grad:
            rv += "{:>9}:{}/{:<3} ".format(
                name, grad[name]['value'],
                'YES' if grad[name]['converged'] else 'NO'
            )

        return rv[:-2]

    def calc_Grimme_G(self, temperature=None):
        v0 = 100
        if self.frequency is None:
            msg = "Vibrational frequencies not found, "
            msg += "cannot calculate Grimme free energy."
            raise AttributeError(msg)

        rot = self.rotational_temperature
        T = temperature if temperature is not None else self.temperature
        mass = self.mass
        sigmar = self.rotational_symmetry_number
        mult = self.multiplicity
        freqs = self.frequency.real_frequencies

        vibtemps = PHYSICAL.SPEED_OF_LIGHT * PHYSICAL.PLANK / PHYSICAL.KB
        vibtemps = [f_i * vibtemps for f_i in freqs]

        Bav = PHYSICAL.PLANK**2 / (24 * np.pi**2 * PHYSICAL.KB)
        Bav *= sum([1 / r for r in rot])

        # Translational
        qt = 2 * np.pi * mass * PHYSICAL.KB * T / (PHYSICAL.PLANK**2)
        qt = qt**(3 / 2)
        qt *= PHYSICAL.KB * T / PHYSICAL.STANDARD_PRESSURE
        St = PHYSICAL.GAS_CONSTANT * (np.log(qt) + (5 / 2))
        Et = 3 * PHYSICAL.GAS_CONSTANT * T / 2

        # Electronic
        Se = PHYSICAL.GAS_CONSTANT * (np.log(mult))

        # Rotational
        qr = (np.sqrt(np.pi) / sigmar)
        qr *= (T**(3 / 2) / np.sqrt(rot[0] * rot[1] * rot[2]))
        Sr = PHYSICAL.GAS_CONSTANT * (np.log(qr) + 3 / 2)
        Er = 3 * PHYSICAL.GAS_CONSTANT * T / 2

        # Vibrational
        Ev = 0
        Sv_qRRHO = 0
        for i, vib in enumerate(vibtemps):
            Sv_T = vib / (T * (np.exp(vib / T) - 1))
            Sv_T -= np.log(1 - np.exp(-vib / T))
            Ev += vib * (1 / 2 + 1 / (np.exp(vib / T) - 1))

            mu = PHYSICAL.PLANK
            mu /= (8 * np.pi**2 * freqs[i] * PHYSICAL.SPEED_OF_LIGHT)
            mu = mu * Bav / (mu + Bav)
            Sr_eff = 1 / 2 + np.log(np.sqrt(
                8 * np.pi**3 * mu * PHYSICAL.KB * T / PHYSICAL.PLANK**2))
            weight = 1 / (1 + (v0 / freqs[i])**4)

            Sv_qRRHO += weight * Sv_T + (1 - weight) * Sr_eff
        Ev *= PHYSICAL.GAS_CONSTANT
        Sv_qRRHO *= PHYSICAL.GAS_CONSTANT

        Hcorr = Et + Er + Ev + PHYSICAL.GAS_CONSTANT * T
        Stot_qRRHO = St + Sr + Sv_qRRHO + Se
        Gcorr_qRRHO = (Hcorr - T * Stot_qRRHO) / (UNIT.HART_TO_KCAL * 1000)

        return self.energy + Gcorr_qRRHO

    def bond_change(self, atom1, atom2, threshold=0.25):
        """

        """
        ref = self.opts[0]
        d_ref = ref.atoms[atom1].dist(ref.atoms[atom2])

        n = len(self.opts) - 1
        for i, step in enumerate(self.opts[::-1]):
            d = step.atoms[atom1].dist(step.atoms[atom2])
            if abs(d_ref - d) < threshold:
                n = len(self.opts) - 1 - i
                break
        return n

    def parse_archive(self):
        """
        Reads info from archive string

        Returns: a dictionary with the parsed information
        """
        def grab_coords(line):
            rv = {}
            for i, word in enumerate(line.split('\\')):
                word = word.split(',')
                if i == 0:
                    rv['charge'] = int(word[0])
                    rv['multiplicity'] = int(word[1])
                    rv['atoms'] = []
                    continue
                rv['atoms'] += [Atom(element=word[0],
                                     coords=word[1:4], name=str(i))]
            return rv

        rv = {}
        lines = iter(self.archive.split('\\\\'))
        for line in lines:
            line = line.strip()
            if not line:
                continue

            if line.startswith('@'):
                line = line[1:]
                for word in line.split('\\'):
                    if 'summary' not in rv:
                        rv['summary'] = [word]
                    elif word not in rv['summary']:
                        rv['summary'] += [word]
                continue

            if line.startswith('#'):
                if 'route' not in rv:
                    rv['route'] = line
                elif isinstance(rv['route'], list):
                    # for compound jobs, like opt freq
                    rv['route'] += [line]
                else:
                    # for compound jobs, like opt freq
                    rv['route'] = [rv['route']] + [line]

                line = next(lines).strip()
                if 'comment' not in line:
                    rv['comment'] = line

                line = next(lines).strip()
                for key, val in grab_coords(line).items():
                    rv[key] = val
                continue

            words = iter(line.split('\\'))
            for word in words:
                if not word:
                    # get rid of pesky empty elements
                    continue
                if '=' in word:
                    key, val = word.split('=')
                    rv[key.lower()] = float_vec(val)
                else:
                    if 'hessian' not in rv:
                        rv['hessian'] = uptri2sym(
                            float_vec(word), 3*len(rv['atoms']),
                            col_based=True)
                    else:
                        rv['gradient'] = float_vec(word)
        return rv

    def follow(self, reverse=False, step=0.1):
        """
        Follow imaginary mode
        """
        # get geometry and frequency objects
        geom = self.geometry.copy()
        freq = self.frequency

        # make sure geom is a TS and has computed frequencies available
        if freq is None:
            raise AttributeError("Frequencies for this geometry not found.")
        if not freq.is_TS:
            raise RuntimeError("Geometry not a transition state")

        # get displacement vectors for imaginary frequency
        img_mode = freq.imaginary_frequencies[0]
        vector = freq.by_frequency[img_mode]['vector']

        # apply transformation to geometry and return it
        for i, a in enumerate(geom.atoms):
            if reverse:
                a.coords -= vector[i] * step
            else:
                a.coords += vector[i] * step
        return geom
コード例 #9
0
ファイル: follow.py プロジェクト: Feteya/AaronTools.py
            x_factor = scale[i][j] / max_norm

        if args.reverse:
            x_factor *= -1

        dX += x_factor * G_AAron_file.other['frequency'].data[combo].vector

    if args.animate is not None:
        #animate by setting up 3 geometries: -, 0, and +
        #then create a Pathway to interpolate between these
        #if roundtrip, - -> 0 -> + -> 0 -> -
        #if not roundtrip, - -> 0 -> +
        w = width(args.animate[0])
        fmt = "%0" + "%i" % w + "i"

        Gf = G.copy()
        Gf.update_geometry(G.coords() + dX)
        Gr = G.copy()
        Gr.update_geometry(G.coords() - dX)

        #make a scales(t) function so we can see the animation progress in the XYZ file comment
        if args.roundtrip:
            S = Pathway([Gf, G, Gr, G, Gf])
            ev = [
                Pathway.get_splines_vector([-x, 0, x, 0, -x]) for x in scale[i]
            ]
            m = Pathway.get_splines_mat(5)

        else:
            S = Pathway([Gf, G, Gr])
            ev = [Pathway.get_splines_vector([-x, 0, x]) for x in scale[i]]
コード例 #10
0
                    ", ".join(
                        str(pathway.var_func[key](t)) for key in other_vars),
                ))
            if args.outfile:
                followed_geom.write(append=False,
                                    outfile=outfile.replace(
                                        "$INFILE",
                                        get_filename(args.input_file)))
            else:
                print(followed_geom.write(outfile=False))

    else:
        w = width(len(modes))
        fmt = "%0" + "%i" % w + "i"

        followed_geom = geom.copy()
        followed_geom.update_geometry(geom.coords + dX)
        followed_geom.comment = "following mode %s scaled to displace at most %s" % (
            repr(mode),
            repr(scale[i]),
        )

        outfile = outfiles[i]
        if args.outfile:
            if "$INFILE" in outfile:
                outfile = outfile.replace("$INFILE",
                                          get_filename(args.input_file))
            followed_geom.write(append=True, outfile=outfile)
        else:
            print(followed_geom.write(outfile=False))
コード例 #11
0
        lig_names = [
            l for l in "=".join(args.ligand.split("=")[1:]).split(",")
        ]

    else:
        lig_names = [
            l for l in "=".join(args.ligand.split("=")[1:]).split(",")
        ]
        key_atoms = []

    for lig_names in [
            get_matching_ligands(lig_name) for lig_name in lig_names
    ]:
        for lig_name in lig_names:
            ligands = [Component(lig_name)]
            cat_copy = cat.copy()

            if key_atoms != []:
                key = cat_copy.find(key_atoms)
            else:
                key = []

            original_ligands = [l for l in ligands]

            lig_keys = sum([len(ligand.key_atoms) for ligand in ligands])
            while len(key) > lig_keys:
                ligands.extend(l.copy() for l in original_ligands)
                lig_keys += sum(
                    [len(ligand.key_atoms) for ligand in original_ligands])

            j = 0
コード例 #12
0
ファイル: interpolate.py プロジェクト: Feteya/AaronTools.py
        dEdt = S.dE_func(t)*S.dE_func(t+dt)
        if dEdt <= 0 and S.dE_func(t) > 0 and args.print_max:
            max_n_min_ts.append(t)
        if dEdt <= 0 and S.dE_func(t) < 0 and args.print_min:
            max_n_min_ts.append(t)

    for i, t in enumerate(max_n_min_ts):
        E = S.E_func(t)
        if args.print_E:
            dE = S.dE_func(t)
            nrg_out += "%f\t%f\t%f\n" % (t,E,dE)
        else:
            G = S.Geom_func(t)
            comment = "E(%f) = %f" % (t, E)
            G.comment = comment
            write_geoms.append(G.copy())

if args.specific_ts:
    #print structures for specified values of t
    for i, t in enumerate(args.specific_ts):
        if args.print_E:
            E = S.E_func(t)
            dE = S.dE_func(t)
            nrg_out += "%f\t%f\t%f\n" % (t,E,dE)
        else:
            G = S.Geom_func(t)
            E = S.E_func(t)
            comment = "E(%f) = %f" % (t, E)
            G.comment = comment
            write_geoms.append(G.copy())
コード例 #13
0
class CompOutput:
    """
    Attributes:
        geometry    the last Geometry
        opts        list of Geometry for each optimization steps
        frequency   Frequency object
        archive     a string containing the archive entry
        energy, enthalpy, free_energy, grimme_g,
        mass, temperature, rotational_temperature,
        multiplicity, charge, rotational_symmetry_number
        error, error_msg, finished,
        gradient, E_ZPVE, ZPVE
    """

    QUASI_HARMONIC = "QHARM"
    QUASI_RRHO = "QRRHO"
    RRHO = "RRHO"
    LOG = None
    LOGLEVEL = "debug"

    def __init__(
        self,
        fname="",
        get_all=True,
        freq_name=None,
        conf_name=None,
    ):
        self.geometry = None
        self.opts = None
        self.opt_steps = None
        self.frequency = None
        self.archive = None
        self.other = None
        self.conformers = None

        self.gradient, self.E_ZPVE, self.ZPVE = ({}, None, None)
        self.energy, self.enthalpy = (None, None)
        self.free_energy, self.grimme_g = (None, None)

        self.mass, self.temperature = (None, None)
        self.multiplicity, self.charge = (None, None)

        self.rotational_temperature = None
        self.rotational_symmetry_number = None

        self.error, self.error_msg, self.finished = (None, None, None)

        # these will be pulled out of FileReader.other dict
        keys = [
            "opt_steps",
            "energy",
            "error",
            "error_msg",
            "gradient",
            "finished",
            "frequency",
            "mass",
            "temperature",
            "rotational_temperature",
            "free_energy",
            "multiplicity",
            "charge",
            "E_ZPVE",
            "ZPVE",
            "rotational_symmetry_number",
            "enthalpy",
            "archive",
        ]

        if isinstance(fname, (str, tuple)) and len(fname) > 0:
            from_file = FileReader(
                fname,
                get_all,
                just_geom=False,
                freq_name=freq_name,
                conf_name=conf_name,
            )
        elif isinstance(fname, FileReader):
            from_file = fname
        else:
            return

        if from_file.atoms:
            self.geometry = Geometry(
                from_file.atoms, comment=from_file.comment
            )
        if from_file.all_geom:
            self.opts = []
            for g in from_file.all_geom:
                self.opts += [Geometry(g[0])]
        if "conformers" in from_file.other:
            self.conformers = []
            for comment, atoms in from_file.other["conformers"]:
                self.conformers.append(Geometry(atoms, comment=comment))
            del from_file.other["conformers"]

        for k in keys:
            if k in from_file.other:
                setattr(self, k, from_file.other[k])
        
        self.other = {k:v for k, v in from_file.other.items() if k not in keys}

        if self.rotational_temperature is None and self.geometry:
            self.compute_rot_temps()

        if self.frequency:
            self.grimme_g = self.calc_Grimme_G()
            # recalculate ZPVE b/c our constants and the ones in various programs
            # might be slightly different
            self.ZPVE = self.calc_zpe()
            self.E_ZPVE = self.energy + self.ZPVE

    def to_dict(self, skip_attrs=None):
        return obj_to_dict(self, skip_attrs=skip_attrs)

    def get_progress(self):
        rv = ""
        grad = self.gradient
        if not grad:
            rv += "Progress not found"
            return rv

        for name in grad:
            rv += "{:>9}:{}/{:<3} ".format(
                name,
                grad[name]["value"],
                "YES" if grad[name]["converged"] else "NO",
            )

        return rv.rstrip()

    def calc_zpe(self, anharmonic=False):
        """returns ZPVE correction"""
        hc = PHYSICAL.PLANCK * PHYSICAL.SPEED_OF_LIGHT / UNIT.HART_TO_JOULE
        if anharmonic:
            vib = sum(self.frequency.real_frequencies)
            x = np.tril(self.other["X_matrix"]).sum()
            x0 = self.other["X0"]
            zpve = hc * (0.5 * vib + 0.25 * x + x0)
        else:
            vib = sum(self.frequency.real_frequencies)
            zpve = 0.5 * hc * vib
        return zpve

    def therm_corr(self, temperature=None, v0=100, method="RRHO"):
        """
        returns thermal correction to energy, enthalpy correction to energy, and entropy
        for the specified cutoff frequency and temperature
        in that order (Hartrees for corrections, Eh/K for entropy)

        temperature: float, temperature in K- None will use self.temperature
        v0: float, cutoff/damping parameter for quasi G corrections
        method: str - type of quasi treatment:
                RRHO  - no quasi treatment
                QRRHO - Grimme's quasi-RRHO
                see Grimme, S. (2012), Supramolecular Binding Thermodynamics by
                Dispersion‐Corrected Density Functional Theory. Chem. Eur. J.,
                18: 9955-9964. (DOI: 10.1002/chem.201200497) for details
                QHARM - Truhlar's quasi-harmonic
                see J. Phys. Chem. B 2011, 115, 49, 14556–14562
                (DOI: 10.1021/jp205508z) for details
        """
        if self.frequency is None:
            msg = "Vibrational frequencies not found, "
            msg += "cannot calculate vibrational entropy."
            raise AttributeError(msg)

        rot = [temp for temp in self.rotational_temperature if temp != 0]
        T = temperature if temperature is not None else self.temperature
        if T == 0:
            return 0, 0, 0

        mass = self.mass
        sigmar = self.rotational_symmetry_number
        if sigmar is None and len(self.geometry.atoms) == 1:
            sigmar = 3
        mult = self.multiplicity
        freqs = np.array(self.frequency.real_frequencies)

        vib_unit_convert = (
            PHYSICAL.SPEED_OF_LIGHT * PHYSICAL.PLANCK / PHYSICAL.KB
        )
        vibtemps = np.array(
            [f_i * vib_unit_convert for f_i in freqs if f_i > 0]
        )
        if method == "QHARM":
            harm_vibtemps = np.array(
                [
                    f_i * vib_unit_convert
                    if f_i > v0
                    else v0 * vib_unit_convert
                    for f_i in freqs
                    if f_i > 0
                ]
            )
        else:
            harm_vibtemps = vibtemps

        Bav = PHYSICAL.PLANCK ** 2 / (24 * np.pi ** 2 * PHYSICAL.KB)
        Bav *= sum([1 / r for r in rot])

        # Translational
        qt = 2 * np.pi * mass * PHYSICAL.KB * T / (PHYSICAL.PLANCK ** 2)
        qt = qt ** (3 / 2)
        qt *= PHYSICAL.KB * T / PHYSICAL.STANDARD_PRESSURE
        St = PHYSICAL.GAS_CONSTANT * (np.log(qt) + (5 / 2))
        Et = 3 * PHYSICAL.GAS_CONSTANT * T / 2

        # Electronic
        Se = PHYSICAL.GAS_CONSTANT * (np.log(mult))

        # Rotational
        if all(r == np.inf for r in rot):
            # atomic
            qr = 1
            Sr = 0
        elif len(rot) == 3:
            # non linear molecules
            qr = np.sqrt(np.pi) / sigmar
            qr *= T ** (3 / 2) / np.sqrt(rot[0] * rot[1] * rot[2])
            Sr = PHYSICAL.GAS_CONSTANT * (np.log(qr) + 3 / 2)
        elif len(rot) == 2:
            # linear molecules
            qr = (1 / sigmar) * (T / np.sqrt(rot[0] * rot[1]))
            Sr = PHYSICAL.GAS_CONSTANT * (np.log(qr) + 1)
        else:
            # atoms
            qr = 1
            Sr = 0

        if all(r == np.inf for r in rot):
            Er = 0
        else:
            Er = len(rot) * PHYSICAL.GAS_CONSTANT * T / 2

        # Vibrational
        if method == self.QUASI_HARMONIC:
            Sv = np.sum(
                harm_vibtemps / (T * (np.exp(harm_vibtemps / T) - 1))
                - np.log(1 - np.exp(-harm_vibtemps / T))
            )
        elif method == self.RRHO:
            Sv = np.sum(
                vibtemps / (T * (np.exp(vibtemps / T) - 1))
                - np.log(1 - np.exp(-vibtemps / T))
            )
        elif method == self.QUASI_RRHO:
            mu = PHYSICAL.PLANCK
            mu /= 8 * np.pi ** 2 * freqs * PHYSICAL.SPEED_OF_LIGHT
            mu = mu * Bav / (mu + Bav)
            Sr_eff = 1 / 2 + np.log(
                np.sqrt(
                    8
                    * np.pi ** 3
                    * mu
                    * PHYSICAL.KB
                    * T
                    / PHYSICAL.PLANCK ** 2
                )
            )

            weights = weight = 1 / (1 + (v0 / freqs) ** 4)

            Sv = np.sum(
                weights
                * (
                    harm_vibtemps / (T * (np.exp(harm_vibtemps / T) - 1))
                    - np.log(1 - np.exp(-harm_vibtemps / T))
                )
                + (1 - weights) * Sr_eff
            )

        Ev = np.sum(vibtemps * (1.0 / 2 + 1 / (np.exp(vibtemps / T) - 1)))

        Ev *= PHYSICAL.GAS_CONSTANT
        Sv *= PHYSICAL.GAS_CONSTANT

        Ecorr = (Et + Er + Ev) / (UNIT.HART_TO_KCAL * 1000)
        Hcorr = Ecorr + (
            PHYSICAL.GAS_CONSTANT * T / (UNIT.HART_TO_KCAL * 1000)
        )
        Stot = (St + Sr + Sv + Se) / (UNIT.HART_TO_KCAL * 1000)

        return Ecorr, Hcorr, Stot

    def calc_G_corr(self, temperature=None, v0=0, method="RRHO", **kwargs):
        """
        returns quasi rrho free energy correction (Eh)
        temperature: float, temperature; default is self.temperature
        v0: float, parameter for quasi-rrho or quasi-harmonic entropy
        method: str (RRHO, QRRHO, QHARM) method for treating entropy
                see CompOutput.therm_corr for references
        """
        Ecorr, Hcorr, Stot = self.therm_corr(temperature, v0, method, **kwargs)
        T = temperature if temperature is not None else self.temperature
        Gcorr_qRRHO = Hcorr - T * Stot

        return Gcorr_qRRHO

    def calc_Grimme_G(self, temperature=None, v0=100, **kwargs):
        """
        returns quasi rrho free energy (Eh)
        see Grimme, S. (2012), Supramolecular Binding Thermodynamics by
        Dispersion‐Corrected Density Functional Theory. Chem. Eur. J.,
        18: 9955-9964. (DOI: 10.1002/chem.201200497) for details
        """
        Gcorr_qRRHO = self.calc_G_corr(
            temperature=temperature, v0=v0, method=self.QUASI_RRHO, **kwargs
        )
        return Gcorr_qRRHO + self.energy

    def bond_change(self, atom1, atom2, threshold=0.25):
        """"""
        ref = self.opts[0]
        d_ref = ref.atoms[atom1].dist(ref.atoms[atom2])

        n = len(self.opts) - 1
        for i, step in enumerate(self.opts[::-1]):
            d = step.atoms[atom1].dist(step.atoms[atom2])
            if abs(d_ref - d) < threshold:
                n = len(self.opts) - 1 - i
                break
        return n

    def parse_archive(self):
        """
        Reads info from archive string

        Returns: a dictionary with the parsed information
        """

        def grab_coords(line):
            rv = {}
            for i, word in enumerate(line.split("\\")):
                word = word.split(",")
                if i == 0:
                    rv["charge"] = int(word[0])
                    rv["multiplicity"] = int(word[1])
                    rv["atoms"] = []
                    continue
                rv["atoms"] += [
                    Atom(element=word[0], coords=word[1:4], name=str(i))
                ]
            return rv

        rv = {}
        lines = iter(self.archive.split("\\\\"))
        for line in lines:
            line = line.strip()
            if not line:
                continue

            if line.startswith("@"):
                line = line[1:]
                for word in line.split("\\"):
                    if "summary" not in rv:
                        rv["summary"] = [word]
                    elif word not in rv["summary"]:
                        rv["summary"] += [word]
                continue

            if line.startswith("#"):
                if "route" not in rv:
                    rv["route"] = line
                elif isinstance(rv["route"], list):
                    # for compound jobs, like opt freq
                    rv["route"] += [line]
                else:
                    # for compound jobs, like opt freq
                    rv["route"] = [rv["route"]] + [line]

                line = next(lines).strip()
                if "comment" not in line:
                    rv["comment"] = line

                line = next(lines).strip()
                for key, val in grab_coords(line).items():
                    rv[key] = val
                continue

            words = iter(line.split("\\"))
            for word in words:
                if not word:
                    # get rid of pesky empty elements
                    continue
                if "=" in word:
                    key, val = word.split("=")
                    rv[key.lower()] = float_vec(val)
                else:
                    if "hessian" not in rv:
                        rv["hessian"] = uptri2sym(
                            float_vec(word),
                            3 * len(rv["atoms"]),
                            col_based=True,
                        )
                    else:
                        rv["gradient"] = float_vec(word)
        return rv

    def follow(self, reverse=False, step=0.1):
        """
        Follow imaginary mode
        """
        # get geometry and frequency objects
        geom = self.geometry.copy()
        freq = self.frequency

        # make sure geom is a TS and has computed frequencies available
        if freq is None:
            raise AttributeError("Frequencies for this geometry not found.")
        if not freq.is_TS:
            raise RuntimeError("Geometry not a transition state")

        # get displacement vectors for imaginary frequency
        img_mode = freq.imaginary_frequencies[0]
        vector = freq.by_frequency[img_mode]["vector"]

        # apply transformation to geometry and return it
        for i, a in enumerate(geom.atoms):
            if reverse:
                a.coords -= vector[i] * step
            else:
                a.coords += vector[i] * step
        return geom

    def compute_rot_temps(self):
        """
        sets self's 'rotational_temperature' attribute by using self.geometry
        not recommended b/c atoms should be specific isotopes, but this uses
        average atomic weights
        exists because older versions of ORCA don't print rotational temperatures
        """
        COM = self.geometry.COM(mass_weight=True)
        self.geometry.coord_shift(-COM)
        inertia_mat = np.zeros((3, 3))
        for atom in self.geometry.atoms:
            for i in range(0, 3):
                for j in range(0, 3):
                    if i == j:
                        inertia_mat[i][j] += sum(
                            [
                                atom.mass() * atom.coords[k] ** 2
                                for k in range(0, 3)
                                if k != i
                            ]
                        )
                    else:
                        inertia_mat[i][j] -= (
                            atom.mass() * atom.coords[i] * atom.coords[j]
                        )

        principal_inertia, vecs = np.linalg.eigh(inertia_mat)

        principal_inertia *= UNIT.AMU_TO_KG * 1e-20

        # rotational constants in Hz
        rot_consts = [
            PHYSICAL.PLANCK / (8 * np.pi ** 2 * moment)
            for moment in principal_inertia
            if moment > 0
        ]

        self.rotational_temperature = [
            PHYSICAL.PLANCK * const / PHYSICAL.KB for const in rot_consts
        ]
        # shift geometry back
        self.geometry.coord_shift(COM)
コード例 #14
0
ファイル: unique.py プロジェクト: QChASM/AaronTools.py
            infile = FileReader((f, args.input_format, None), just_geom=False)
        else:
            infile = FileReader(f, just_geom=False)
    else:
        if args.input_format is not None:
            infile = FileReader(("from stdin", args.input_format, f),
                                just_geom=False)
        else:
            if len(sys.argv) >= 1:
                infile = FileReader(("from stdin", "xyz", f), just_geom=False)

    geom = Geometry(infile)
    geom.other = infile.other

    if args.mirror:
        geom_mirrored = geom.copy()
        geom_mirrored.update_geometry(np.dot(geom.coords, mirror_mat))

    n_atoms = len(geom.atoms)

    if n_atoms not in structures:
        structures[n_atoms] = {}

    element_list = [atom.element for atom in geom.atoms]
    elements = sorted(list(set(element_list)))
    s = ""
    for ele in elements:
        s += "%s%i" % (ele, element_list.count(ele))

    if not any(s == key for key in structures[n_atoms].keys()):
        structures[n_atoms][s] = []