Exemplo n.º 1
0
def test_contracted_gradients():
    """
    check gradients of contractions

      * dA/dx(U)
      * d(diagA)/dx(V)
      * dF^(e)/dx(E)
      * dF^(n)/dx(N)
      * dS/dx(R)

    """
    # load QM and MM regions from AMBER files
    molecule, polarizable_atoms, point_charges, polarizabilities = amber2psi4('solvated.prmtop', 'solvated.rst7', 'solvated.qmregion')
    
    # basis for QM atoms
    wfn = psi4.core.Wavefunction.build(molecule, 'sto-3g')
    basis = wfn.basisset()
    ribasis = basis

    polham_grad = PolarizationHamiltonianGradients(molecule, basis, ribasis,
                                                   polarizable_atoms, point_charges,
                                                   polarizabilities=polarizabilities,
                                                   verbose=0)

    # number of polarizable sites
    npol = polham_grad.npol
    # number of AOs
    nbf = polham_grad.nbf

    # make random numbers reproducible
    np.random.seed(101)

    # random tensor V for contracting with the gradients of diag(A)
    V = np.random.rand(npol, 3, 3)

    # random matrix D for contracting with the gradients of overlap matrix
    D = np.random.rand(nbf, nbf)

    # random tensor E for contracting with the gradients of F^(elec)_{i,m,n}
    E = np.random.rand(3*npol, nbf, nbf)

    # random tensor Y for contracting with the gradients of I^(elec)_{k,a,b,m,n}
    Y = np.random.rand(npol, 3, 3, nbf, nbf)
    # symmetrize matrices in 2nd and 3rd indices
    Y = 0.5 * (Y + np.einsum('kabmn->kbamn', Y))

    grad_comp = GradientComparison(molecule, basis, ribasis,
                                   polarizable_atoms, point_charges,
                                   polarizabilities=polarizabilities,
                                   verbose=0)

    # compute analytical and numerical QM gradients and compare them
    assert grad_comp.compare_contracted_gradients_diagA(V)
    assert grad_comp.compare_contracted_gradients_overlap(D)

    assert grad_comp.compare_contracted_gradients_F(E)
    assert grad_comp.compare_contracted_gradients_I(Y)
Exemplo n.º 2
0
def test_one_electron_part_gradients():
    """
    check gradients of one-electron energy of polarization Hamiltonian
    """
    # load QM and MM regions from AMBER files
    molecule, polarizable_atoms, point_charges, polarizabilities = amber2psi4('solvated.prmtop', 'solvated.rst7', 'solvated.qmregion')
    
    # basis for QM atoms
    wfn = psi4.core.Wavefunction.build(molecule, 'sto-3g')
    basis = wfn.basisset()
    ribasis = basis

    polham_grad = PolarizationHamiltonianGradients(molecule, basis, ribasis,
                                                   polarizable_atoms, point_charges,
                                                   polarizabilities=polarizabilities,
                                                   verbose=0)

    # number of polarizable sites
    npol = polham_grad.npol
    # number of AOs
    nbf = polham_grad.nbf

    # make random numbers reproducible
    np.random.seed(101)

    # random density matrix
    D = np.random.rand(nbf, nbf)
#    # symmetrize matrix
#    D = 0.5 * (D + np.einsum('mn->nm', D))

    # 1) All integrals are treated with resolution of identity (R.I.)
    grad_comp = GradientComparison(molecule, basis, ribasis,
                                   polarizable_atoms, point_charges,
                                   polarizabilities=polarizabilities,
                                   same_site_integrals='R.I.',
                                   verbose=0)

    # compute analytical and numerical QM gradients and compare them
    assert grad_comp.compare_one_electron_part_gradients(D)

    # 2) Same-site integrals are treated exactly.
    grad_comp = GradientComparison(molecule, basis, ribasis,
                                   polarizable_atoms, point_charges,
                                   polarizabilities=polarizabilities,
                                   same_site_integrals='exact',
                                   verbose=0)

    # compute analytical and numerical QM gradients and compare them
    assert grad_comp.compare_one_electron_part_gradients(D)
Exemplo n.º 3
0
def test_coulomb_exchange_gradients():
    """
    check gradients of Coulomb and exchange energy
    """
    # load QM and MM regions from AMBER files
    molecule, polarizable_atoms, point_charges, polarizabilities = amber2psi4('solvated.prmtop', 'solvated.rst7', 'solvated.qmregion')
    
    # basis for QM atoms
    wfn = psi4.core.Wavefunction.build(molecule, 'sto-3g')
    basis = wfn.basisset()
    ribasis = basis

    polham_grad = PolarizationHamiltonianGradients(molecule, basis, ribasis,
                                                   polarizable_atoms, point_charges,
                                                   polarizabilities=polarizabilities,
                                                   verbose=0)

    # number of polarizable sites
    npol = polham_grad.npol
    # number of AOs
    nbf = polham_grad.nbf

    # make random numbers reproducible
    np.random.seed(101)

    # random density matrices
    D1 = np.random.rand(nbf, nbf)
    D2 = np.random.rand(nbf, nbf)
#    # symmetrize matrices
#    D1 = 0.5 * (D1 + np.einsum('mn->nm', D1))
#    D2 = 0.5 * (D2 + np.einsum('mn->nm', D2))

    grad_comp = GradientComparison(molecule, basis, ribasis,
                                   polarizable_atoms, point_charges,
                                   polarizabilities=polarizabilities,
                                   verbose=0)

    # compute analytical and numerical gradients and compare them
    assert grad_comp.compare_coulomb_J_gradients(D1, D2)
    assert grad_comp.compare_exchange_K_gradients(D1, D2)
Exemplo n.º 4
0
    def compare_one_electron_part_gradients(self, D):
        """
        compare analytical and numerical gradients for the contraction

          dH              d (m|h^(1)|n)
          --(D) =  sum    ------------- D
          dx          m,n      d x       mn

        """
        polham_grad = PolarizationHamiltonianGradients(*self.args, **self.kwds)
        
        molecule, basis, ribasis, polarizable_atoms = self.args[0:4]

        # analytical gradient on QM atoms and point charges
        grad = polham_grad.one_electron_part_GRAD(D)
        grad_QM, grad_POL, grad_CHG = polham_grad.split_gradient(grad)


        # step size for finite differences
        step = 0.0001

        # numerical gradient on QM atoms
        grad_QM_NUM = [None for k in range(0, 3*molecule.natom())]
        for i in range(0, molecule.natom()):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    molecule_ = molecule.clone()
                    coords = molecule_.geometry().np
                    coords[i,xyz] += step
                    molecule_.set_geometry(psi4.core.Matrix.from_array(coords))

                    # The centers of the basis functions are taken from the basis object.
                    wfn = psi4.core.Wavefunction.build(molecule_, basis.name() )
                    basis_ = wfn.basisset()

                    args_ = list(self.args[:])
                    args_[0:3] = (molecule_, basis_, basis_)
                    polham = PolarizationHamiltonian(*args_, **self.kwds)
                    return polham
                # f(x+dx)
                H_plus = shift_coords(step).one_electron_part()
                HD_plus = np.einsum('mn,mn', H_plus, D)
                # f(x-dx)
                H_minus = shift_coords(-step).one_electron_part()
                HD_minus = np.einsum('mn,mn', H_minus, D)

                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_QM_NUM[3*i+xyz] = (HD_plus - HD_minus)/(2*step)

        grad_QM = np.array(grad_QM)
        grad_QM_NUM = np.array(grad_QM_NUM)
        err_QM = la.norm(grad_QM - grad_QM_NUM)

        print("+++ Gradient of core Hamiltonian-like contraction  sum_{m,n} d(m|h^(1)|n)/dx D_{m,n}   on QM atoms +++")
        print(" Analytical:")
        print(grad_QM)
        print(" Numerical:")
        print(grad_QM_NUM)
        print(f" Error: {err_QM}")

        # numerical gradient on polarizable sites
        npol = polarizable_atoms.natom()
        grad_POL_NUM = np.zeros(3*npol)
        
        for i in range(0, npol):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    polarizable_atoms_ = polarizable_atoms.clone()
                    coords = polarizable_atoms_.geometry().np
                    coords[i,xyz] += step
                    polarizable_atoms_.set_geometry(psi4.core.Matrix.from_array(coords))
                    args_ = list(self.args[:])
                    args_[3] = polarizable_atoms_
                    polham = PolarizationHamiltonian(*args_, **self.kwds)
                    return polham
                # f(x+dx)
                H_plus = shift_coords(step).one_electron_part()
                HD_plus = np.einsum('mn,mn', H_plus, D)
                # f(x-dx)
                H_minus = shift_coords(-step).one_electron_part()
                HD_minus = np.einsum('mn,mn', H_minus, D)

                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_POL_NUM[3*i+xyz] = (HD_plus - HD_minus)/(2*step)

        grad_POL = np.array(grad_POL)
        err_POL = la.norm(grad_POL - grad_POL_NUM)

        print("+++ Gradient of core Hamiltonian-like contraction  sum_{m,n} d(m|h^(1)|n)/dx D_{m,n}   on polarizable sites +++")
        print(" Analytical:")
        print(grad_POL)
        print(" Numerical:")
        print(grad_POL_NUM)
        print(f" Error: {err_POL}")


        # numerical gradient on point charges
        point_charges = self.args[4]

        grad_CHG_NUM = [None for k in range(0, 3*point_charges.natom())]
        for i in range(0, point_charges.natom()):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    point_charges_ = point_charges.clone()
                    coords = point_charges_.geometry().np
                    coords[i,xyz] += step
                    point_charges_.set_geometry(psi4.core.Matrix.from_array(coords))
                    args_ = list(self.args[:])
                    args_[4] = point_charges_
                    polham = PolarizationHamiltonian(*args_, **self.kwds)
                    return polham
                # f(x+dx)
                H_plus = shift_coords(step).one_electron_part()
                HD_plus = np.einsum('mn,mn', H_plus, D)
                # f(x-dx)
                H_minus = shift_coords(-step).one_electron_part()
                HD_minus = np.einsum('mn,mn', H_minus, D)

                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_CHG_NUM[3*i+xyz] = (HD_plus - HD_minus)/(2*step)

        grad_CHG = np.array(grad_CHG)
        grad_CHG_NUM = np.array(grad_CHG_NUM)
        err_CHG = la.norm(grad_CHG - grad_CHG_NUM)

        print("+++ Gradient of core Hamiltonian-like contraction  sum_{m,n} d(m|h^(1)|n)/dx D_{m,n}   on point charges +++")
        print(" Analytical:")
        print(grad_CHG)
        print(" Numerical:")
        print(grad_CHG_NUM)
        print(f" Error: {err_CHG}")


        assert err_QM  < 1.0e-5
        assert err_POL < 1.0e-5
        assert err_CHG < 1.0e-5

        # all tests passed
        return True
Exemplo n.º 5
0
    def compare_exchange_K_gradients(self, D1, D2):
        """
        compare analytical and numerical gradients for the contraction

          dK                       (1)  d (pr|h^(2)|qs)   (2)
          --(D1,D2) =  sum        D     ---------------  D
          dx              p,q,r,s  p,q        d x         r,s

        """
        polham_grad = PolarizationHamiltonianGradients(*self.args, **self.kwds)
        
        molecule, basis, ribasis, polarizable_atoms = self.args[0:4]

        # analytical gradient on QM atoms and point charges
        grad = polham_grad.exchange_K_GRAD(D1, D2)
        grad_QM, grad_POL, grad_CHG = polham_grad.split_gradient(grad)

        assert la.norm(grad_CHG) == 0.0

        # step size for finite differences
        step = 0.001

        # numerical gradient on QM atoms
        grad_QM_NUM = [None for k in range(0, 3*molecule.natom())]
        for i in range(0, molecule.natom()):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    molecule_ = molecule.clone()
                    coords = molecule_.geometry().np
                    coords[i,xyz] += step
                    molecule_.set_geometry(psi4.core.Matrix.from_array(coords))

                    # The centers of the basis functions are taken from the basis object.
                    wfn = psi4.core.Wavefunction.build(molecule_, basis.name() )
                    basis_ = wfn.basisset()

                    args_ = list(self.args[:])
                    args_[0:3] = (molecule_, basis_, basis_)
                    polham = PolarizationHamiltonian(*args_, **self.kwds)
                    return polham
                # f(x+dx)
                K_plus = shift_coords(step).exchange_K(D2)
                DKD_plus = np.einsum('mn,mn', D1, K_plus)
                # f(x-dx)
                K_minus = shift_coords(-step).exchange_K(D2)
                DKD_minus = np.einsum('mn,mn', D1, K_minus)

                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_QM_NUM[3*i+xyz] = (DKD_plus - DKD_minus)/(2*step)

        grad_QM = np.array(grad_QM)
        grad_QM_NUM = np.array(grad_QM_NUM)
        err_QM = la.norm(grad_QM - grad_QM_NUM)

        print("+++ Gradient of K-like contraction  sum_{p,q,r,s} D1_{p,q} d(pr|h^(2)|qs)/dx D2_{r,s}   on QM atoms +++")
        print(" Analytical:")
        print(grad_QM)
        print(" Numerical:")
        print(grad_QM_NUM)
        print(f" Error: {err_QM}")

        # numerical gradient on polarizable sites
        npol = polarizable_atoms.natom()
        grad_POL_NUM = np.zeros(3*npol)
        
        for i in range(0, npol):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    polarizable_atoms_ = polarizable_atoms.clone()
                    coords = polarizable_atoms_.geometry().np
                    coords[i,xyz] += step
                    polarizable_atoms_.set_geometry(psi4.core.Matrix.from_array(coords))
                    args_ = list(self.args[:])
                    args_[3] = polarizable_atoms_
                    polham = PolarizationHamiltonian(*args_, **self.kwds)
                    return polham
                # f(x+dx)
                K_plus = shift_coords(step).exchange_K(D2)
                DKD_plus = np.einsum('mn,mn', D1, K_plus)
                # f(x-dx)
                K_minus = shift_coords(-step).exchange_K(D2)
                DKD_minus = np.einsum('mn,mn', D1, K_minus)

                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_POL_NUM[3*i+xyz] = (DKD_plus - DKD_minus)/(2*step)

        grad_POL = np.array(grad_POL)
        err_POL = la.norm(grad_POL - grad_POL_NUM)

        print("+++ Gradient of K-like contraction  sum_{p,q,r,s} D1_{p,q} d(pr|h^(2)|qs)/dx D1_{r,s}   on polarizable sites +++")
        print(" Analytical:")
        print(grad_POL)
        print(" Numerical:")
        print(grad_POL_NUM)
        print(f" Error: {err_POL}")


        assert err_QM  < 1.0e-5
        assert err_POL < 1.0e-5

        # all tests passed
        return True
Exemplo n.º 6
0
    def compare_gradients(self, function_name):
        """
        compare analytical and numerical gradients for the quantity computed by the member
        function `function_name`.

        The function computes the gradients
         * on QM atoms
         * polarizable sites and
         * point charges
        first analytically and then by a two-point finite difference formula.

        Example
        -------
        >>> ...
        >>> G = GradientComparison(molecule, basis, ribasis, polarizable_atoms, point_charges)
        >>> G.compare_gradients('zero_electron_part')

        """
        polham_grad = PolarizationHamiltonianGradients(*self.args, **self.kwds)
        
        molecule = self.args[0]

        # analytical gradients
        func_GRAD = getattr(polham_grad, function_name + "_GRAD")
        grad = func_GRAD()
        grad_QM, grad_POL, grad_CHG = polham_grad.split_gradient(grad)
        
        # Step size for finite differences
        step = 0.001

        # numerical gradient on QM atoms
        grad_QM_NUM = [None for k in range(0, 3*molecule.natom())]
        for i in range(0, molecule.natom()):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    molecule_ = molecule.clone()
                    coords = molecule_.geometry().np
                    coords[i,xyz] += step
                    molecule_.set_geometry(psi4.core.Matrix.from_array(coords))
                    args_ = list(self.args[:])
                    args_[0] = molecule_
                    polham = PolarizationHamiltonian(*args_, **self.kwds)
                    return polham
                # f(x+dx)
                # call member function by its name
                h_plus = getattr(shift_coords(step), function_name)()
                # f(x-dx)
                h_minus = getattr(shift_coords(-step), function_name)()

                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_QM_NUM[3*i+xyz] = (h_plus - h_minus)/(2*step)

        grad_QM = np.array(grad_QM)
        grad_QM_NUM = np.array(grad_QM_NUM)
        err_QM = la.norm(grad_QM - grad_QM_NUM)

        print(f"+++ Gradient of {function_name} on QM atoms +++")
        print(" Analytical:")
        print(grad_QM)
        print(" Numerical:")
        print(grad_QM_NUM)
        print(f" Error: {err_QM}")

        # numerical gradient on polarizable sites
        polarizable_atoms = self.args[3]

        grad_POL_NUM = [None for k in range(0, 3*polarizable_atoms.natom())]
        for i in range(0, polarizable_atoms.natom()):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    polarizable_atoms_ = polarizable_atoms.clone()
                    coords = polarizable_atoms_.geometry().np
                    coords[i,xyz] += step
                    polarizable_atoms_.set_geometry(psi4.core.Matrix.from_array(coords))
                    args_ = list(self.args[:])
                    args_[3] = polarizable_atoms_
                    polham = PolarizationHamiltonian(*args_, **self.kwds)
                    return polham
                # f(x+dx)
                # call member function by its name
                h_plus = getattr(shift_coords(step), function_name)()

                # f(x-dx)
                h_minus = getattr(shift_coords(-step), function_name)()
                
                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_POL_NUM[3*i+xyz] = (h_plus - h_minus)/(2*step)

        grad_POL = np.array(grad_POL)
        grad_POL_NUM = np.array(grad_POL_NUM)
        err_POL = la.norm(grad_POL - grad_POL_NUM)

        print(f"+++ Gradient of {function_name} on polarizable sites +++")
        print(" Analytical:")
        print(grad_POL)
        print(" Numerical:")
        print(grad_POL_NUM)
        print(f" Error: {err_POL}")

        # numerical gradient on point charges
        point_charges = self.args[4]

        grad_CHG_NUM = [None for k in range(0, 3*point_charges.natom())]
        for i in range(0, point_charges.natom()):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    point_charges_ = point_charges.clone()
                    coords = point_charges_.geometry().np
                    coords[i,xyz] += step
                    point_charges_.set_geometry(psi4.core.Matrix.from_array(coords))
                    args_ = list(self.args[:])
                    args_[4] = point_charges_
                    polham = PolarizationHamiltonian(*args_, **self.kwds)
                    return polham
                # f(x+dx)
                # call member function by its name
                h_plus = getattr(shift_coords(step), function_name)()

                # f(x-dx)
                h_minus = getattr(shift_coords(-step), function_name)()
                
                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_CHG_NUM[3*i+xyz] = (h_plus - h_minus)/(2*step)

        grad_CHG = np.array(grad_CHG)
        grad_CHG_NUM = np.array(grad_CHG_NUM)
        err_CHG = la.norm(grad_CHG - grad_CHG_NUM)

        print(f"+++ Gradient of {function_name} on point charges +++")
        print(" Analytical:")
        print(grad_CHG)
        print(" Numerical:")
        print(grad_CHG_NUM)
        print(f" Error: {err_CHG}")

        assert err_QM  < 1.0e-5
        assert err_POL < 1.0e-5
        assert err_CHG < 1.0e-5

        # all tests passed
        return True
Exemplo n.º 7
0
    def compare_contracted_gradients_diagA(self, V):
        """
        compare analytical and numerical gradients for the contraction

           d diag(A)                    d A_{3*k+a,3*k+b}
           --------- (V) = sum  sum     ----------------- V
             d x              k    a,b        d x          k,a,b

        """
        polham_grad = PolarizationHamiltonianGradients(*self.args, **self.kwds)
        
        molecule, basis, ribasis, polarizable_atoms = self.args[0:4]

        # analytical gradient on QM atoms and point charges
        grad = polham_grad._gradDiagA(V)
        grad_QM, grad_POL, grad_CHG = polham_grad.split_gradient(grad)

        assert la.norm(grad_QM ) == 0.0
        assert la.norm(grad_CHG) == 0.0

        # step size for finite differences
        step = 0.001

        # numerical gradient on polarizable sites
        npol = polarizable_atoms.natom()
        grad_POL_NUM = np.zeros(3*npol)
        
        for i in range(0, npol):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    polarizable_atoms_ = polarizable_atoms.clone()
                    coords = polarizable_atoms_.geometry().np
                    coords[i,xyz] += step
                    polarizable_atoms_.set_geometry(psi4.core.Matrix.from_array(coords))
                    args_ = list(self.args[:])
                    args_[3] = polarizable_atoms_
                    polham = PolarizationHamiltonian(*args_, **self.kwds)
                    return polham
                def contract_diagonal(A, V):
                    AV = 0.0
                    npol = polham_grad.npol
                    for k in range(0, npol):
                        for a in [0,1,2]:
                            for b in [0,1,2]:
                                AV += A[3*k+a,3*k+b] * V[k,a,b]
                    return AV
                # f(x+dx)
                A_plus = shift_coords(step).A
                AV_plus = contract_diagonal(A_plus, V)
                # f(x-dx)
                A_minus = shift_coords(-step).A
                AV_minus = contract_diagonal(A_minus, V)
                
                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_POL_NUM[3*i+xyz] = (AV_plus - AV_minus)/(2*step)

        grad_POL = np.array(grad_POL)
        err_POL = la.norm(grad_POL - grad_POL_NUM)

        print("+++ Gradient of contraction  sum_{k,a,b} diag(A)_{k,a,b} V_{k,a,b}   on polarizable sites +++")
        print(" Analytical:")
        print(grad_POL)
        print(" Numerical:")
        print(grad_POL_NUM)
        print(f" Error: {err_POL}")

        assert err_POL < 1.0e-4

        # all tests passed
        return True
Exemplo n.º 8
0
    def compare_contracted_gradients_overlap(self, R):
        """
        compare analytical and numerical gradients for the contraction of the
        overlap matrix with some density matrix R

          dS 
          -- (R)  = sum    d/dx (S   ) R
          dx           m,n        m,n   m,n

        """
        polham_grad = PolarizationHamiltonianGradients(*self.args, **self.kwds)
        
        molecule, basis, ribasis, polarizable_atoms = self.args[0:4]

        # analytical gradient on QM atoms and point charges
        grad = polham_grad._gradS(R)
        grad_QM, grad_POL, grad_CHG = polham_grad.split_gradient(grad)

        assert la.norm(grad_CHG) == 0.0
        assert la.norm(grad_POL) == 0.0

        # step size for finite differences
        step = 0.001

        # numerical gradient on QM atoms
        grad_QM_NUM = [None for k in range(0, 3*molecule.natom())]
        for i in range(0, molecule.natom()):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    molecule_ = molecule.clone()
                    coords = molecule_.geometry().np
                    coords[i,xyz] += step
                    molecule_.set_geometry(psi4.core.Matrix.from_array(coords))

                    # The centers of the basis functions are taken from the basis object.
                    wfn = psi4.core.Wavefunction.build(molecule_, basis.name() )
                    basis_ = wfn.basisset()

                    # compute the overlap matrix
                    mints_ri = psi4.core.MintsHelper(basis_)
                    S = mints_ri.ao_overlap()
                    return S
                # f(x+dx)
                S_plus = shift_coords(step)
                SR_plus = np.einsum('mn,mn', S_plus, R)
                # f(x-dx)
                S_minus = shift_coords(-step)
                SR_minus = np.einsum('mn,mn', S_minus, R)

                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_QM_NUM[3*i+xyz] = (SR_plus - SR_minus)/(2*step)

        grad_QM = np.array(grad_QM)
        grad_QM_NUM = np.array(grad_QM_NUM)
        err_QM = la.norm(grad_QM - grad_QM_NUM)

        print("+++ Gradient of contraction  sum_{m,n} S_{m,n} R_{m,n}   on QM atoms +++")
        print(" Analytical:")
        print(grad_QM)
        print(" Numerical:")
        print(grad_QM_NUM)
        print(f" Error: {err_QM}")

        assert err_QM  < 1.0e-5

        # all tests passed
        return True
Exemplo n.º 9
0
    def compare_contracted_gradients_I(self, Y):
        """
        compare analytical and numerical gradients for the contraction

          dI                              (elec)
          -- (Y)  = sum            d/dx (I         )   Y
          dx           k,a,b,m,n          k;a,b;m,n      k;a,b;m,n

        """
        polham_grad = PolarizationHamiltonianGradients(*self.args, **self.kwds)
        
        molecule, basis, ribasis, polarizable_atoms = self.args[0:4]

        # analytical gradient on QM atoms and point charges
        grad = polham_grad._gradI1e(Y)
        grad_QM, grad_POL, grad_CHG = polham_grad.split_gradient(grad)

        assert la.norm(grad_CHG) == 0.0

        # step size for finite differences
        step = 0.001

        # numerical gradient on QM atoms
        grad_QM_NUM = [None for k in range(0, 3*molecule.natom())]
        for i in range(0, molecule.natom()):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    molecule_ = molecule.clone()
                    coords = molecule_.geometry().np
                    coords[i,xyz] += step
                    molecule_.set_geometry(psi4.core.Matrix.from_array(coords))

                    # The centers of the basis functions are taken from the basis object.
                    wfn = psi4.core.Wavefunction.build(molecule_, basis.name() )
                    basis_ = wfn.basisset()

                    args_ = list(self.args[:])
                    args_[0:3] = (molecule_, basis_, basis_)
                    polham = PolarizationHamiltonian(*args_, **self.kwds)
                    return polham
                # f(x+dx)
                I_plus = shift_coords(step)._polarization_integrals_I()
                IY_plus = np.einsum('kabmn,kabmn', I_plus, Y)
                # f(x-dx)
                I_minus = shift_coords(-step)._polarization_integrals_I()
                IY_minus = np.einsum('kabmn,kabmn', I_minus, Y)

                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_QM_NUM[3*i+xyz] = (IY_plus - IY_minus)/(2*step)

        grad_QM = np.array(grad_QM)
        grad_QM_NUM = np.array(grad_QM_NUM)
        err_QM = la.norm(grad_QM - grad_QM_NUM)

        print("+++ Gradient of contraction  sum_{k,a,b,m,n} I^(elec)_{k,a,b,m,n} Y_{k,a,b,m,n}   on QM atoms +++")
        print(" Analytical:")
        print(grad_QM)
        print(" Numerical:")
        print(grad_QM_NUM)
        print(f" Error: {err_QM}")

        # numerical gradient on polarizable sites
        npol = polarizable_atoms.natom()
        grad_POL_NUM = np.zeros(3*npol)
        
        for i in range(0, npol):
            for xyz in [0,1,2]:
                def shift_coords(step):
                    polarizable_atoms_ = polarizable_atoms.clone()
                    coords = polarizable_atoms_.geometry().np
                    coords[i,xyz] += step
                    polarizable_atoms_.set_geometry(psi4.core.Matrix.from_array(coords))
                    args_ = list(self.args[:])
                    args_[3] = polarizable_atoms_
                    polham = PolarizationHamiltonian(*args_, **self.kwds)
                    return polham
                # f(x+dx)
                I_plus = shift_coords(step)._polarization_integrals_I()
                IY_plus = np.einsum('kabmn,kabmn', I_plus, Y)
                # f(x-dx)
                I_minus = shift_coords(-step)._polarization_integrals_I()
                IY_minus = np.einsum('kabmn,kabmn', I_minus, Y)
                
                # df/dx = (f(x+dx) - f(x-dx))/(2 dx)
                grad_POL_NUM[3*i+xyz] = (IY_plus - IY_minus)/(2*step)

        grad_POL = np.array(grad_POL)
        err_POL = la.norm(grad_POL - grad_POL_NUM)

        print("+++ Gradient of contraction  sum_{k,a,b,m,n} I^(elec)_{k,a,b,m,n} Y_{k,a,b,m,n}   on polarizable sites +++")
        print(" Analytical:")
        print(grad_POL)
        print(" Numerical:")
        print(grad_POL_NUM)
        print(f" Error: {err_POL}")


        assert err_QM  < 1.0e-5
        assert err_POL < 1.0e-5

        # all tests passed
        return True