1 //
2 // BAGEL - Brilliantly Advanced General Electronic Structure Library
3 // Filename: asd/dimer/dimer_linked.cc
4 // Copyright (C) 2015 Toru Shiozaki
5 //
6 // Author: Inkoo Kim <inkoo.kim@northwestern.edu>
7 // Maintainer: NU theory
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/asd/dimer/dimer.h>
26 #include <src/scf/hf/fock.h>
27 #include <src/wfn/localization.h>
28 #include <src/util/io/moldenout.h>
29
30 using namespace std;
31 using namespace bagel;
32
set_active(shared_ptr<const PTree> idata)33 void Dimer::set_active(shared_ptr<const PTree> idata) {
34 auto isp = idata->get_child_optional("dimer_active");
35 set<int> it;
36 if (isp) for (auto& s : *isp) { it.insert(lexical_cast<int>(s->data())-1); }
37 if (it.empty())
38 throw runtime_error("Active space of the dimer MUST be specified in dimer_active.");
39
40 set<int> Alist, Blist;
41
42 { //sort out mixed active indices into A or B fragment (defined by region), if an index does not belong to any of fragments, throw error..
43 vector<pair<int, int>> bounds;
44 vector<int> sizes = idata->get_vector<int>("region_sizes"); // [A, B, the rest]
45 int nbasis = 0;
46 int natoms = 0;
47 //set bounds : first & last basis function indices for the given region
48 for (int& region : sizes) {
49 const int atomstart = natoms;
50 const int basisstart = nbasis;
51 for (int atom = atomstart; atom < atomstart + region; ++atom)
52 nbasis += sgeom_->atoms()[atom]->nbasis();
53
54 natoms += region;
55 if (basisstart != nbasis)
56 bounds.emplace_back(basisstart, nbasis);
57 }
58 if (bounds.size() != 3)
59 throw logic_error("Only 3 regions should be defined in order of A, B, and the rest");
60 if (natoms != count_if(sgeom_->atoms().begin(), sgeom_->atoms().end(), [](const shared_ptr<const Atom> a){return !a->dummy();}))
61 throw logic_error("All atoms must be assigned to regions");
62 //scan it set
63 cout << " o Assigning dimer active (localized & projected) orbitals to monomers A and B" << endl;
64 shared_ptr<Matrix> coeff = isolated_refs_.first->coeff()->copy(); //"projected coeff" on isolated_refs (so far, first == second respresenting dimer hf)
65 for (auto& imo : it) {
66 const double sum_A = blas::dot_product(coeff->element_ptr(bounds[0].first, imo), bounds[0].second - bounds[0].first, coeff->element_ptr(bounds[0].first, imo));
67 const double sum_B = blas::dot_product(coeff->element_ptr(bounds[1].first, imo), bounds[1].second - bounds[1].first, coeff->element_ptr(bounds[1].first, imo));
68 const double sum_rest = blas::dot_product(coeff->element_ptr(bounds[2].first, imo), bounds[2].second - bounds[2].first, coeff->element_ptr(bounds[2].first, imo));
69
70 if (sum_A > sum_B && sum_A > sum_rest)
71 //cout << " - active orbital(" << imo << ") is assigned to monomer A." << endl;
72 //cout << " A(" << setw(6) << setprecision(3) << sum_A << "), B(" << setw(6) << setprecision(3) << sum_B << "), the rest(" << setw(6) << setprecision(3) << sum_rest << ")" << endl;
73 Alist.insert(imo);
74 else if (sum_B > sum_A && sum_B > sum_rest)
75 Blist.insert(imo);
76 else
77 throw runtime_error("Wrong choice of active orbitals. The orbital(" + to_string(imo) + ") does not belong to any of monomers.");
78 }
79 cout << " - orbitals are assigned as : " << Alist.size() << "(A), " << Blist.size() << "(B)." << endl;
80 }
81
82 // Make new References, with large basis sets, but with projected coeffs (MO space up to smaller basis sets); active orbitals are placed after closed orbitals
83 active_refs_ = {isolated_refs_.first->set_active(Alist), isolated_refs_.second->set_active(Blist)};
84
85 if (print_orbital_ && mpi__->rank() == 0) {
86 {
87 MoldenOut mfs("dimer_orbital_A_projected.molden");
88 mfs << active_refs_.first->geom();
89 mfs << active_refs_.first;
90 }
91 {
92 MoldenOut mfs("dimer_orbital_B_projected.molden");
93 mfs << active_refs_.second->geom();
94 mfs << active_refs_.second;
95 }
96 }
97
98 // Update Dimer info
99 const int nclosedA = active_refs_.first->nclosed();
100 const int nclosedB = active_refs_.second->nclosed(); //these shares common closed indices, but with different embedded active(closed part) orbtals
101 const int nactA = active_refs_.first->nact();
102 const int nactB = active_refs_.second->nact();
103 const int nact = nactA + nactB;
104 const int nactvirtA = isolated_refs_.first->nvirt() - active_refs_.first->nvirt();
105 const int nactvirtB = isolated_refs_.second->nvirt() - active_refs_.second->nvirt(); // Number of active orbitals in virtual HF subspace
106 const int dimerbasis = sgeom_->nbasis();
107 assert(dimerbasis == geoms_.first->nbasis());
108 assert(dimerbasis == geoms_.second->nbasis());
109 //so far, sref_ is dimer RHF reference
110 const int nclosed_HF = sref_->nclosed();
111 const int nvirt_HF = sref_->nvirt();
112 assert(dimerbasis == nclosed_HF + nvirt_HF);
113 assert(sref_->nact() == 0);
114 const int nclosed = nclosed_HF - (nclosed_HF - nclosedA) - (nclosed_HF - nclosedB);
115
116 //prepare reference active orbitals
117 auto control = make_shared<Matrix>(dimerbasis,nact);
118 control->copy_block(0,0, dimerbasis,nactA, active_refs_.first->coeff()->get_submatrix(0,nclosedA,dimerbasis,nactA));
119 control->copy_block(0,nactA, dimerbasis,nactB, active_refs_.second->coeff()->get_submatrix(0,nclosedB,dimerbasis,nactB));
120
121 //pick active orbitals based on reference and reorder to [closed,A,B,virt]
122 cout << endl << " o Picking up active orbitals using projected orbitals" << endl;
123 shared_ptr<Matrix> out_coeff = overlap_selection(control, sref_->coeff());
124 nvirt_ = {nvirt_HF - nactvirtA, nvirt_HF - nactvirtB};
125 sref_ = make_shared<Reference>(sgeom_, make_shared<Coeff>(*out_coeff), nclosed, nact, nvirt_HF - nactvirtA - nactvirtB);
126 }
127
128
129 //semi-canonicalise only in the active space (thus, it is still the same quality as the localized orbitals)
130 //this will be used as reference to find the actual semi-canonical orbital
form_reference_active_coeff() const131 shared_ptr<Matrix> Dimer::form_reference_active_coeff() const {
132 const int nactA = active_refs_.first->nact();
133 const int nactB = active_refs_.second->nact();
134 const int nact = nactA + nactB;
135 const int nactcloA = isolated_refs_.first->nclosed() - active_refs_.first->nclosed();
136 const int nactcloB = isolated_refs_.second->nclosed() - active_refs_.second->nclosed();
137 const int nclosed = sref_->nclosed();
138 const int dimerbasis = sgeom_->nbasis();
139
140 auto active_semi_coeff = make_shared<Matrix>(dimerbasis,nact);
141
142 //closed
143 auto ccoeff = make_shared<Matrix>(dimerbasis, nclosed+nactcloA+nactcloB);
144 ccoeff->copy_block(0,0, dimerbasis,nclosed, sref_->coeff()->get_submatrix(0,0, dimerbasis,nclosed)); //shared closed
145 ccoeff->copy_block(0,nclosed, dimerbasis,nactcloA, sref_->coeff()->get_submatrix(0,nclosed, dimerbasis,nactcloA)); //embed activeB
146 ccoeff->copy_block(0,nclosed+nactcloA, dimerbasis,nactcloB, sref_->coeff()->get_submatrix(0,nclosed+nactA, dimerbasis,nactcloB)); //embed activeB
147
148 //AO Fock
149 shared_ptr<const Matrix> ofockao = make_shared<Fock<1>>(sgeom_, sref_->hcore(), nullptr, ccoeff, /*store*/false, /*rhf*/true);
150
151 {//Monomer A
152 auto acoeff = sref_->coeff()->slice_copy(nclosed, nclosed+nactA);
153 // MO Fock
154 VectorB eigs(nactA);
155 auto fockact = make_shared<Matrix>(*acoeff % *ofockao * *acoeff);
156 fockact->diagonalize(eigs);
157 cout << endl << " o Eigenvlues of A orbitals :" << endl;
158 for (int i = 0; i < nactA; ++i) cout << setw(12) << setprecision(6) << eigs[i];
159 cout << endl << endl;
160 *acoeff *= *fockact;
161
162 size_t act_position = 0; //for A
163 for (int i = 0; i < nactA; ++i)
164 copy_n(acoeff->element_ptr(0, i), dimerbasis, active_semi_coeff->element_ptr(0,act_position++));
165 }
166
167 {//Monomer B
168 auto acoeff = sref_->coeff()->slice_copy(nclosed+nactA, nclosed+nact);
169 // MO Fock
170 VectorB eigs(nactB);
171 auto fockact = make_shared<Matrix>(*acoeff % *ofockao * *acoeff);
172 fockact->diagonalize(eigs);
173 cout << " o Eigenvlues of B orbitals :" << endl;
174 for (int i = 0; i < nactB; ++i) cout << setw(12) << setprecision(6) << eigs[i];
175 cout << endl << endl;
176 *acoeff *= *fockact;
177
178 size_t act_position = nactA; //for B
179 for (int i = 0; i < nactB; ++i)
180 copy_n(acoeff->element_ptr(0, i), dimerbasis, active_semi_coeff->element_ptr(0,act_position++));
181 }
182
183 return active_semi_coeff;
184 }
185
186
form_semi_canonical_coeff(shared_ptr<const PTree> idata) const187 shared_ptr<Matrix> Dimer::form_semi_canonical_coeff(shared_ptr<const PTree> idata) const {
188 const int nactA = active_refs_.first->nact();
189 const int nactB = active_refs_.second->nact();
190 const int nact = nactA + nactB;
191 const int nactcloA = isolated_refs_.first->nclosed() - active_refs_.first->nclosed();
192 const int nactcloB = isolated_refs_.second->nclosed() - active_refs_.second->nclosed();
193 const int nactvirtA = isolated_refs_.first->nvirt() - active_refs_.first->nvirt();
194 const int nactvirtB = isolated_refs_.second->nvirt() - active_refs_.second->nvirt(); // Number of active orbitals in virtual HF subspace
195 const int nclosed = sref_->nclosed();
196 const int nclosed_HF = sref_->nclosed() + nactcloA + nactcloB;
197 const int nvirt_HF = sref_->nvirt();
198 const int dimerbasis = sgeom_->nbasis();
199
200 //completely semi-canonicalized based on fragments. [ closed | virtual]
201 shared_ptr<Matrix> semi_coeff = sref_->coeff()->copy();
202
203 //sort closed & virtual into A, B and bridge sites
204 vector<pair<int, int>> bounds;
205 set<int> ambiguous_list;
206 //sort according to region and put into Alist & Blist
207 vector<int> sizes = idata->get_vector<int>("region_sizes");
208 int nbasis = 0;
209 int natoms = 0;
210 for (int& region : sizes) {
211 const int atomstart = natoms;
212 const int basisstart = nbasis;
213 for (int atom = atomstart; atom < atomstart + region; ++atom)
214 nbasis += sgeom_->atoms()[atom]->nbasis();
215
216 natoms += region;
217 if (basisstart != nbasis)
218 bounds.emplace_back(basisstart, nbasis);
219 }
220 if (bounds.size() != 3)
221 throw logic_error("Only 3 regions should be defined in order of A, B, and the rest");
222 if (natoms != count_if(sgeom_->atoms().begin(), sgeom_->atoms().end(), [](const shared_ptr<const Atom> a){return !a->dummy();}))
223 throw logic_error("All atoms must be assigned to regions");
224 //scan it set
225 set<int> closed_Alist, closed_Blist, closed_Clist;
226 set<int> virtual_Alist, virtual_Blist, virtual_Clist;
227 shared_ptr<Matrix> coeff = semi_coeff->copy();
228 {//closed
229 set<int> cset; // (0:nclosed)
230 for (int i = 0; i != nclosed; ++i) cset.insert(i);
231 cout << " o Assigning dimer closed (localized) orbitals to monomers A and B, and bridge" << endl;
232 for (auto& imo : cset) {
233 const double sum_A = blas::dot_product(coeff->element_ptr(bounds[0].first, imo), bounds[0].second - bounds[0].first, coeff->element_ptr(bounds[0].first, imo));
234 const double sum_B = blas::dot_product(coeff->element_ptr(bounds[1].first, imo), bounds[1].second - bounds[1].first, coeff->element_ptr(bounds[1].first, imo));
235 const double sum_rest = blas::dot_product(coeff->element_ptr(bounds[2].first, imo), bounds[2].second - bounds[2].first, coeff->element_ptr(bounds[2].first, imo));
236 if (sum_A > sum_B && sum_A > sum_rest)
237 closed_Alist.insert(imo);
238 //cout << " - closed orbital(" << imo << ") is assigned to monomer A." << endl;
239 //cout << " A(" << setw(6) << setprecision(3) << sum_A << "), B(" << setw(6) << setprecision(3) << sum_B << "), the rest(" << setw(6) << setprecision(3) << sum_rest << ")" << endl;
240 else if (sum_B > sum_A && sum_B > sum_rest)
241 closed_Blist.insert(imo);
242 else
243 closed_Clist.insert(imo);
244 }
245 cout << " - orbitals are assigned as : " << closed_Alist.size() << "(A), " << closed_Blist.size() << "(B), " << closed_Clist.size() << "(bridge) " << endl << endl;
246 }
247 {//virtual
248 set<int> vset; // (nclosed+nact:nbasis)
249 for (int i = nclosed+nact; i != nbasis; ++i) vset.insert(i);
250 cout << " o Assigning dimer virtual (localized) orbitals to monomers A and B, and bridge" << endl;
251 for (auto& imo : vset) {
252 const double sum_A = blas::dot_product(coeff->element_ptr(bounds[0].first, imo), bounds[0].second - bounds[0].first, coeff->element_ptr(bounds[0].first, imo));
253 const double sum_B = blas::dot_product(coeff->element_ptr(bounds[1].first, imo), bounds[1].second - bounds[1].first, coeff->element_ptr(bounds[1].first, imo));
254 const double sum_rest = blas::dot_product(coeff->element_ptr(bounds[2].first, imo), bounds[2].second - bounds[2].first, coeff->element_ptr(bounds[2].first, imo));
255 if (sum_A > sum_B && sum_A > sum_rest)
256 virtual_Alist.insert(imo);
257 //cout << " - virtual orbital(" << imo << ") is assigned to monomer A." << endl;
258 else if (sum_B > sum_A && sum_B > sum_rest)
259 virtual_Blist.insert(imo);
260 else
261 virtual_Clist.insert(imo);
262 }
263 cout << " - orbitals are assigned as : " << virtual_Alist.size() << "(A), " << virtual_Blist.size() << "(B), " << virtual_Clist.size() << "(bridge) " << endl;
264 }
265
266 auto ccoeff_A = make_shared<Matrix>(dimerbasis,nclosed_HF);
267 auto ccoeff_B = ccoeff_A->clone();
268 auto ccoeff_C = ccoeff_A->clone();
269
270 auto vcoeff_A = make_shared<Matrix>(dimerbasis,nvirt_HF);
271 auto vcoeff_B = vcoeff_A->clone();
272 auto vcoeff_C = vcoeff_A->clone();
273
274 //Put orbitals to appropriate coefficient matrices
275 //closed + active
276 {//Monomer A
277 size_t pos = 0;
278 for (auto& i : closed_Alist)
279 copy_n(coeff->element_ptr(0, i), dimerbasis, ccoeff_A->element_ptr(0,pos++));
280 for (int i = 0; i != nactcloA; ++i)
281 copy_n(coeff->element_ptr(0, nclosed+i), dimerbasis, ccoeff_A->element_ptr(0,pos++));
282 }
283 {//Monomer B
284 size_t pos = 0;
285 for (auto& i : closed_Blist)
286 copy_n(coeff->element_ptr(0, i), dimerbasis, ccoeff_B->element_ptr(0,pos++));
287 for (int i = 0; i != nactcloB; ++i)
288 copy_n(coeff->element_ptr(0, nclosed+nactA+i), dimerbasis, ccoeff_B->element_ptr(0,pos++));
289 }
290 {//Bridge
291 size_t pos = 0;
292 for (auto& i : closed_Clist)
293 copy_n(coeff->element_ptr(0, i), dimerbasis, ccoeff_C->element_ptr(0,pos++));
294 }
295 //virtual + active
296 {//Monomer A
297 size_t pos = 0;
298 for (auto& i : virtual_Alist)
299 copy_n(coeff->element_ptr(0, i), dimerbasis, vcoeff_A->element_ptr(0,pos++));
300 for (int i = 0; i != nactvirtA; ++i)
301 copy_n(coeff->element_ptr(0, nclosed+nactcloA+i), dimerbasis, vcoeff_A->element_ptr(0,pos++));
302 }
303 {//Monomer B
304 size_t pos = 0;
305 for (auto& i : virtual_Blist)
306 copy_n(coeff->element_ptr(0, i), dimerbasis, vcoeff_B->element_ptr(0,pos++));
307 for (int i = 0; i != nactvirtB; ++i)
308 copy_n(coeff->element_ptr(0, nclosed+nactA+nactcloB+i), dimerbasis, vcoeff_B->element_ptr(0,pos++));
309 }
310 {//Bridge
311 size_t pos = 0;
312 for (auto& i : virtual_Clist)
313 copy_n(coeff->element_ptr(0, i), dimerbasis, vcoeff_C->element_ptr(0,pos++));
314 }
315
316 //form AO Fock
317 shared_ptr<const Matrix> ofockao;
318 {
319 assert(nclosed_HF == nclosed + nactcloA + nactcloB);
320 auto ccoeff = make_shared<Matrix>(dimerbasis, nclosed_HF);
321 ccoeff->copy_block(0,0, dimerbasis,nclosed, *coeff->get_submatrix(0,0, dimerbasis,nclosed)); //shared closed
322 ccoeff->copy_block(0,nclosed, dimerbasis,nactcloA, *coeff->get_submatrix(0,nclosed, dimerbasis,nactcloA)); //embed activeA
323 ccoeff->copy_block(0,nclosed+nactcloA, dimerbasis,nactcloB, *coeff->get_submatrix(0,nclosed+nactA, dimerbasis,nactcloB)); //embed activeB
324 ofockao = make_shared<Fock<1>>(sgeom_, sref_->hcore(), nullptr, ccoeff, /*store*/false, /*rhf*/true);
325 }
326
327 //form MO fock
328 size_t pos = 0; // order=[closed(A,B,C)|virtual(A,B,C)]
329 { //closed A
330 const int dim = closed_Alist.size()+nactcloA;
331 VectorB eigs(dim);
332 auto mocoeff = ccoeff_A->slice_copy(0,dim);
333 auto fock = make_shared<Matrix>(*mocoeff % *ofockao * *mocoeff);
334 fock->diagonalize(eigs);
335 *mocoeff *= *fock; //transformed
336 for (int i = 0; i < dim; ++i)
337 copy_n(mocoeff->element_ptr(0, i), dimerbasis, semi_coeff->element_ptr(0,pos++)); //put back to right place of output matrix
338 }
339 { //closed B
340 const int dim = closed_Blist.size()+nactcloB;
341 VectorB eigs(dim);
342 auto mocoeff = ccoeff_B->slice_copy(0,dim);
343 auto fock = make_shared<Matrix>(*mocoeff % *ofockao * *mocoeff);
344 fock->diagonalize(eigs);
345 *mocoeff *= *fock; //transformed
346 for (int i = 0; i < dim; ++i)
347 copy_n(mocoeff->element_ptr(0, i), dimerbasis, semi_coeff->element_ptr(0,pos++));
348 }
349 if (!closed_Clist.empty()) { //closed C
350 const int dim = closed_Clist.size();
351 VectorB eigs(dim);
352 auto mocoeff = ccoeff_C->slice_copy(0,dim);
353 auto fock = make_shared<Matrix>(*mocoeff % *ofockao * *mocoeff);
354 fock->diagonalize(eigs);
355 *mocoeff *= *fock; //transformed
356 for (int i = 0; i < dim; ++i)
357 copy_n(mocoeff->element_ptr(0, i), dimerbasis, semi_coeff->element_ptr(0,pos++));
358 }
359 { //virtual A
360 const int dim = virtual_Alist.size()+nactvirtA;
361 VectorB eigs(dim);
362 auto mocoeff = vcoeff_A->slice_copy(0,dim);
363 auto fock = make_shared<Matrix>(*mocoeff % *ofockao * *mocoeff);
364 fock->diagonalize(eigs);
365 *mocoeff *= *fock; //transformed
366 for (int i = 0; i < dim; ++i)
367 copy_n(mocoeff->element_ptr(0, i), dimerbasis, semi_coeff->element_ptr(0,pos++));
368 }
369 { //virtual B
370 const int dim = virtual_Blist.size()+nactvirtB;
371 VectorB eigs(dim);
372 auto mocoeff = vcoeff_B->slice_copy(0,dim);
373 auto fock = make_shared<Matrix>(*mocoeff % *ofockao * *mocoeff);
374 fock->diagonalize(eigs);
375 *mocoeff *= *fock; //transformed
376 for (int i = 0; i < dim; ++i)
377 copy_n(mocoeff->element_ptr(0, i), dimerbasis, semi_coeff->element_ptr(0,pos++));
378 }
379 if (!virtual_Clist.empty()) { //virtual C
380 const int dim = virtual_Clist.size();
381 VectorB eigs(dim);
382 auto mocoeff = vcoeff_C->slice_copy(0,dim);
383 auto fock = make_shared<Matrix>(*mocoeff % *ofockao * *mocoeff);
384 fock->diagonalize(eigs);
385 *mocoeff *= *fock; //transformed
386 for (int i = 0; i < dim; ++i)
387 copy_n(mocoeff->element_ptr(0, i), dimerbasis, semi_coeff->element_ptr(0,pos++));
388 }
389
390 return semi_coeff;
391 }
392
393
overlap_selection(shared_ptr<const Matrix> control,shared_ptr<const Matrix> treatment) const394 shared_ptr<Matrix> Dimer::overlap_selection(shared_ptr<const Matrix> control, shared_ptr<const Matrix> treatment) const {
395 const int nactA = active_refs_.first->nact();
396 const int nactB = active_refs_.second->nact();
397 const int nact = nactA + nactB;
398
399 const int nactcloA = isolated_refs_.first->nclosed() - active_refs_.first->nclosed();
400 const int nactcloB = isolated_refs_.second->nclosed() - active_refs_.second->nclosed();
401 const int nactvirtA = isolated_refs_.first->nvirt() - active_refs_.first->nvirt();
402 const int nactvirtB = isolated_refs_.second->nvirt() - active_refs_.second->nvirt(); // Number of active orbitals in virtual HF subspace
403 assert(nactA == nactcloA + nactvirtA);
404 assert(nactB == nactcloB + nactvirtB);
405
406 const int dimerbasis = sgeom_->nbasis();
407 const int nclosed_HF = isolated_refs_.first->nclosed();
408 const int nclosed = nclosed_HF - nactcloA - nactcloB;
409
410 vector<tuple<shared_ptr<const Matrix>, pair<int, int>, int, string, bool>> ovl_info;
411
412 auto activeA = make_shared<Matrix>(dimerbasis, nactA);
413 activeA->copy_block(0, 0, dimerbasis, nactA, control->get_submatrix(0,0, dimerbasis, nactA));
414 ovl_info.emplace_back(activeA, make_pair(0, nclosed_HF), nactcloA, "A", true); //A active in closed subspace
415 ovl_info.emplace_back(activeA, make_pair(nclosed_HF, dimerbasis), nactvirtA, "A", false); //A active in virtual subspace
416
417 auto activeB = make_shared<Matrix>(dimerbasis, nactB);
418 activeB->copy_block(0, 0, dimerbasis, nactB, control->get_submatrix(0,nactA, dimerbasis, nactB));
419 ovl_info.emplace_back(activeB, make_pair(0, nclosed_HF), nactcloB, "B", true);
420 ovl_info.emplace_back(activeB, make_pair(nclosed_HF, dimerbasis), nactvirtB, "B", false);
421
422 const Overlap S(sgeom_);
423
424 shared_ptr<Matrix> out_coeff = treatment->clone();
425 size_t active_position = nclosed;
426
427 set<int> mask; //records what orbitals have been copied to sref_ coeff, to be used to copy back the common closed & virtual orbitals
428 for (int i = 0; i != out_coeff->mdim(); ++i) mask.insert(i);
429
430 //this fills the active (A and B) in order of closed A - virtual A - closed B - virtual B
431 for (auto& subset : ovl_info) {
432 const Matrix& active = *get<0>(subset);
433 const pair<int, int> bounds = get<1>(subset);
434 const int norb = get<2>(subset);
435 const string set_name = get<3>(subset);
436 const bool closed = get<4>(subset);
437
438 shared_ptr<const Matrix> subcoeff = treatment->slice_copy(bounds.first, bounds.second);
439
440 const Matrix overlaps(active % S * *subcoeff);
441
442 multimap<double, int> norms;
443
444 for(int i = 0; i < overlaps.mdim(); ++i) {
445 const double norm = blas::dot_product(overlaps.element_ptr(0, i), overlaps.ndim(), overlaps.element_ptr(0, i));
446 norms.emplace(norm, i);
447 }
448
449 cout << endl << " o Forming dimer's active orbitals arising from " << (closed ? "closed " : "virtual ") << set_name
450 << " orbitals. Threshold for inclusion in cadidate space: " << setw(6) << setprecision(3) << active_thresh_ << endl;
451
452 vector<int> active_list;
453 double max_overlap, min_overlap;
454 {
455 auto end = norms.rbegin(); advance(end, norb);
456 end = find_if(end, norms.rend(), [this] (const pair<const double, int>& p) { return p.first < active_thresh_; });
457 for_each(norms.rbegin(), end, [&active_list] (const pair<const double, int>& p) { active_list.emplace_back(p.second); });
458 auto mnmx = minmax_element(norms.rbegin(), end);
459 tie(min_overlap, max_overlap) = make_tuple(mnmx.first->first, mnmx.second->first);
460 }
461
462 const int active_size = active_list.size();
463 cout << " - size of candidate space: " << active_size << endl;
464 cout << " - largest overlap with monomer space: " << max_overlap << ", smallest: " << min_overlap << endl;
465
466 if (active_size != norb) {
467 throw runtime_error("Try adjust active_thresh.");
468 } else {
469 const set<int> active_set(active_list.begin(), active_list.end());
470 for (size_t i = 0; i < subcoeff->mdim(); ++i) {
471 if (active_set.count(i)) {
472 const int imo = bounds.first + i;
473 assert(mask.count(imo));
474 mask.erase(imo);
475 copy_n(subcoeff->element_ptr(0, i), dimerbasis, out_coeff->element_ptr(0, active_position++)); //fill active columns, order=[cA,vA,cB,vB]
476 }
477 }
478 }
479 }
480
481 //fill common closed and virtual subspace
482 size_t closed_position = 0;
483 for (int i = 0; i < nclosed_HF; ++i)
484 if (mask.count(i))
485 copy_n(treatment->element_ptr(0, i), dimerbasis, out_coeff->element_ptr(0, closed_position++)); //fill closed columns
486
487 size_t virt_position = nclosed + nact;
488 for (int i = nclosed_HF; i < dimerbasis; ++i)
489 if (mask.count(i))
490 copy_n(treatment->element_ptr(0, i), dimerbasis, out_coeff->element_ptr(0, virt_position++)); //fill virtual columns
491
492 return out_coeff;
493 }
494
495
reduce_active(shared_ptr<const PTree> idata)496 void Dimer::reduce_active(shared_ptr<const PTree> idata) {
497 const int nactA = active_refs_.first->nact();
498 const int nactB = active_refs_.second->nact();
499 const int nact = nactA + nactB;
500 const int nactvirtA = isolated_refs_.first->nvirt() - active_refs_.first->nvirt();
501 const int nactvirtB = isolated_refs_.second->nvirt() - active_refs_.second->nvirt(); // Number of active orbitals in virtual HF subspace
502 assert(nactA == isolated_refs_.first->nclosed() - active_refs_.first->nclosed() + nactvirtA);
503 assert(nactB == isolated_refs_.second->nclosed() - active_refs_.second->nclosed() + nactvirtB);
504 const int dimerbasis = sgeom_->nbasis();
505 //sref has been activated; nclosed, nact, nvirt are defined
506 const int nclosed = sref_->nclosed();
507 const int nvirt_HF = sref_->nvirt() + nactvirtA + nactvirtB;
508 assert(dimerbasis == nclosed + isolated_refs_.first->nclosed()-active_refs_.first->nclosed() + isolated_refs_.second->nclosed()-active_refs_.second->nclosed() + nvirt_HF);
509
510 auto deact = idata->get_array<int,4>("reduction", {0,0,0,0});
511 const int cA = deact[0];
512 const int vA = deact[1];
513 const int cB = deact[2];
514 const int vB = deact[3];
515 cout << endl << " o Active space is truncated." << endl;
516 cout << " - monomer A : " << cA << " closed and " << vA << " virtual orbitals are created from active space." << endl;
517 cout << " - monomer B : " << cB << " closed and " << vB << " virtual orbitals are created from active space." << endl;
518 shared_ptr<Matrix> acoeff = sref_->coeff()->slice_copy(nclosed, nclosed+nact); // [nactA nactB]
519 auto closedAB = make_shared<Matrix>(dimerbasis,cA+cB);
520 auto activeAB = make_shared<Matrix>(dimerbasis,nact-cA-cB-vA-vB);
521 auto virtualAB = make_shared<Matrix>(dimerbasis,vA+vB);
522
523 //reduced to closed
524 closedAB->copy_block(0,0, dimerbasis,cA, acoeff->get_submatrix(0,0, dimerbasis,cA));
525 closedAB->copy_block(0,cA, dimerbasis,cB, acoeff->get_submatrix(0,nactA, dimerbasis,cB));
526 //reduced to virtual
527 virtualAB->copy_block(0,0, dimerbasis,vA, acoeff->get_submatrix(0,nactA-vA, dimerbasis,vA));
528 virtualAB->copy_block(0,vA, dimerbasis,vB, acoeff->get_submatrix(0,nact-vB, dimerbasis,vB));
529 //remains active
530 activeAB->copy_block(0,0, dimerbasis,nactA-cA-vA, acoeff->get_submatrix(0,cA, dimerbasis,nactA-cA-vA));
531 activeAB->copy_block(0,nactA-cA-vA, dimerbasis,nactB-cB-vB, acoeff->get_submatrix(0,nactA+cB, dimerbasis,nactB-cB-vB));
532
533 shared_ptr<Matrix> outcoeff = sref_->coeff()->copy();
534 outcoeff->copy_block(0,nclosed, dimerbasis,cA+cB, closedAB);
535 outcoeff->copy_block(0,nclosed+cA+cB, dimerbasis,nact-cA-cB-vA-vB, activeAB);
536 outcoeff->copy_block(0,nclosed+nact-vA-vB, dimerbasis,vA+vB, virtualAB);
537
538 //update active refs
539 active_refs_ = { make_shared<Reference>(active_refs_.first->geom(), active_refs_.first->coeff(), active_refs_.first->nclosed()+cA, active_refs_.first->nact()-cA-vA, active_refs_.first->nvirt()+vA),
540 make_shared<Reference>(active_refs_.second->geom(), active_refs_.second->coeff(), active_refs_.first->nclosed()+cB, active_refs_.first->nact()-cB-vB, active_refs_.first->nvirt()+vB)};
541
542 //synchronization
543 outcoeff->broadcast();
544
545 //semi canonical coeff
546 sref_ = make_shared<Reference>(sgeom_, make_shared<Coeff>(*outcoeff), nclosed+cA+cB, nact-cA-cB-vA-vB, nvirt_HF - nactvirtA - nactvirtB + vA+vB);
547
548 }
549