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