Example #1
0
    def _makeDihedralFittingSetFromQMResults(self, atoms, results):
        # Extract the valid QM poses and energies from the QM result set
        # Evaluate the MM on those poses
        ffe = FFEvaluate(self)

        ret = QMFittingSet()
        ret.name = "%s-%s-%s-%s" % (self._rtf.names[atoms[0]], self._rtf.names[
            atoms[1]], self._rtf.names[atoms[2]], self._rtf.names[atoms[3]])

        completed = 0

        qmin = 1.e100
        for q in results:
            if q.completed and not q.errored:
                if q.energy < qmin:
                    qmin = q.energy

        completed = 0
        for q in results:
            if q.completed and not q.errored:
                if (
                        q.energy - qmin
                ) < 20.:  # Only fit against QM points < 20 kcal above the minimum
                    mmeval = ffe.evaluate(q.coords)
                    if mmeval["vdw"] < 200:
                        completed += 1
                        phi = getPhi(q.coords, atoms)

                        ret.qm.append(q.energy - qmin)
                        ret.mm_original.append(mmeval['total'])
                        ret.coords.append(q.coords)
                        if phi > 180.:
                            phi -= 360.
                        ret.phi.append(phi)
                    else:
                        print(
                            "Omitting optimised pose for phi=%f (MM VDW too high)"
                            % phi)
                else:
                    print(
                        "Omitting optimised QM pose (QM energy too high %f)" %
                        q.energy)

        mmin = min(ret.mm_original)
        # roughly align the qm with the mm
        for q in range(completed):
            ret.mm_original[q] = ret.mm_original[q] - mmin
        ret.N = completed

        if completed < 5:
            raise RuntimeError(
                "Fewer than 5 valid QM points. Not enough to fit!")

        return ret
Example #2
0
    def _makeDihedralFittingSetFromQMResults(self, atoms, results):
        # Extract the valid QM poses and energies from the QM result set
        # Evaluate the MM on those poses
        ffe = FFEvaluate(self)

        ret = QMFittingSet()
        ret.name = "%s-%s-%s-%s" % (
            self._rtf.names[atoms[0]], self._rtf.names[atoms[1]], self._rtf.names[atoms[2]], self._rtf.names[atoms[3]])

        completed = 0

        qmin = 1.e100
        for q in results:
            if q.completed and not q.errored:
                if q.energy < qmin:
                    qmin = q.energy

        completed = 0
        for q in results:
            if q.completed and not q.errored:
                if (q.energy - qmin) < 20.:  # Only fit against QM points < 20 kcal above the minimum
                    mmeval = ffe.evaluate(q.coords)
                    if mmeval["vdw"] < 200:
                        completed += 1
                        phi = getPhi(q.coords, atoms)

                        ret.qm.append(q.energy - qmin)
                        ret.mm_original.append(mmeval['total'])
                        ret.coords.append(q.coords)
                        if phi > 180.:
                            phi -= 360.
                        ret.phi.append(phi)
                    else:
                        print("Omitting optimised pose for phi=%f (MM VDW too high)" % phi)
                else:
                    print("Omitting optimised pose for phi=%f (QM energy too high)" % phi)

        mmin = min(ret.mm_original)
        # roughly align the qm with the mm
        for q in range(completed):
            ret.mm_original[q] = ret.mm_original[q] - mmin
        ret.N = completed

        if completed < 5:
            raise RuntimeError("Fewer than 5 valid QM points. Not enough to fit!")

        return ret
Example #3
0
    def fitSoftTorsion(self, phi, geomopt=True):
        found = False
        phi_to_fit = None
        frozens = []
        dih_index = 0
        i = 0
        bkp_coords = self.coords.copy()

        for d in self._soft_dihedrals:
            if (d.atoms == phi).all():
                phi_to_fit = d
                dih_index = i
                frozens.append(d.atoms)
            else:
                if not geomopt:
                    frozens.append(d.atoms)
            i += 1
        if not phi_to_fit:
            raise ValueError("specified phi is not a recognised soft dihedral")
        self._makeDihedralUnique(phi_to_fit)

        atoms = phi_to_fit.atoms
        left = phi_to_fit.left
        right = phi_to_fit.right
        equivs = phi_to_fit.equivalents

        step = 10  # degrees
        nstep = int(360 / step)
        cset = np.zeros((self.natoms, 3, nstep))

        i = 0
        for phi in range(-180, 180, step):
            cset[:, :, i] = setPhi(self.coords[:, :, 0], atoms, left, right, phi)
            i += 1

        mol = self.copy()
        mol.coords = cset

        dirname = "dihedral-single-point"
        if geomopt:
            dirname = "dihedral-opt"

        dih_name = "%s-%s-%s-%s" % (self.name[atoms[0]], self.name[atoms[1]], self.name[atoms[2]], self.name[atoms[3]])

        fitdir = os.path.join(self.outdir, dirname, dih_name, self.output_directory_name()) 

        try:
            os.makedirs(fitdir, exist_ok=True)
        except:
            raise OSError('Directory {} could not be created. Check if you have permissions.'.format(fitdir))

        qmset = QMCalculation(mol, charge=self.netcharge, directory=fitdir, frozen=frozens, optimize=geomopt, theory=self.theory, solvent=self.solvent,
                              basis=self.basis, execution=self.execution, code=self.qmcode)

        ret = self._makeDihedralFittingSetFromQMResults(atoms, qmset.results())

        # Get the initial parameters of the dihedral we are going to fit

        param = self._prm.dihedralParam(self._rtf.type_by_index[atoms[0]],
                                        self._rtf.type_by_index[atoms[1]],
                                        self._rtf.type_by_index[atoms[2]],
                                        self._rtf.type_by_index[atoms[3]])

        # Save these parameters as the best fit (fit to beat)
        best_param = np.zeros((13))
        for t in range(6):
            best_param[t] = param[t].k0
            best_param[t + 6] = param[t].phi0
        best_param[12] = 0.

        #    print(param)

        # Evalaute the mm potential with this dihedral zeroed out
        # The objective function will try to fit to the delta between
        # The QM potential and the this modified mm potential

        for t in param:
            t.k0 = t.phi0 = 0.
            t.e14 = 1.  # Always fit with e14 scaling of 1. per CHARMM
        self._prm.updateDihedral(param)

        ffe = FFEvaluate(self)
        #  print(ffe.evaluate( ret.coords[0] ) )
        #  input
        # Now evaluate the ff without the dihedral being fitted
        for t in range(ret.N):
            mm_zeroed = (ffe.evaluate(ret.coords[t])["total"])
            ret.mm_delta.append(ret.qm[t] - mm_zeroed)
            ret.mm_zeroed.append(mm_zeroed)

        mmin1 = min(ret.mm_zeroed)
        mmin2 = min(ret.mm_delta)
        for t in range(ret.N):
            ret.mm_zeroed[t] = ret.mm_zeroed[t] - mmin1
            ret.mm_delta[t] = ret.mm_delta[t] - mmin2

        self._fitDihedral_results = ret
        self._fitDihedral_phi = param

        # Now measure all of the soft dihedrals phis that are mapped to this dihedral
        ret.phis = []
        for i in range(ret.N):
            ret.phis.append([ret.phi[i]])
            for e in equivs:
                ret.phis[i].append(getPhi(ret.coords[i], e))
            #    print ("EQUIVALENT DIHEDRALS FOR THIS DIHEDRAL" )
            #    print(equivs)
            #    print ("PHI VALUES TO FIT")
            #    print (ret.phis)
        # Set up the NOLOPT fit
        #  There are 13 parameters, k,phi for n=1,2,3,4,5,6 and a shift
        N = 13
        # initial guess,
        st = np.zeros(13)
        # bounds

        best_chisq = self._fitDihedral_objective(best_param)
        #    print("CHISQ of initial = %f" % ( best_chisq ) )

        # Now zero out the terms of the dihedral we are going to fit
        bar = ProgressBar(64, description="Fitting")
        for i in range(64):

            (bounds, start) = self._fitDihedral_make_bounds(i)

            xopt = optimize.minimize(self._fitDihedral_objective, start, method="L-BFGS-B", bounds=bounds,
                                     options={'disp': False})

            chisq = self._fitDihedral_objective(xopt.x)
            #      print( "CHISQ of fit = %f " % (chisq) )
            if (chisq < best_chisq):
                best_chisq = chisq
                best_param = xopt.x
            bar.progress()
        bar.stop()
        #    print("Best ChiSQ = %f" %(best_chisq) )

        # Update the target dihedral with the optimized parameters
        # print(param)
        # print(best_param )
        for i in range(6):
            param[i].k0 = best_param[0 + i]
            param[i].phi0 = best_param[6 + i]

        self._prm.updateDihedral(param)
        # print(param)
        param = self._prm.dihedralParam(self._rtf.type_by_index[atoms[0]],
                                        self._rtf.type_by_index[atoms[1]],
                                        self._rtf.type_by_index[atoms[2]],
                                        self._rtf.type_by_index[atoms[3]])
        # print(param)

        # Finally evaluate the fitted potential
        ffe = FFEvaluate(self)
        for t in range(ret.N):
            ret.mm_fitted.append(ffe.evaluate(ret.coords[t])["total"])
        mmin = min(ret.mm_fitted)
        chisq = 0.

        #    print( "QM energies" )
        #    print( ret.qm )

        for t in range(ret.N):
            ret.mm_fitted[t] = ret.mm_fitted[t] - mmin
            delta = ret.mm_fitted[t] - ret.qm[t]
            chisq = chisq + (delta * delta)
        ret.chisq = chisq

        # TODO Score it
        self.coords = bkp_coords

        return ret
Example #4
0
    def fitSoftTorsion(self, phi, geomopt=True):
        found = False
        phi_to_fit = None
        frozens = []
        dih_index = 0
        i = 0
        bkp_coords = self.coords.copy()

        for d in self._soft_dihedrals:
            if (d.atoms == phi).all():
                phi_to_fit = d
                dih_index = i
                frozens.append(d.atoms)
            else:
                if not geomopt:
                    frozens.append(d.atoms)
            i += 1
        if not phi_to_fit:
            raise ValueError("specified phi is not a recognised soft dihedral")
        self._makeDihedralUnique(phi_to_fit)

        atoms = phi_to_fit.atoms
        left = phi_to_fit.left
        right = phi_to_fit.right
        equivs = phi_to_fit.equivalents

        step = 10  # degrees
        nstep = int(360 / step)
        cset = np.zeros((self.natoms, 3, nstep))

        i = 0
        for phi in range(-180, 180, step):
            cset[:, :, i] = setPhi(self.coords[:, :, 0], atoms, left, right,
                                   phi)
            i += 1

        mol = self.copy()
        mol.coords = cset

        dirname = "dihedral-single-point"
        if geomopt:
            dirname = "dihedral-opt"

        dih_name = "%s-%s-%s-%s" % (self.name[atoms[0]], self.name[atoms[1]],
                                    self.name[atoms[2]], self.name[atoms[3]])

        fitdir = os.path.join(self.outdir, dirname, dih_name,
                              self.output_directory_name())

        try:
            os.makedirs(fitdir, exist_ok=True)
        except:
            raise OSError(
                'Directory {} could not be created. Check if you have permissions.'
                .format(fitdir))

        qmset = QMCalculation(mol,
                              charge=self.netcharge,
                              directory=fitdir,
                              frozen=frozens,
                              optimize=geomopt,
                              theory=self.theory,
                              solvent=self.solvent,
                              basis=self.basis,
                              execution=self.execution,
                              code=self.qmcode)

        ret = self._makeDihedralFittingSetFromQMResults(atoms, qmset.results())

        # Get the initial parameters of the dihedral we are going to fit

        param = self._prm.dihedralParam(self._rtf.type_by_index[atoms[0]],
                                        self._rtf.type_by_index[atoms[1]],
                                        self._rtf.type_by_index[atoms[2]],
                                        self._rtf.type_by_index[atoms[3]])

        # Save these parameters as the best fit (fit to beat)
        best_param = np.zeros((13))
        for t in range(6):
            best_param[t] = param[t].k0
            best_param[t + 6] = param[t].phi0
        best_param[12] = 0.

        #    print(param)

        # Evalaute the mm potential with this dihedral zeroed out
        # The objective function will try to fit to the delta between
        # The QM potential and the this modified mm potential

        for t in param:
            t.k0 = t.phi0 = 0.
            #t.e14 = 1.  # Use whatever e14 has been inherited for the type
        self._prm.updateDihedral(param)

        ffe = FFEvaluate(self)
        #  print(ffe.evaluate( ret.coords[0] ) )
        #  input
        # Now evaluate the ff without the dihedral being fitted
        for t in range(ret.N):
            mm_zeroed = (ffe.evaluate(ret.coords[t])["total"])
            ret.mm_delta.append(ret.qm[t] - mm_zeroed)
            ret.mm_zeroed.append(mm_zeroed)

        mmin1 = min(ret.mm_zeroed)
        mmin2 = min(ret.mm_delta)
        for t in range(ret.N):
            ret.mm_zeroed[t] = ret.mm_zeroed[t] - mmin1
            ret.mm_delta[t] = ret.mm_delta[t] - mmin2

        self._fitDihedral_results = ret
        self._fitDihedral_phi = param

        # Now measure all of the soft dihedrals phis that are mapped to this dihedral
        ret.phis = []
        for i in range(ret.N):
            ret.phis.append([ret.phi[i]])
            for e in equivs:
                ret.phis[i].append(getPhi(ret.coords[i], e))
            #    print ("EQUIVALENT DIHEDRALS FOR THIS DIHEDRAL" )
            #    print(equivs)
            #    print ("PHI VALUES TO FIT")
            #    print (ret.phis)
        # Set up the NOLOPT fit
        #  There are 13 parameters, k,phi for n=1,2,3,4,5,6 and a shift
        N = 13
        # initial guess,
        st = np.zeros(13)
        # bounds

        best_chisq = self._fitDihedral_objective(best_param)
        #    print("CHISQ of initial = %f" % ( best_chisq ) )

        # Now zero out the terms of the dihedral we are going to fit
        bar = ProgressBar(64, description="Fitting")
        for i in range(64):

            (bounds, start) = self._fitDihedral_make_bounds(i)

            xopt = optimize.minimize(self._fitDihedral_objective,
                                     start,
                                     method="L-BFGS-B",
                                     bounds=bounds,
                                     options={'disp': False})

            chisq = self._fitDihedral_objective(xopt.x)
            #      print( "CHISQ of fit = %f " % (chisq) )
            if (chisq < best_chisq):
                best_chisq = chisq
                best_param = xopt.x
            bar.progress()
        bar.stop()
        #    print("Best ChiSQ = %f" %(best_chisq) )

        # Update the target dihedral with the optimized parameters
        # print(param)
        # print(best_param )
        for i in range(6):
            param[i].k0 = best_param[0 + i]
            param[i].phi0 = best_param[6 + i]

        self._prm.updateDihedral(param)
        # print(param)
        param = self._prm.dihedralParam(self._rtf.type_by_index[atoms[0]],
                                        self._rtf.type_by_index[atoms[1]],
                                        self._rtf.type_by_index[atoms[2]],
                                        self._rtf.type_by_index[atoms[3]])
        # print(param)

        # Finally evaluate the fitted potential
        ffe = FFEvaluate(self)
        for t in range(ret.N):
            ret.mm_fitted.append(ffe.evaluate(ret.coords[t])["total"])
        mmin = min(ret.mm_fitted)
        chisq = 0.

        #    print( "QM energies" )
        #    print( ret.qm )

        for t in range(ret.N):
            ret.mm_fitted[t] = ret.mm_fitted[t] - mmin
            delta = ret.mm_fitted[t] - ret.qm[t]
            chisq = chisq + (delta * delta)
        ret.chisq = chisq

        # TODO Score it
        self.coords = bkp_coords

        return ret