/*
 * @BEGIN LICENSE
 *
 * Psi4: an open-source quantum chemistry software package
 *
 * Copyright (c) 2007-2019 The Psi4 Developers.
 *
 * The copyrights for code used from other parties are included in
 * the corresponding files.
 *
 * This file is part of Psi4.
 *
 * Psi4 is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, version 3.
 *
 * Psi4 is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License along
 * with Psi4; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * @END LICENSE
 */

/*! \file
    \ingroup CCENERGY
    \brief Enter brief description of file here
*/
#include "psi4/libdpd/dpd.h"
#include "psi4/libqt/qt.h"
#include "Params.h"
#include "psi4/cc/ccwave.h"

namespace psi {
namespace ccenergy {

/* cc2_Wmnij(): Compute the Wmnij matrix from CC2 theory, which is
** given in spin-orbitals as:
**
** Wmnij = <mn||ij> + P(ij) t_j^e <mn||ie> + t_i^e t_j^f <mn||ef>
**
** TDC, Feb 2004
*/

void CCEnergyWavefunction::cc2_Wmnij_build() {
    dpdbuf4 A, E, D, Z, W, Z1;
    dpdfile2 t1, tIA, tia;

    timer_on("A->Wmnij");
    if (params_.ref == 0) { /** RHF **/
        /* Wmnij <- <mn||ij> */
        global_dpd_->buf4_init(&A, PSIF_CC_AINTS, 0, 0, 0, 0, 0, 0, "A <ij|kl>");
        global_dpd_->buf4_copy(&A, PSIF_CC2_HET1, "CC2 WMnIj");
        global_dpd_->buf4_close(&A);
    } else if (params_.ref == 1) { /** ROHF **/
        /** W(M>N,I>J) <--- <MN||IJ> **/
        /** W(m>n,i>j) <--- <mn||ij> **/
        global_dpd_->buf4_init(&A, PSIF_CC_AINTS, 0, 2, 2, 0, 0, 1, "A <ij|kl>");
        global_dpd_->buf4_copy(&A, PSIF_CC2_HET1, "CC2 WMNIJ (M>N,I>J)");
        global_dpd_->buf4_copy(&A, PSIF_CC2_HET1, "CC2 Wmnij (m>n,i>j)");
        global_dpd_->buf4_close(&A);

        /** W(Mn,Ij) <--- <Mn|Ij> **/
        global_dpd_->buf4_init(&A, PSIF_CC_AINTS, 0, 0, 0, 0, 0, 0, "A <ij|kl>");
        global_dpd_->buf4_copy(&A, PSIF_CC2_HET1, "CC2 WMnIj");
        global_dpd_->buf4_close(&A);
    } else if (params_.ref == 2) { /** UHF **/
        /** W(M>N,I>J) <--- <MN||IJ> **/
        global_dpd_->buf4_init(&A, PSIF_CC_AINTS, 0, 2, 2, 0, 0, 1, "A <IJ|KL>");
        global_dpd_->buf4_copy(&A, PSIF_CC2_HET1, "CC2 WMNIJ (M>N,I>J)");
        global_dpd_->buf4_close(&A);

        /** W(m>n,i>j) <--- <mn||ij> **/
        global_dpd_->buf4_init(&A, PSIF_CC_AINTS, 0, 12, 12, 10, 10, 1, "A <ij|kl>");
        global_dpd_->buf4_copy(&A, PSIF_CC2_HET1, "CC2 Wmnij (m>n,i>j)");
        global_dpd_->buf4_close(&A);

        /** W(Mn,Ij) <--- <Mn|Ij> **/
        global_dpd_->buf4_init(&A, PSIF_CC_AINTS, 0, 22, 22, 22, 22, 0, "A <Ij|Kl>");
        global_dpd_->buf4_copy(&A, PSIF_CC2_HET1, "CC2 WMnIj");
        global_dpd_->buf4_close(&A);
    }
    timer_off("A->Wmnij");

    timer_on("E->Wmnij");
    if (params_.ref == 0) { /** RHF **/

        global_dpd_->file2_init(&t1, PSIF_CC_OEI, 0, 0, 1, "tIA");

        /* Wmnij <- + P(ij) t(j,e) * <mn||ie> */
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 0, 0, 0, 0, 0, "CC2 ZMnIj");
        global_dpd_->buf4_init(&E, PSIF_CC_EINTS, 0, 0, 10, 0, 10, 0, "E <ij|ka>");
        global_dpd_->contract424(&E, &t1, &Z, 3, 1, 0, 1, 0);
        global_dpd_->buf4_close(&E);

        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 0, 0, 0, 0, 0, "CC2 WMnIj");
        global_dpd_->buf4_axpy(&Z, &W, 1);
        global_dpd_->buf4_close(&W);
        global_dpd_->buf4_sort_axpy(&Z, PSIF_CC2_HET1, qpsr, 0, 0, "CC2 WMnIj", 1);
        global_dpd_->buf4_close(&Z);

        global_dpd_->file2_close(&t1);
    } else if (params_.ref == 1) { /** ROHF **/

        global_dpd_->file2_init(&tIA, PSIF_CC_OEI, 0, 0, 1, "tIA");
        global_dpd_->file2_init(&tia, PSIF_CC_OEI, 0, 0, 1, "tia");

        /**** W(M>N,I>J) <-- ZMNIJ <-- P(I/J)( <MN||IE> * t1[J][E] ) ****/
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 2, 0, 2, 0, 0, "Z (M>N,IJ)");
        global_dpd_->buf4_init(&E, PSIF_CC_EINTS, 0, 2, 10, 2, 10, 0, "E <ij||ka> (i>j,ka)");
        global_dpd_->contract424(&E, &tIA, &Z, 3, 1, 0, 1, 0);
        global_dpd_->buf4_close(&E);

        global_dpd_->buf4_sort(&Z, PSIF_CC_TMP0, pqsr, 2, 0, "Z (M>N,JI)");
        global_dpd_->buf4_init(&Z1, PSIF_CC_TMP0, 0, 2, 0, 2, 0, 0, "Z (M>N,JI)");
        global_dpd_->buf4_axpy(&Z1, &Z, -1);
        global_dpd_->buf4_close(&Z1);

        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 2, 0, 2, 2, 0, "CC2 WMNIJ (M>N,I>J)");
        global_dpd_->buf4_axpy(&Z, &W, 1);
        global_dpd_->buf4_close(&Z);
        global_dpd_->buf4_close(&W);

        /**** W(m>n,i>j) <-- Zmnij <-- P(i/j)( <mn||ie> * t1[j][e] ) ****/
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 2, 0, 2, 0, 0, "Z (m>n,ij)");
        global_dpd_->buf4_init(&E, PSIF_CC_EINTS, 0, 2, 10, 2, 10, 0, "E <ij||ka> (i>j,ka)");
        global_dpd_->contract424(&E, &tia, &Z, 3, 1, 0, 1, 0);
        global_dpd_->buf4_close(&E);

        global_dpd_->buf4_sort(&Z, PSIF_CC_TMP0, pqsr, 2, 0, "Z (m>n,ji)");
        global_dpd_->buf4_init(&Z1, PSIF_CC_TMP0, 0, 2, 0, 2, 0, 0, "Z (m>n,ji)");
        global_dpd_->buf4_axpy(&Z1, &Z, -1);
        global_dpd_->buf4_close(&Z1);

        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 2, 0, 2, 2, 0, "CC2 Wmnij (m>n,i>j)");
        global_dpd_->buf4_axpy(&Z, &W, 1);
        global_dpd_->buf4_close(&Z);
        global_dpd_->buf4_close(&W);

        /**** W(Mn,Ij) <-- <Mn|Ie> * t1[j][e] ****/
        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 0, 0, 0, 0, 0, "CC2 WMnIj");
        global_dpd_->buf4_init(&E, PSIF_CC_EINTS, 0, 0, 10, 0, 10, 0, "E <ij|ka>");
        global_dpd_->contract424(&E, &tia, &W, 3, 1, 0, 1, 1);
        global_dpd_->buf4_close(&E);
        global_dpd_->buf4_close(&W);

        /**** W(Mn,Ij) <-- <Mn|Ej> * t1[I][E] ****/
        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 0, 0, 0, 0, 0, "CC2 WMnIj");
        global_dpd_->buf4_init(&E, PSIF_CC_EINTS, 0, 0, 11, 0, 11, 0, "E <ij|ak>");
        global_dpd_->contract244(&tIA, &E, &W, 1, 2, 1, 1, 1);
        global_dpd_->buf4_close(&E);
        global_dpd_->buf4_close(&W);

        global_dpd_->file2_close(&tIA);
        global_dpd_->file2_close(&tia);
    } else if (params_.ref == 2) { /** UHF **/

        global_dpd_->file2_init(&tIA, PSIF_CC_OEI, 0, 0, 1, "tIA");
        global_dpd_->file2_init(&tia, PSIF_CC_OEI, 0, 2, 3, "tia");

        /**** W(M>N,I>J) <-- ZMNIJ <-- P(I/J)( <MN||IE> * t1[J][E] ) ****/
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 2, 0, 2, 0, 0, "Z (M>N,IJ)");
        global_dpd_->buf4_init(&E, PSIF_CC_EINTS, 0, 2, 20, 2, 20, 0, "E <IJ||KA> (I>J,KA)");
        global_dpd_->contract424(&E, &tIA, &Z, 3, 1, 0, 1, 0);
        global_dpd_->buf4_close(&E);

        global_dpd_->buf4_sort(&Z, PSIF_CC_TMP0, pqsr, 2, 0, "Z (M>N,JI)");
        global_dpd_->buf4_init(&Z1, PSIF_CC_TMP0, 0, 2, 0, 2, 0, 0, "Z (M>N,JI)");
        global_dpd_->buf4_axpy(&Z1, &Z, -1);
        global_dpd_->buf4_close(&Z1);

        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 2, 0, 2, 2, 0, "CC2 WMNIJ (M>N,I>J)");
        global_dpd_->buf4_axpy(&Z, &W, 1);
        global_dpd_->buf4_close(&Z);
        global_dpd_->buf4_close(&W);

        /**** W(m>n,i>j) <-- Zmnij <-- P(i/j)( <mn||ie> * t1[j][e] ) ****/
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 12, 10, 12, 10, 0, "Z (m>n,ij)");
        global_dpd_->buf4_init(&E, PSIF_CC_EINTS, 0, 12, 30, 12, 30, 0, "E <ij||ka> (i>j,ka)");
        global_dpd_->contract424(&E, &tia, &Z, 3, 1, 0, 1, 0);
        global_dpd_->buf4_close(&E);

        global_dpd_->buf4_sort(&Z, PSIF_CC_TMP0, pqsr, 12, 10, "Z (m>n,ji)");
        global_dpd_->buf4_init(&Z1, PSIF_CC_TMP0, 0, 12, 10, 12, 10, 0, "Z (m>n,ji)");
        global_dpd_->buf4_axpy(&Z1, &Z, -1);
        global_dpd_->buf4_close(&Z1);

        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 12, 10, 12, 12, 0, "CC2 Wmnij (m>n,i>j)");
        global_dpd_->buf4_axpy(&Z, &W, 1);
        global_dpd_->buf4_close(&Z);
        global_dpd_->buf4_close(&W);

        /**** W(Mn,Ij) <-- <Mn|Ie> * t1[j][e] ****/
        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 22, 22, 22, 22, 0, "CC2 WMnIj");
        global_dpd_->buf4_init(&E, PSIF_CC_EINTS, 0, 22, 24, 22, 24, 0, "E <Ij|Ka>");
        global_dpd_->contract424(&E, &tia, &W, 3, 1, 0, 1, 1);
        global_dpd_->buf4_close(&E);
        global_dpd_->buf4_close(&W);

        /**** W(Mn,Ij) <-- <Mn|Ej> * t1[I][E] ****/
        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 22, 22, 22, 22, 0, "CC2 WMnIj");
        global_dpd_->buf4_init(&E, PSIF_CC_EINTS, 0, 22, 26, 22, 26, 0, "E <Ij|Ak>");
        global_dpd_->contract244(&tIA, &E, &W, 1, 2, 1, 1, 1);
        global_dpd_->buf4_close(&E);
        global_dpd_->buf4_close(&W);

        global_dpd_->file2_close(&tIA);
        global_dpd_->file2_close(&tia);
    }
    timer_off("E->Wmnij");

    timer_on("D->Wmnij");
    if (params_.ref == 0) { /** RHF **/

        global_dpd_->file2_init(&t1, PSIF_CC_OEI, 0, 0, 1, "tIA");

        /* Wmnij<- +1/2 P(ij) t(i,e) t(j,f) * <mn||ef> */
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 0, 10, 0, 10, 0, "CC2 ZMnIf (Mn,If)");
        global_dpd_->buf4_init(&D, PSIF_CC_DINTS, 0, 0, 5, 0, 5, 0, "D <ij|ab>");
        global_dpd_->contract244(&t1, &D, &Z, 1, 2, 1, 1, 0);
        global_dpd_->buf4_close(&D);

        global_dpd_->buf4_init(&Z1, PSIF_CC_TMP0, 0, 0, 0, 0, 0, 0, "CC2 ZMnIj");
        global_dpd_->contract424(&Z, &t1, &Z1, 3, 1, 0, 0.5, 0);
        global_dpd_->buf4_close(&Z);
        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 0, 0, 0, 0, 0, "CC2 WMnIj");
        global_dpd_->buf4_axpy(&Z1, &W, 1);
        global_dpd_->buf4_close(&W);
        global_dpd_->buf4_sort_axpy(&Z1, PSIF_CC2_HET1, qpsr, 0, 0, "CC2 WMnIj", 1);
        global_dpd_->buf4_close(&Z1);

        global_dpd_->file2_close(&t1);
    } else if (params_.ref == 1) { /** ROHF **/

        global_dpd_->file2_init(&tIA, PSIF_CC_OEI, 0, 0, 1, "tIA");
        global_dpd_->file2_init(&tia, PSIF_CC_OEI, 0, 0, 1, "tia");

        /**** W(M>N,I>J) <-- tIE tJF <MN||EF> ****/
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 2, 11, 2, 11, 0, "Z (M>N,EJ)");
        global_dpd_->buf4_init(&D, PSIF_CC_DINTS, 0, 2, 5, 2, 7, 0, "D <ij||ab> (i>j,a>b)");
        global_dpd_->contract424(&D, &tIA, &Z, 3, 1, 0, 1, 0);
        global_dpd_->buf4_close(&D);

        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 2, 0, 2, 2, 0, "CC2 WMNIJ (M>N,I>J)");
        global_dpd_->contract244(&tIA, &Z, &W, 1, 2, 1, 1, 1);
        global_dpd_->buf4_close(&W);
        global_dpd_->buf4_close(&Z);

        /**** W(M>N,I>J) <-- tIE tJF <MN||EF> ****/
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 2, 11, 2, 11, 0, "Z (m>n,ej)");
        global_dpd_->buf4_init(&D, PSIF_CC_DINTS, 0, 2, 5, 2, 7, 0, "D <ij||ab> (i>j,a>b)");
        global_dpd_->contract424(&D, &tia, &Z, 3, 1, 0, 1, 0);
        global_dpd_->buf4_close(&D);

        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 2, 0, 2, 2, 0, "CC2 Wmnij (m>n,i>j)");
        global_dpd_->contract244(&tia, &Z, &W, 1, 2, 1, 1, 1);
        global_dpd_->buf4_close(&W);
        global_dpd_->buf4_close(&Z);

        /**** W(Mn,Ij) <-- tIE tjf <Mn|Ef> ****/
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 0, 11, 0, 11, 0, "Z (Mn,Ej)");
        global_dpd_->buf4_init(&D, PSIF_CC_DINTS, 0, 0, 5, 0, 5, 0, "D <ij|ab>");
        global_dpd_->contract424(&D, &tia, &Z, 3, 1, 0, 1, 0);
        global_dpd_->buf4_close(&D);

        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 0, 0, 0, 0, 0, "CC2 WMnIj");
        global_dpd_->contract244(&tIA, &Z, &W, 1, 2, 1, 1, 1);
        global_dpd_->buf4_close(&W);
        global_dpd_->buf4_close(&Z);

        global_dpd_->file2_close(&tIA);
        global_dpd_->file2_close(&tia);

        purge_cc2_Wmnij();
    } else if (params_.ref == 2) { /** UHF **/

        global_dpd_->file2_init(&tIA, PSIF_CC_OEI, 0, 0, 1, "tIA");
        global_dpd_->file2_init(&tia, PSIF_CC_OEI, 0, 2, 3, "tia");

        /**** W(M>N,I>J) <-- tIE tJF <MN||EF> ****/
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 2, 21, 2, 21, 0, "Z (M>N,EJ)");
        global_dpd_->buf4_init(&D, PSIF_CC_DINTS, 0, 2, 5, 2, 7, 0, "D <IJ||AB> (I>J,A>B)");
        global_dpd_->contract424(&D, &tIA, &Z, 3, 1, 0, 1, 0);
        global_dpd_->buf4_close(&D);

        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 2, 0, 2, 2, 0, "CC2 WMNIJ (M>N,I>J)");
        global_dpd_->contract244(&tIA, &Z, &W, 1, 2, 1, 1, 1);
        global_dpd_->buf4_close(&W);
        global_dpd_->buf4_close(&Z);

        /**** W(M>N,I>J) <-- tIE tJF <MN||EF> ****/
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 12, 31, 12, 31, 0, "Z (m>n,ej)");
        global_dpd_->buf4_init(&D, PSIF_CC_DINTS, 0, 12, 15, 12, 17, 0, "D <ij||ab> (i>j,a>b)");
        global_dpd_->contract424(&D, &tia, &Z, 3, 1, 0, 1, 0);
        global_dpd_->buf4_close(&D);

        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 12, 10, 12, 12, 0, "CC2 Wmnij (m>n,i>j)");
        global_dpd_->contract244(&tia, &Z, &W, 1, 2, 1, 1, 1);
        global_dpd_->buf4_close(&W);
        global_dpd_->buf4_close(&Z);

        /**** W(Mn,Ij) <-- tIE tjf <Mn|Ef> ****/
        global_dpd_->buf4_init(&Z, PSIF_CC_TMP0, 0, 22, 26, 22, 26, 0, "Z (Mn,Ej)");
        global_dpd_->buf4_init(&D, PSIF_CC_DINTS, 0, 22, 28, 22, 28, 0, "D <Ij|Ab>");
        global_dpd_->contract424(&D, &tia, &Z, 3, 1, 0, 1, 0);
        global_dpd_->buf4_close(&D);

        global_dpd_->buf4_init(&W, PSIF_CC2_HET1, 0, 22, 22, 22, 22, 0, "CC2 WMnIj");
        global_dpd_->contract244(&tIA, &Z, &W, 1, 2, 1, 1, 1);
        global_dpd_->buf4_close(&W);
        global_dpd_->buf4_close(&Z);

        global_dpd_->file2_close(&tIA);
        global_dpd_->file2_close(&tia);
    }
    timer_off("D->Wmnij");
}

void CCEnergyWavefunction::purge_cc2_Wmnij() {
    dpdfile4 W;
    int *occpi, *virtpi;
    int i, j, m, n;
    int I, J, M, N;
    int isym, jsym, msym, nsym;
    int *occ_off, *vir_off;
    int *occ_sym, *vir_sym;
    int *openpi, nirreps;

    nirreps = moinfo_.nirreps;
    occpi = moinfo_.occpi;
    virtpi = moinfo_.virtpi;
    occ_off = moinfo_.occ_off;
    vir_off = moinfo_.vir_off;
    occ_sym = moinfo_.occ_sym;
    vir_sym = moinfo_.vir_sym;
    openpi = moinfo_.openpi;

    /* Purge Wmnij matrix elements */
    global_dpd_->file4_init(&W, PSIF_CC2_HET1, 0, 2, 2, "CC2 Wmnij (m>n,i>j)");
    for (int h = 0; h < nirreps; h++) {
        global_dpd_->file4_mat_irrep_init(&W, h);
        global_dpd_->file4_mat_irrep_rd(&W, h);
        for (int mn = 0; mn < W.params->rowtot[h]; mn++) {
            m = W.params->roworb[h][mn][0];
            n = W.params->roworb[h][mn][1];
            msym = W.params->psym[m];
            nsym = W.params->qsym[n];
            M = m - occ_off[msym];
            N = n - occ_off[nsym];
            for (int ij = 0; ij < W.params->coltot[h]; ij++) {
                i = W.params->colorb[h][ij][0];
                j = W.params->colorb[h][ij][1];
                isym = W.params->rsym[i];
                jsym = W.params->ssym[j];
                I = i - occ_off[isym];
                J = j - occ_off[jsym];
                if ((I >= (occpi[isym] - openpi[isym])) || (J >= (occpi[jsym] - openpi[jsym])) ||
                    (M >= (occpi[msym] - openpi[msym])) || (N >= (occpi[nsym] - openpi[nsym])))
                    W.matrix[h][mn][ij] = 0.0;
            }
        }
        global_dpd_->file4_mat_irrep_wrt(&W, h);
        global_dpd_->file4_mat_irrep_close(&W, h);
    }
    global_dpd_->file4_close(&W);

    global_dpd_->file4_init(&W, PSIF_CC2_HET1, 0, 0, 0, "CC2 WMnIj");
    for (int h = 0; h < nirreps; h++) {
        global_dpd_->file4_mat_irrep_init(&W, h);
        global_dpd_->file4_mat_irrep_rd(&W, h);
        for (int mn = 0; mn < W.params->rowtot[h]; mn++) {
            n = W.params->roworb[h][mn][1];
            nsym = W.params->qsym[n];
            N = n - occ_off[nsym];
            for (int ij = 0; ij < W.params->coltot[h]; ij++) {
                j = W.params->colorb[h][ij][1];
                jsym = W.params->ssym[j];
                J = j - occ_off[jsym];
                if ((J >= (occpi[jsym] - openpi[jsym])) || (N >= (occpi[nsym] - openpi[nsym])))
                    W.matrix[h][mn][ij] = 0.0;
            }
        }
        global_dpd_->file4_mat_irrep_wrt(&W, h);
        global_dpd_->file4_mat_irrep_close(&W, h);
    }
    global_dpd_->file4_close(&W);
}
}  // namespace ccenergy
}  // namespace psi
