1 //
2 // BAGEL - Brilliantly Advanced General Electronic Structure Library
3 // Filename: reference.cc
4 // Copyright (C) 2012 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/wfn/reference.h>
26 #include <src/wfn/relreference.h>
27 #include <src/integral/os/overlapbatch.h>
28 #include <src/mat1e/mixedbasis.h>
29 #include <src/ci/fci/fci.h>
30 #include <src/util/io/moldenin.h>
31 
32 BOOST_CLASS_EXPORT_IMPLEMENT(bagel::Reference)
33 
34 using namespace std;
35 using namespace bagel;
36 
Reference(shared_ptr<const Geometry> g,shared_ptr<const Coeff> c,const int _nclosed,const int _nact,const int _nvirt,const vector<double> en,shared_ptr<const VecRDM<1>> _rdm1,shared_ptr<const VecRDM<2>> _rdm2,shared_ptr<const RDM<1>> _rdm1_av,shared_ptr<const RDM<2>> _rdm2_av,shared_ptr<const CIWfn> ci)37 Reference::Reference(shared_ptr<const Geometry> g, shared_ptr<const Coeff> c,
38                      const int _nclosed, const int _nact, const int _nvirt,
39                      const vector<double> en,
40                      shared_ptr<const VecRDM<1>> _rdm1, shared_ptr<const VecRDM<2>> _rdm2,
41                      shared_ptr<const RDM<1>> _rdm1_av, shared_ptr<const RDM<2>> _rdm2_av,
42                      shared_ptr<const CIWfn> ci)
43  : geom_(g), noccA_(0), noccB_(0), energy_(en), hcore_(make_shared<Hcore>(geom_,geom_->hcoreinfo())), nclosed_(_nclosed), nact_(_nact), nvirt_(_nvirt), nstate_(1), ciwfn_(ci), rdm1_(_rdm1), rdm2_(_rdm2),
44    rdm1_av_(_rdm1_av), rdm2_av_(_rdm2_av) {
45 
46   // we need to make sure that all the quantities are consistent in every MPI process
47   if (c) {
48     mpi__->broadcast(const_pointer_cast<Coeff>(c)->data(), c->size(), 0);
49     coeff_ = c;
50   }
51 
52   for (auto& i : *rdm1_)
53     mpi__->broadcast(i.second->data(), i.second->size(), 0);
54   for (auto& i : *rdm2_)
55     mpi__->broadcast(i.second->data(), i.second->size(), 0);
56   if (rdm1_av_)
57     mpi__->broadcast(const_cast<double*>(rdm1_av_->data()), rdm1_av_->size(), 0);
58   if (rdm2_av_)
59     mpi__->broadcast(const_cast<double*>(rdm2_av_->data()), rdm2_av_->size(), 0);
60 
61   occup_ = VectorB(nclosed_+nact_+nvirt_);
62   fill_n(occup_.data(), nclosed_, 2.0);
63 
64   //if (nact_ && rdm1_.empty())
65   //  throw logic_error("If nact != 0, Reference::Reference wants to have RDMs.");
66 
67 }
68 
69 
Reference(shared_ptr<const Geometry> g,shared_ptr<const PTree> itree)70 Reference::Reference(shared_ptr<const Geometry> g, shared_ptr<const PTree> itree) : geom_(g), hcore_(make_shared<Hcore>(geom_)) {
71   // Note that other informations are not available...
72   // Then read molden
73   const string molden_file = itree->get<string>("molden_file", "");
74   assert(!molden_file.empty());
75   MoldenIn mfs(molden_file, geom_->spherical());
76   mfs.read();
77   if (mfs.has_mo()) {
78     auto c = make_shared<Coeff>(geom_);
79     auto e = make_shared<VectorB>(c->mdim());
80     auto o = make_shared<VectorB>(c->mdim());
81     mfs >> tie(c, g, e, o);
82     coeff_ = c;
83     eig_ = *e;
84     occup_ = *o;
85   }
86 }
87 
88 
rdm12(const int ist,const int jst,const bool recompute) const89 tuple<shared_ptr<const RDM<1>>,shared_ptr<const RDM<2>>> Reference::rdm12(const int ist, const int jst, const bool recompute) const {
90   shared_ptr<const RDM<1>> r1;
91   shared_ptr<const RDM<2>> r2;
92   if (!recompute && rdm1_->exist(ist, jst) && rdm2_->exist(ist, jst)) {
93     r1 = rdm1_->at(ist, jst);
94     r2 = rdm2_->at(ist, jst);
95   } else {
96     FCI_bare fci(ciwfn_);
97     fci.compute_rdm12(ist, jst);
98     r1 = fci.rdm1(ist, jst);
99     r2 = fci.rdm2(ist, jst);
100   }
101   return make_tuple(r1, r2);
102 }
103 
104 
rdm34(const int ist,const int jst) const105 tuple<shared_ptr<const RDM<3>>,shared_ptr<const RDM<4>>> Reference::rdm34(const int ist, const int jst) const {
106   FCI_bare fci(ciwfn_);
107   fci.compute_rdm12(ist, jst); // TODO stupid code
108   return fci.rdm34(ist, jst);
109 }
110 
111 
rdm1_mat(shared_ptr<const RDM<1>> active) const112 shared_ptr<Matrix> Reference::rdm1_mat(shared_ptr<const RDM<1>> active) const {
113   if (nact_)
114     return active->rdm1_mat(nclosed_);
115   else {
116     auto out = make_shared<Matrix>(nocc(), nocc());
117     for (int i = 0; i != nclosed_; ++i) out->element(i,i) = 2.0;
118     return out;
119   }
120 }
121 
rdm1_mat_tr(shared_ptr<const RDM<1>> active) const122 shared_ptr<Matrix> Reference::rdm1_mat_tr(shared_ptr<const RDM<1>> active) const {
123   if (nact_)
124     return active->rdm1_mat_tr(nclosed_);
125   else {
126     auto out = make_shared<Matrix>(nocc(), nocc());
127     for (int i = 0; i != nclosed_; ++i) out->element(i,i) = 0.0;	// unnecessary and silly: just to make it sure
128     return out;
129   }
130 }
131 
civectors() const132 shared_ptr<const Dvec> Reference::civectors() const {
133   return ciwfn_->civectors();
134 }
135 
136 
rdm1deriv(const int istate) const137 shared_ptr<Dvec> Reference::rdm1deriv(const int istate) const {
138   FCI_bare fci(ciwfn_);
139   return fci.rdm1deriv(istate);
140 }
141 
142 
rdm2deriv(const int istate) const143 shared_ptr<Dvec> Reference::rdm2deriv(const int istate) const {
144   FCI_bare fci(ciwfn_);
145   return fci.rdm2deriv(istate);
146 }
147 
148 
rdm2fderiv(const int istate,shared_ptr<const Matrix> fock,shared_ptr<const Matrix> dmat) const149 shared_ptr<Matrix> Reference::rdm2fderiv(const int istate, shared_ptr<const Matrix> fock, shared_ptr<const Matrix> dmat) const {
150   FCI_bare fci(ciwfn_);
151   return fci.rdm2fderiv(istate, fock, dmat);
152 }
153 
rdm2deriv_offset(const int istate,const size_t offset,const size_t size,shared_ptr<const Matrix> dmat) const154 shared_ptr<Matrix> Reference::rdm2deriv_offset(const int istate, const size_t offset, const size_t size, shared_ptr<const Matrix> dmat) const {
155   FCI_bare fci(ciwfn_);
156   return fci.rdm2deriv_offset(istate, offset, size, dmat);
157 }
158 
159 
160 tuple<shared_ptr<Matrix>,shared_ptr<Matrix>>
rdm3deriv(const int istate,shared_ptr<const Matrix> fock,const size_t offset,const size_t size,shared_ptr<const Matrix> dbra_in,shared_ptr<const Matrix> fock_ebra_in) const161 Reference::rdm3deriv(const int istate, shared_ptr<const Matrix> fock, const size_t offset, const size_t size, shared_ptr<const Matrix> dbra_in, shared_ptr<const Matrix> fock_ebra_in) const {
162   FCI_bare fci(ciwfn_);
163   return fci.rdm3deriv(istate, fock, offset, size, dbra_in, fock_ebra_in);
164 }
165 
166 
project_coeff(shared_ptr<const Geometry> geomin,const bool check_geom_change) const167 shared_ptr<Reference> Reference::project_coeff(shared_ptr<const Geometry> geomin, const bool check_geom_change) const {
168 
169   if (geomin->magnetism())
170     throw runtime_error("Projection from real to GIAO basis set is not implemented.   Use the GIAO code at zero-field.");
171 
172   bool moved = false;
173   bool newbasis = false;
174 
175   if (check_geom_change) {
176     auto j = geomin->atoms().begin();
177     for (auto& i : geom_->atoms()) {
178       moved |= i->distance(*j) > 1.0e-12;
179       newbasis |= i->basis() != (*j)->basis();
180       ++j;
181     }
182   } else {
183     newbasis = true;
184   }
185 
186   if (moved && newbasis)
187     throw runtime_error("changing geometry and basis set at the same time is not allowed");
188 
189   shared_ptr<Reference> out;
190 
191   if (newbasis) {
192     // project to a new basis
193     const Overlap snew(geomin);
194     Overlap snewinv = snew;
195     snewinv.inverse_symmetric();
196     MixedBasis<OverlapBatch> mixed(geom_, geomin);
197     auto coeff = coeff_->copy();
198     coeff->delocalize();
199     auto c = make_shared<Coeff>(snewinv * mixed * *coeff);
200 
201     // make coefficient orthogonal (under the overlap metric)
202     Matrix unit = *c % snew * *c;
203     unit.inverse_half();
204     *c *= unit;
205 
206     out = make_shared<Reference>(geomin, c, nclosed_, nact_, coeff_->mdim()-nclosed_-nact_, energy_);
207     if (coeffA_) {
208       assert(coeffB_);
209       auto coeffA = coeffA_->copy();
210       auto coeffB = coeffB_->copy();
211       coeffA->delocalize();
212       coeffB->delocalize();
213       out->coeffA_ = make_shared<Coeff>(snewinv * mixed * *coeffA * unit);
214       out->coeffB_ = make_shared<Coeff>(snewinv * mixed * *coeffB * unit);
215     }
216   } else {
217     Overlap snew(geomin);
218     Overlap sold(geom_);
219     snew.inverse_half();
220     sold.sqrt();
221     auto coeff = coeff_->copy();
222     coeff->delocalize();
223     auto c = make_shared<Coeff>(snew * sold * *coeff);
224 
225     out = make_shared<Reference>(geomin, c, nclosed_, nact_, coeff_->mdim()-nclosed_-nact_, energy_);
226     if (coeffA_) {
227       assert(coeffB_);
228       auto coeffA = coeffA_->copy();
229       auto coeffB = coeffB_->copy();
230       coeffA->delocalize();
231       coeffB->delocalize();
232       out->coeffA_ = make_shared<Coeff>(snew * sold * *coeffA);
233       out->coeffB_ = make_shared<Coeff>(snew * sold * *coeffB);
234     }
235   }
236 
237   return out;
238 }
239 
240 
set_eig(const VectorB & eig)241 void Reference::set_eig(const VectorB& eig) {
242   eig_ = eig;
243   mpi__->broadcast(eig_.data(), eig_.size(), 0);
244 }
245 
246 
set_eigB(const VectorB & eigB)247 void Reference::set_eigB(const VectorB& eigB) {
248   eigB_ = eigB;
249   mpi__->broadcast(eigB_.data(), eigB_.size(), 0);
250 }
251 
252 
set_occup(const VectorB & occup)253 void Reference::set_occup(const VectorB& occup) {
254   occup_ = occup;
255   mpi__->broadcast(occup_.data(), occup_.size(), 0);
256 }
257 
258 
set_coeff_AB(const shared_ptr<const Coeff> a,const shared_ptr<const Coeff> b)259 void Reference::set_coeff_AB(const shared_ptr<const Coeff> a, const shared_ptr<const Coeff> b) {
260   mpi__->broadcast(const_pointer_cast<Coeff>(a)->data(), a->size(), 0);
261   mpi__->broadcast(const_pointer_cast<Coeff>(b)->data(), b->size(), 0);
262   coeffA_ = a;
263   coeffB_ = b;
264 }
265 
266 // This function currently assumes it is being called on a Reference object with no defined active space
set_active(set<int> active_indices) const267 shared_ptr<Reference> Reference::set_active(set<int> active_indices) const {
268   if (!coeff_) throw logic_error("Reference::set_active is not implemented for relativistic cases");
269   const int naobasis = geom_->nbasis();
270   const int nmobasis = coeff_->mdim();
271 
272   int nactive = active_indices.size();
273 
274   int nclosed = nclosed_;
275   int nvirt = nmobasis - nclosed;
276   for (auto& iter : active_indices) {
277     if (iter < nclosed_) --nclosed;
278     else --nvirt;
279   }
280 
281   auto coeff = coeff_;
282   auto tmp_coeff = make_shared<Matrix>(naobasis, nmobasis);
283 
284   int iclosed = 0;
285   int iactive = nclosed;
286   int ivirt = nclosed + nactive;
287 
288   auto cp = [&tmp_coeff, &naobasis, &coeff] (const int i, int& pos) { copy_n(coeff->element_ptr(0,i), naobasis, tmp_coeff->element_ptr(0, pos)); ++pos; };
289 
290   for (int i = 0; i < nmobasis; ++i) {
291     if ( active_indices.find(i) != active_indices.end() ) cp(i, iactive);
292     else if ( i < nclosed_ ) cp(i, iclosed);
293     else cp(i, ivirt);
294   }
295 
296   return make_shared<Reference>(geom_, make_shared<const Coeff>(*tmp_coeff), nclosed, nactive, nvirt);
297 }
298 
299 // This function currently assumes it is being called on a Reference object with no defined active space
set_ractive(set<int> ras1,set<int> ras2,set<int> ras3) const300 shared_ptr<Reference> Reference::set_ractive(set<int> ras1, set<int> ras2, set<int> ras3) const {
301   if (!coeff_) throw logic_error("Reference::set_active is not implemented for relativistic cases");
302   const int naobasis = geom_->nbasis();
303   const int nmobasis = coeff_->mdim();
304 
305   const int nras1 = ras1.size();
306   const int nras2 = ras2.size();
307   const int nras3 = ras3.size();
308 
309   int nactive = nras1 + nras2 + nras3;
310 
311   set<int> total_active;
312   total_active.insert(ras1.begin(), ras1.end());
313   total_active.insert(ras2.begin(), ras2.end());
314   total_active.insert(ras3.begin(), ras3.end());
315   if (total_active.size() != nactive) throw runtime_error("Each orbital can occur in only one of RAS1, RAS2, or RAS3.");
316 
317   int nclosed = nclosed_;
318   int nvirt = nmobasis - nclosed;
319   for (auto& iter : total_active) {
320     if (iter < nclosed_) --nclosed;
321     else --nvirt;
322   }
323 
324   auto coeff = coeff_;
325   auto tmp_coeff = make_shared<Matrix>(naobasis, nmobasis);
326 
327   int iclosed = 0;
328   int iras1 = nclosed;
329   int iras2 = iras1 + nras1;
330   int iras3 = iras2 + nras2;
331   int ivirt = nclosed + nactive;
332 
333   auto cp = [&tmp_coeff, &naobasis, &coeff] (const int i, int& pos) { copy_n(coeff->element_ptr(0,i), naobasis, tmp_coeff->element_ptr(0, pos)); ++pos; };
334 
335   for (int i = 0; i < nmobasis; ++i) {
336     if ( total_active.find(i) != total_active.end() ) {
337       if (ras1.find(i) != ras1.end()) cp(i, iras1);
338       else if (ras2.find(i) != ras2.end()) cp(i, iras2);
339       else if (ras3.find(i) != ras3.end()) cp(i, iras3);
340       else assert(false);
341     }
342     else if (i < nclosed_) cp(i, iclosed);
343     else cp(i, ivirt);
344   }
345 
346   return make_shared<Reference>(geom_, make_shared<const Coeff>(*tmp_coeff), nclosed, nactive, nvirt);
347 }
348 
349 
extract_state(const vector<int> input,const bool update_rdms) const350 shared_ptr<Reference> Reference::extract_state(const vector<int> input, const bool update_rdms) const {
351   cout << " * Extracting CI coefficients from Reference object for the following states: ";
352   for (int i = 0; i != input.size(); ++i)
353     cout << input[i] << " ";
354   cout << endl;
355 
356   vector<double> newenergies(input.size());
357   for (int i = 0; i != input.size(); ++i)
358     newenergies[i] = energy_[input[i]];
359 
360   // Construct a CIWfn with only CI coefficients for the desired state
361   auto newciwfn = make_shared<CIWfn>(geom_, nclosed_, nact_, input.size(), newenergies,
362                                      ciwfn_->civectors()->extract_state(input), ciwfn_->det());
363 
364   // Use extract_average_rdm(...) to get desired RDMs and prepare output
365   shared_ptr<Reference> out;
366   if (update_rdms) {
367     shared_ptr<Reference> rdmref = extract_average_rdm(input);
368     out = make_shared<Reference>(geom_, coeff_, nclosed_, nact_, nvirt_, newenergies,
369                                  rdmref->rdm1(), rdmref->rdm2(), rdmref->rdm1_av(), rdmref->rdm2_av(), newciwfn);
370   } else {
371     out = make_shared<Reference>(geom_, coeff_, nclosed_, nact_, nvirt_, newenergies,
372                                  rdm1(), rdm2(), rdm1_av(), rdm2_av(), newciwfn);
373   }
374   return out;
375 }
376 
377 
378 // TODO Cleanup or remove?  Body is mostly the same as FCI_base::compute_rdm12()
extract_average_rdm(const vector<int> rdm_state) const379 shared_ptr<Reference> Reference::extract_average_rdm(const vector<int> rdm_state) const {
380   if (rdm_state.size() == 0 || rdm_state.size() > nstate())
381     throw runtime_error("Trying to obtain a state-averaged RDM over some invalid number of states.");
382 
383   cout << " * Extracting RDMs for ";
384   cout << (rdm_state.size() > 1 ? "the average of the following states: " : "the following state: ");
385   for (int i = 0; i != rdm_state.size(); ++i)
386     cout << rdm_state[i] << " ";
387   cout << endl;
388 
389   // for one-body RDM
390   auto rdm1 = make_shared<VecRDM<1>>();
391   auto rdm2 = make_shared<VecRDM<2>>();
392   auto rdm1_av = make_shared<RDM<1>>(nact_);
393   auto rdm2_av = make_shared<RDM<2>>(nact_);
394 
395   for (int index = 0; index != rdm_state.size(); ++index) {
396     const int istate = rdm_state[index];
397     // one and two body RDMs
398     rdm1->emplace(index, rdm1_->at(istate)->copy());
399     rdm2->emplace(index, rdm2_->at(istate)->copy());
400   }
401 
402   if (rdm_state.size() > 1) {
403     for (int index = 0; index != rdm_state.size(); ++index) {
404       const int istate = rdm_state[index];
405       const double weight = 1.0/static_cast<double>(rdm_state.size());
406       rdm1_av->ax_plus_y(weight, rdm1_->at(istate));
407       rdm2_av->ax_plus_y(weight, rdm2_->at(istate));
408     }
409   } else {
410     rdm1_av = rdm1_->at(rdm_state[0])->copy();
411     rdm2_av = rdm2_->at(rdm_state[0])->copy();
412   }
413 
414   return make_shared<Reference>(geom_, coeff_, nclosed_, nact_, nvirt_, energy_, rdm1, rdm2, rdm1_av, rdm2_av, ciwfn_);
415 }
416 
417 
418