1 //
2 // BAGEL - Brilliantly Advanced General Electronic Structure Library
3 // Filename: casscf.cc
4 // Copyright (C) 2011 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 
26 #include <fstream>
27 #include <src/scf/hf/fock.h>
28 #include <src/multi/casscf/casscf.h>
29 #include <src/multi/casscf/qvec.h>
30 
31 using namespace std;
32 using namespace bagel;
33 
34 
CASSCF(shared_ptr<const PTree> idat,const shared_ptr<const Geometry> geom,const shared_ptr<const Reference> re)35 CASSCF::CASSCF(shared_ptr<const PTree> idat, const shared_ptr<const Geometry> geom, const shared_ptr<const Reference> re)
36   : Method(idat, geom, re), hcore_(make_shared<Hcore>(geom, geom->hcoreinfo())) {
37 
38   // check if RDMs are supplied externally
39   external_rdm_ = idata_->get<string>("external_rdm", "");
40   if (!external_rdm_.empty() && external_rdm_ != "noref") {
41     IArchive ar("ref");
42     ar >> ref_;
43   }
44 
45   // drop the reference if restart is requested
46   if (idata_->get<bool>("restart", false))
47     ref_ = nullptr;
48 
49   if (!ref_) {
50     auto scf = make_shared<RHF>(idat, geom);
51     scf->compute();
52     ref_ = scf->conv_to_ref();
53   }
54 
55   common_init();
56 
57 }
58 
59 
common_init()60 void CASSCF::common_init() {
61   print_header();
62 
63   const shared_ptr<const PTree> iactive = idata_->get_child_optional("active");
64   if (iactive) {
65     set<int> active_indices;
66     // Subtracting one so that orbitals are input in 1-based format but are stored in C format (0-based)
67     for (auto& i : *iactive) active_indices.insert(lexical_cast<int>(i->data()) - 1);
68     ref_ = ref_->set_active(active_indices);
69     cout << " " << endl;
70     cout << "    ==== Active orbitals : ===== " << endl;
71     for (auto& i : active_indices) cout << "         Orbital " << i+1 << endl;
72     cout << "    ============================ " << endl << endl;
73   }
74 
75   thresh_overlap_ = idata_->get<double>("thresh_overlap", 1.0e-8);
76 
77   // first set coefficient
78   coeff_ = ref_->coeff();
79   if (geom_->nbasis() != coeff_->mdim()) {
80     Overlap ovl(geom_);
81     shared_ptr<const Matrix> tildex = ovl.tildex(thresh_overlap_);
82 
83     Matrix c(coeff_->ndim(), tildex->mdim());
84     c.copy_block(0, 0, coeff_->ndim(), coeff_->mdim(), coeff_);
85 
86     shared_ptr<const Matrix> trans = get<0>((*tildex % ovl * *coeff_).svd());
87     c.copy_block(0, coeff_->mdim(), coeff_->ndim(), tildex->mdim()-coeff_->mdim(), *tildex * trans->slice(coeff_->mdim(), tildex->mdim()));
88     coeff_ = make_shared<Coeff>(move(c));
89 
90 #ifndef NDEBUG
91     Matrix unit(coeff_->mdim(), coeff_->mdim()); unit.unit();
92     assert((*coeff_ % ovl * *coeff_ - unit).rms() < 1.0e-10);
93 #endif
94   }
95 
96   // get maxiter from the input
97   max_iter_ = idata_->get<int>("maxiter", 50);
98   // get maxiter from the input
99   max_micro_iter_ = idata_->get<int>("maxiter_micro", 100);
100   // get nstate from the input
101   nstate_ = idata_->get<int>("nstate", 1);
102   energy_.resize(nstate_);
103   // get thresh (for macro iteration) from the input
104   thresh_ = idata_->get<double>("thresh", 1.0e-8);
105   // get thresh (for micro iteration) from the input
106   thresh_micro_ = idata_->get<double>("thresh_micro", 5.0e-6);
107 
108   // whether or not to throw if the calculation does not converge
109   conv_ignore_ = idata_->get<bool>("conv_ignore", false);
110 
111   // to save binary archives with each iteration
112   restart_cas_ = idata_->get<bool>("restart_cas", false);
113 
114   // option for printing natural orbitals
115   natocc_ = idata_->get<bool>("natocc", false);
116 
117   // option for saving canonical orbitals
118   canonical_ = idata_->get<bool>("canonical", false);
119 
120   // nact from the input.
121   nact_ = idata_->get<int>("nact", 0);
122   // FCI algorithm
123   auto fci_algo = idata_->get<string>("fci_algorithm", ((nact_ > 9) && (mpi__->size() >= 8)) ? "parallel" : "knowles");
124   fci_algorithm_ = make_shared<FCI_algorithms>(fci_algo);
125 
126   // nclosed from the input. If not present, full core space is generated.
127   nclosed_ = idata_->get<int>("nclosed", -1);
128   if (nclosed_ < -1) {
129     throw runtime_error("It appears that nclosed < 0. Check nocc value.");
130   } else if (nclosed_ == -1) {
131     cout << "    * full core space generated for nclosed." << endl;
132     nclosed_ = geom_->num_count_ncore_only() / 2;
133   }
134   nocc_ = nclosed_ + nact_;
135 
136   nmo_ = coeff_->mdim();
137   nvirt_ = nmo_ - nocc_;
138   if (nvirt_ < 0) throw runtime_error("It appears that nvirt < 0. Check the nocc value");
139 
140   cout << "    * nstate   : " << setw(6) << nstate_ << endl;
141   cout << "    * nclosed  : " << setw(6) << nclosed_ << endl;
142   cout << "    * nact     : " << setw(6) << nact_ << endl;
143   cout << "    * nvirt    : " << setw(6) << nvirt_ << endl;
144 
145   const int idel = geom_->nbasis() - nmo_;
146   if (idel)
147     cout << "      Due to linear dependency, " << idel << (idel==1 ? " function is" : " functions are") << " omitted" << endl;
148 
149 
150   // CASSCF methods should have FCI member. Inserting "ncore" and "norb" keyword for closed and total orbitals.
151   muffle_ = make_shared<Muffle>("casscf.log");
152   if (nact_) {
153     auto idata = make_shared<PTree>(*idata_);
154     idata->erase("active");
155     if (fci_algorithm_->is_knowles()) {
156       cout << "    * Using serial Knowles-Handy algorithm in FCI." << endl;
157       fci_ = make_shared<KnowlesHandy>(idata, geom_, ref_, nclosed_, nact_, /*nstates to be read from idata*/-1, /*store*/true);
158     } else if (fci_algorithm_->is_harrison()) {
159       cout << "    * Using serial Harrison-Zarrabian algorithm in FCI." << endl;
160       fci_ = make_shared<HarrisonZarrabian>(idata, geom_, ref_, nclosed_, nact_, /*nstates to be read from idata*/-1, /*store*/true);
161 #ifdef HAVE_MPI_H
162     } else if (fci_algorithm_->is_dist()) {
163       cout << "    * Using parallel algorithm in FCI." << endl;
164       fci_ = make_shared<DistFCI>(idata, geom_, ref_, nclosed_, nact_, /*nstates to be read from idata*/-1, /*store*/true);
165 #endif
166     }
167   }
168   muffle_->unmute();
169 
170   do_hyperfine_ = idata_->get<bool>("hyperfine", false);
171 
172   cout <<  "  === CASSCF iteration (" + geom_->basisfile() + ") ===" << endl << endl;
173 
174 }
175 
176 
~CASSCF()177 CASSCF::~CASSCF() {
178 
179 }
180 
181 
print_header() const182 void CASSCF::print_header() const {
183   cout << "  ---------------------------" << endl;
184   cout << "      CASSCF calculation     " << endl;
185   cout << "  ---------------------------" << endl << endl;
186 }
187 
print_iteration(const int iter,const vector<double> & energy,const double error,const double time) const188 void CASSCF::print_iteration(const int iter, const vector<double>& energy, const double error, const double time) const {
189   muffle_->unmute();
190   if (energy.size() != 1 && iter) cout << endl;
191 
192   int i = 0;
193   for (auto& e : energy) {
194     cout << "  " << setw(5) << iter << setw(3) << i << setw(19) << fixed << setprecision(8) << e << "   "
195                  << setw(10) << scientific << setprecision(2) << (i==0 ? error : 0.0) << fixed << setw(10) << setprecision(2) << time << endl;
196     ++i;
197   }
198   muffle_->mute();
199 }
200 
201 
ao_rdm1(shared_ptr<const RDM<1>> rdm1,const bool inactive_only) const202 shared_ptr<Matrix> CASSCF::ao_rdm1(shared_ptr<const RDM<1>> rdm1, const bool inactive_only) const {
203   // first make 1RDM in MO
204   const size_t nmobasis = coeff_->mdim();
205   auto mo_rdm1 = make_shared<Matrix>(nmobasis, nmobasis);
206   for (int i = 0; i != nclosed_; ++i) mo_rdm1->element(i,i) = 2.0;
207   if (!inactive_only) {
208     for (int i = 0; i != nact_; ++i) {
209       for (int j = 0; j != nact_; ++j) {
210         mo_rdm1->element(nclosed_+j, nclosed_+i) = rdm1->element(j,i);
211       }
212     }
213   }
214   // transform into AO basis
215   return make_shared<Matrix>(*coeff_ * *mo_rdm1 ^ *coeff_);
216 }
217 
218 
compute_active_fock(const MatView acoeff,shared_ptr<const RDM<1>> rdm1) const219 std::shared_ptr<Matrix> CASSCF::compute_active_fock(const MatView acoeff, shared_ptr<const RDM<1>> rdm1) const {
220   Matrix dkl(nact_, nact_);
221   copy_n(rdm1->data(), nact_*nact_, dkl.data());
222   dkl.sqrt();
223   dkl.scale(1.0/sqrt(2.0));
224   return make_shared<Fock<1>>(geom_, hcore_->clone(), nullptr, acoeff * dkl, /*store*/false, /*rhf*/true);
225 }
226 
227 
semi_canonical_orb() const228 tuple<shared_ptr<const Coeff>,VectorB,VectorB> CASSCF::semi_canonical_orb() const {
229   auto rdm1mat = make_shared<Matrix>(nact_, nact_);
230   if (nact_) {
231     copy_n(fci_->rdm1_av()->data(), rdm1mat->size(), rdm1mat->data());
232     rdm1mat->sqrt();
233     rdm1mat->scale(1.0/sqrt(2.0));
234   }
235   auto ocoeff = coeff_->slice(0, nclosed_);
236   auto acoeff = coeff_->slice(nclosed_, nocc_);
237   auto vcoeff = coeff_->slice(nocc_, nmo_);
238 
239   Fock<1> fock;
240   if (nact_)
241     fock = Fock<1>(geom_, fci_->jop()->core_fock(), nullptr, acoeff * *rdm1mat, false, /*rhf*/true);
242   else
243     fock = Fock<1>(geom_, ref_->hcore(), nullptr, coeff_->slice_copy(0, nclosed_), false, /*rhf*/true);
244 
245   VectorB eig(coeff_->mdim());
246   VectorB occup(coeff_->mdim());
247   // calculate the transformation matrix to (semi-)canonical orbitals
248   Matrix trans(nmo_, nmo_);
249   trans.unit();
250   if (nclosed_) {
251     Matrix ofock = ocoeff % fock * ocoeff;
252     VectorB tmp(nclosed_);
253     ofock.diagonalize(tmp);
254     trans.copy_block(0, 0, nclosed_, nclosed_, ofock);
255     copy_n(tmp.data(), nclosed_, eig.data());
256     fill_n(occup.data(), nclosed_, 2.0);
257   }
258   if (nact_ && canonical_) {
259     Matrix afock = acoeff % fock * acoeff;
260     VectorB tmp(nact_);
261     afock.diagonalize(tmp);
262     trans.copy_block(nclosed_, nclosed_, nact_, nact_, afock);
263     copy_n(tmp.data(), nact_, eig.data()+nclosed_);
264   }
265   {
266     Matrix vfock = vcoeff % fock * vcoeff;
267     VectorB tmp(nvirt_);
268     vfock.diagonalize(tmp);
269     trans.copy_block(nocc_, nocc_, nvirt_, nvirt_, vfock);
270     copy_n(tmp.data(), nvirt_, eig.data()+nclosed_+nact_);
271   }
272   // finally calculate the occupation numbers for active orbitals (diagonal elements of transformed 1RDM)
273   {
274     shared_ptr<const Matrix> atrans = trans.get_submatrix(nclosed_, nclosed_, nact_, nact_);
275     const Matrix transrdm = *atrans * *rdm1mat ^ *atrans;
276     for (int i = 0; i != nact_; ++i)
277       occup[i+nclosed_] = transrdm(i, i);
278   }
279 
280   auto coeffout = make_shared<Coeff>(*coeff_ * trans);
281   return make_tuple(coeffout, move(eig), move(occup));
282 }
283 
284 
spin_density() const285 shared_ptr<const Matrix> CASSCF::spin_density() const {
286   Matrix den(nact_, nact_);
287   shared_ptr<const RDM<1>> rdm1 = fci_->rdm1(0);
288   copy_n(rdm1->data(), nact_*nact_, den.data());
289   den.scale((4.0 - fci_->det()->nelea() - fci_->det()->neleb()) * 0.5);
290 
291   shared_ptr<RDM<2>> rdm2 = fci_->rdm2(0);
292   for (int i = 0; i != nact_; ++i)
293     for (int j = 0; j != nact_; ++j)
294       for (int k = 0; k != nact_; ++k)
295         den(j,i) -= rdm2->element(j,k,k,i);
296 
297   den.scale(1.0 / (fci_->det()->nspin()*0.5 + 1.0));
298   auto acoeff = coeff_->slice(nclosed_, nclosed_+nact_);
299   return make_shared<Matrix>(acoeff * den ^ acoeff);
300 }
301 
302 
conv_to_ref() const303 shared_ptr<const Reference> CASSCF::conv_to_ref() const {
304   const bool noci = !nact_ || external_rdm_ == "noref";
305   auto ref = noci ? make_shared<Reference>(geom_, coeff_, nclosed_, nact_, nvirt_, energy_)
306                   : make_shared<Reference>(geom_, coeff_, nclosed_, nact_, nvirt_, energy_,
307                                            fci_->rdm1(), fci_->rdm2(), fci_->rdm1_av(), fci_->rdm2_av(), fci_->conv_to_ciwfn());
308   ref->set_eig(eig_);
309   ref->set_occup(occup_);
310   return ref;
311 }
312