1 //
2 // BAGEL - Brilliantly Advanced General Electronic Structure Library
3 // Filename: cis.cc
4 // Copyright (C) 2017 Toru Shiozaki
5 //
6 // Author: Toru Shiozaki <shiozaki@northwestern.edu>
7 // Maintainer: Shiozaki group
8 //
9 // This file is part of the BAGEL package.
10 //
11 // This program is free software: you can redistribute it and/or modify
12 // it under the terms of the GNU General Public License as published by
13 // the Free Software Foundation, either version 3 of the License, or
14 // (at your option) any later version.
15 //
16 // This program is distributed in the hope that it will be useful,
17 // but WITHOUT ANY WARRANTY; without even the implied warranty of
18 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19 // GNU General Public License for more details.
20 //
21 // You should have received a copy of the GNU General Public License
22 // along with this program.  If not, see <http://www.gnu.org/licenses/>.
23 //
24 
25 #include <src/response/cis.h>
26 #include <src/prop/multipole.h>
27 #include <src/scf/hf/fock.h>
28 #include <src/util/math/davidson.h>
29 
30 using namespace std;
31 using namespace bagel;
32 
CIS(shared_ptr<const PTree> idata,shared_ptr<const Geometry> geom,shared_ptr<const Reference> ref)33 CIS::CIS(shared_ptr<const PTree> idata, shared_ptr<const Geometry> geom, shared_ptr<const Reference> ref)
34   : Method(idata, geom, ref), nstate_(idata->get<int>("nstate", 1)), nocc_(ref_->nocc()), nvirt_(ref_->nvirt()), maxiter_(idata->get<int>("maxiter", 20)),
35     thresh_(idata->get<double>("thresh", 1.0e-6)), eig_(nocc_+nvirt_) {
36 
37   if (nocc_+nvirt_ != ref->coeff()->mdim())
38     throw runtime_error("nocc + nvirt does not match the dimension of the coefficient");
39 
40   const MatView ocoeff = ref->coeff()->slice(0, nocc_);
41 
42   // compute half-transformed integrals
43   half_ = geom_->df()->compute_half_transform(ocoeff);
44   shared_ptr<const DFHalfDist> halfjj = half_->apply_JJ();
45 
46   shared_ptr<Matrix> fock = make_shared<Fock<1>>(geom_, ref_->hcore(), nullptr, ocoeff, false/*dograd*/, true/*rhf*/);
47   *fock = *ref->coeff() % *fock * *ref->coeff();
48   fock->diagonalize(eig_);
49   coeff_ = make_shared<Matrix>(*ref->coeff() * *fock);
50 
51   // re-compute half-transformed integrals
52   half_ = geom_->df()->compute_half_transform(coeff_->slice(0, nocc_));
53   fulljj_ = half_->compute_second_transform(coeff_->slice(0, nocc_))->apply_JJ();
54 
55 }
56 
57 
compute()58 void CIS::compute() {
59   // initial guess
60   {
61     Matrix ebj(nvirt_, nocc_);
62     for (int i = 0; i != nocc_; ++i)
63       for (int j = 0; j != nvirt_; ++j)
64         ebj(j, i) = eig_[nocc_+j] - eig_[i];
65 
66     for (int i = 0; i != nstate_; ++i) {
67       auto min = min_element(ebj.begin(), ebj.end());
68       auto amp = make_shared<Matrix>(nvirt_, nocc_);
69       *(amp->data() + distance(ebj.begin(), min)) += 1.0;
70       amp_.push_back(amp);
71       *min = 1.0e100;
72     }
73   }
74 
75   DavidsonDiag<Matrix> davidson(nstate_, maxiter_);
76 
77   const MatView ocoeff = coeff_->slice(0, nocc_);
78   const MatView vcoeff = coeff_->slice(nocc_, nocc_+nvirt_);
79 
80   vector<bool> conv(nstate_, false);
81 
82   cout << "  === CIS iteration ===" << endl << endl;
83   Timer timer;
84 
85   for (int iter = 0; iter != maxiter_; ++iter) {
86     vector<shared_ptr<const Matrix>> sigma;
87     for (int ist = 0; ist != nstate_; ++ist) {
88       if (!conv[ist]) {
89         shared_ptr<Matrix> tmp = amp_[ist]->copy();
90         // one body part
91         for (int i = 0; i != nocc_; ++i)
92           for (int j = 0; j != nvirt_; ++j)
93             (*tmp)(j, i) *= eig_[nocc_+j] - eig_[i];
94         const Matrix ovcoeff(vcoeff * *amp_[ist]);
95         // J-type term
96         Matrix one_occ(*geom_->df()->compute_Jop(half_, make_shared<Matrix>(*ovcoeff.transpose()*2.0), false) * ocoeff);
97         // K-type term
98         auto chalf = geom_->df()->compute_half_transform(ovcoeff);
99         one_occ += *chalf->form_2index(fulljj_, -1.0);
100         *tmp += vcoeff % one_occ;
101         sigma.push_back(tmp);
102       } else {
103         sigma.push_back(nullptr);
104       }
105     }
106     assert(amp_.size() == sigma.size());
107 
108     energy_ = davidson.compute(amp_, sigma);
109     vector<shared_ptr<Matrix>> residual = davidson.residual();
110 
111     amp_.clear();
112     for (int ist = 0; ist != nstate_; ++ist) {
113       const double rms = residual[ist]->rms();
114       cout << "      " << setw(3) << iter << (conv[ist] ? " * " : "   ")
115            << setw(3) << ist << setw(15) << setprecision(8) << energy_[ist] << setw(15) << rms << setw(10) << setprecision(2) << timer.tick() << endl;
116       if (rms > thresh_) {
117         for (int i = 0; i != nocc_; ++i)
118           for (int j = 0; j != nvirt_; ++j)
119             residual[ist]->element(j, i) /= (eig_[nocc_+j] - eig_[i] - energy_[ist]);
120         residual[ist]->scale(1.0/residual[ist]->norm());
121         amp_.push_back(residual[ist]);
122         conv[ist] = false;
123       } else {
124         amp_.push_back(nullptr);
125         conv[ist] = true;
126       }
127     }
128     if (nstate_ > 1)
129       cout << endl;
130 
131     if (all_of(conv.begin(), conv.end(), [](const bool i) { return i; })) {
132       cout << "    * CIS converged" << endl << endl;
133       break;
134     }
135   }
136 
137   // setting CI amplitudes. Also compute transition dipole moments
138   vector<shared_ptr<Matrix>> amp = davidson.civec();
139   for (int ist = 0; ist != nstate_; ++ist) {
140     amp_[ist] = amp[ist];
141     auto transao = make_shared<Matrix>(vcoeff * *amp_[ist] ^ ocoeff);
142     stringstream ss; ss << "Transition 0 -> " << ist;
143     Dipole dipole(geom_, transao, ss.str());
144     dipole.compute();
145   }
146 }
147