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