source: ThirdParty/mpqc_open/src/lib/chemistry/qc/mbptr12/ri_basis.cc

Candidate_v1.6.1
Last change on this file was 860145, checked in by Frederik Heber <heber@…>, 8 years ago

Merge commit '0b990dfaa8c6007a996d030163a25f7f5fc8a7e7' as 'ThirdParty/mpqc_open'

  • Property mode set to 100644
File size: 19.0 KB
Line 
1//
2// ri_basis.cc
3//
4// Copyright (C) 2004 Edward Valeev
5//
6// Author: Edward Valeev <edward.valeev@chemistry.gatech.edu>
7// Maintainer: EV
8//
9// This file is part of the SC Toolkit.
10//
11// The SC Toolkit is free software; you can redistribute it and/or modify
12// it under the terms of the GNU Library General Public License as published by
13// the Free Software Foundation; either version 2, or (at your option)
14// any later version.
15//
16// The SC Toolkit 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 Library General Public License for more details.
20//
21// You should have received a copy of the GNU Library General Public License
22// along with the SC Toolkit; see the file COPYING.LIB. If not, write to
23// the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
24//
25// The U.S. Government is granted a limited license as per AL 91-7.
26//
27
28#ifdef __GNUC__
29#pragma implementation
30#endif
31
32#include <stdexcept>
33#include <sstream>
34
35#include <util/misc/formio.h>
36#include <util/misc/exenv.h>
37#include <chemistry/qc/basis/basis.h>
38#include <chemistry/qc/basis/symmint.h>
39#include <chemistry/qc/mbptr12/linearr12.h>
40#include <chemistry/qc/mbptr12/vxb_eval_info.h>
41#include <chemistry/qc/mbptr12/svd.h>
42
43using namespace sc;
44using namespace std;
45
46void
47R12IntEvalInfo::construct_ri_basis_(bool safe)
48{
49 if (bs_aux_->equiv(bs_)) {
50 bs_ri_ = bs_;
51 if (abs_method_ == LinearR12::ABS_CABS ||
52 abs_method_ == LinearR12::ABS_CABSPlus)
53 throw std::runtime_error("R12IntEvalInfo::construct_ri_basis_ -- ABS methods CABS and CABS+ can only be used when ABS != OBS");
54 }
55 else {
56 switch(abs_method_) {
57 case LinearR12::ABS_ABS:
58 construct_ri_basis_ks_(safe);
59 break;
60 case LinearR12::ABS_ABSPlus:
61 construct_ri_basis_ksplus_(safe);
62 break;
63 case LinearR12::ABS_CABS:
64 construct_ri_basis_ev_(safe);
65 break;
66 case LinearR12::ABS_CABSPlus:
67 construct_ri_basis_evplus_(safe);
68 break;
69 default:
70 throw std::runtime_error("R12IntEvalInfo::construct_ri_basis_ -- invalid ABS method");
71 }
72 }
73}
74
75void
76R12IntEvalInfo::construct_ri_basis_ks_(bool safe)
77{
78 bs_ri_ = bs_aux_;
79 if (!abs_spans_obs_()) {
80 ExEnv::out0() << endl << indent << "WARNING: the auxiliary basis is not safe to use with the given orbital basis" << endl << endl;
81 if (safe)
82 throw std::runtime_error("R12IntEvalInfo::construct_ri_basis_ks_ -- auxiliary basis is not safe to use with the given orbital basis");
83 }
84}
85
86void
87R12IntEvalInfo::construct_ri_basis_ksplus_(bool safe)
88{
89 GaussianBasisSet& abs = *(bs_aux_.pointer());
90 bs_ri_ = abs + bs_;
91 construct_orthog_ri_();
92}
93
94void
95R12IntEvalInfo::construct_ri_basis_ev_(bool safe)
96{
97 bs_ri_ = bs_aux_;
98 if (!abs_spans_obs_()) {
99 ExEnv::out0() << endl << indent << "WARNING: the auxiliary basis is not safe to use with the given orbital basis" << endl << endl;
100 if (safe)
101 throw std::runtime_error("R12IntEvalInfo::construct_ri_basis_ev_ -- auxiliary basis is not safe to use with the given orbital basis");
102 }
103 construct_ortho_comp_svd_();
104}
105
106void
107R12IntEvalInfo::construct_ri_basis_evplus_(bool safe)
108{
109 GaussianBasisSet& abs = *(bs_aux_.pointer());
110 bs_ri_ = abs + bs_;
111 construct_ortho_comp_svd_();
112}
113
114void
115R12IntEvalInfo::construct_orthog_aux_()
116{
117 if (abs_space_.nonnull())
118 return;
119
120 abs_space_ = orthogonalize("ABS", bs_aux_, integral(), ref_->orthog_method(), ref_->lindep_tol(), nlindep_aux_);
121
122 if (bs_aux_ == bs_ri_)
123 ribs_space_ = abs_space_;
124}
125
126void
127R12IntEvalInfo::construct_orthog_vir_()
128{
129 if (vir_space_.nonnull())
130 return;
131
132 if (bs_ == bs_vir_) {
133 // If virtuals are from the same space as occupieds, then everything is easy
134 vir_space_ = new MOIndexSpace("unoccupied MOs sorted by energy", mo_space_->coefs(),
135 mo_space_->basis(), mo_space_->integral(),
136 mo_space_->evals(), nocc_, 0);
137 // If virtuals are from the same space as occupieds, then everything is easy
138 vir_space_symblk_ = new MOIndexSpace("unoccupied MOs symmetry-blocked", mo_space_->coefs(),
139 mo_space_->basis(), mo_space_->integral(),
140 mo_space_->evals(), nocc_, 0, MOIndexSpace::symmetry);
141
142 if (nfzv_ == 0)
143 act_vir_space_ = vir_space_;
144 else
145 act_vir_space_ = new MOIndexSpace("active unoccupied MOs sorted by energy", mo_space_->coefs(),
146 mo_space_->basis(), mo_space_->integral(),
147 mo_space_->evals(), nocc_, nfzv_);
148 nlindep_vir_ = 0;
149 }
150 else {
151 // This is a set of orthonormal functions that span VBS
152 Ref<MOIndexSpace> vir_space = orthogonalize("VBS", bs_vir_, integral(), ref_->orthog_method(), ref_->lindep_tol(), nlindep_vir_);
153 // Now project out occupied MOs
154 vir_space_symblk_ = orthog_comp(occ_space_symblk_, vir_space, "VBS", ref_->lindep_tol());
155
156 // Design flaw!!! Need to compute Fock matrix right here but can't since Fock is built into R12IntEval
157 // Need to move all relevant code outside of MBPT2-R12 code
158 if (nfzv_ != 0)
159 throw std::runtime_error("R12IntEvalInfo::construct_orthog_vir_() -- nfzv_ != 0 is not allowed yet");
160 vir_space_ = vir_space_symblk_;
161 act_vir_space_ = vir_space_symblk_;
162 }
163}
164
165void
166R12IntEvalInfo::construct_orthog_ri_()
167{
168 if (bs_ri_.null())
169 throw std::runtime_error("R12IntEvalInfo::construct_orthog_ri_ -- RI basis has not been set yet");
170 if (bs_aux_ == bs_ri_)
171 construct_orthog_aux_();
172 if (ribs_space_.nonnull())
173 return;
174
175 ribs_space_ = orthogonalize("RI-BS", bs_ri_, integral(), ref_->orthog_method(), ref_->lindep_tol(), nlindep_ri_);
176}
177
178bool
179R12IntEvalInfo::abs_spans_obs_()
180{
181 construct_orthog_aux_();
182
183 // Compute the bumber of linear dependencies in OBS+ABS
184 GaussianBasisSet& abs = *(bs_aux_.pointer());
185 Ref<GaussianBasisSet> ri_basis = abs + bs_;
186 int nlindep_ri = 0;
187 if (bs_ri_.nonnull() && ri_basis->equiv(bs_ri_)) {
188 construct_orthog_ri_();
189 nlindep_ri = nlindep_ri_;
190 }
191 else {
192 Ref<MOIndexSpace> ribs_space = orthogonalize("OBS+ABS", ri_basis, integral(), ref_->orthog_method(), ref_->lindep_tol(), nlindep_ri);
193 }
194
195 if (nlindep_ri - nlindep_aux_ - mo_space_->rank() == 0)
196 return true;
197 else
198 return false;
199}
200
201/////////////////////////////////////////////////////////////////////////////
202
203void
204R12IntEvalInfo::construct_ortho_comp_svd_()
205{
206 construct_orthog_aux_();
207 construct_orthog_vir_();
208 construct_orthog_ri_();
209
210 if (debug_ > 1) {
211 occ_space_symblk_->coefs().print("Occupied MOs (symblocked)");
212 vir_space_symblk_->coefs().print("Virtual MOs (symblocked)");
213 obs_space_->coefs().print("All MOs");
214 act_occ_space_->coefs().print("Active occupied MOs");
215 act_vir_space_->coefs().print("Active virtual MOs");
216 ribs_space_->coefs().print("Orthogonal RI-BS");
217 }
218
219 ribs_space_ = orthog_comp(occ_space_symblk_, ribs_space_, "RI-BS", ref_->lindep_tol());
220 ribs_space_ = orthog_comp(vir_space_symblk_, ribs_space_, "RI-BS", ref_->lindep_tol());
221}
222
223Ref<MOIndexSpace>
224R12IntEvalInfo::orthogonalize(const std::string& name, const Ref<GaussianBasisSet>& bs,
225 const Ref<Integral>& ints,
226 OverlapOrthog::OrthogMethod orthog_method, double lindep_tol,
227 int& nlindep)
228{
229 // Make an Integral and initialize with bs_aux
230 Ref<Integral> integral = ints->clone();
231 integral->set_basis(bs);
232 Ref<PetiteList> plist = integral->petite_list();
233 Ref<OneBodyInt> ov_engine = integral->overlap();
234
235 // form skeleton s matrix
236 Ref<SCMatrixKit> matrixkit = bs->matrixkit();
237 RefSymmSCMatrix s(bs->basisdim(), matrixkit);
238 Ref<SCElementOp> ov =
239 new OneBodyIntOp(new SymmOneBodyIntIter(ov_engine, plist));
240
241 s.assign(0.0);
242 s.element_op(ov);
243 ov=0;
244 //if (debug_ > 1) {
245 // std::string s_label = "AO skeleton overlap (" + name + "/" + name + ")";
246 // s.print(s_label.c_str());
247 //}
248
249 // then symmetrize it
250 RefSCDimension sodim = plist->SO_basisdim();
251 Ref<SCMatrixKit> so_matrixkit = bs->so_matrixkit();
252 RefSymmSCMatrix overlap(sodim, so_matrixkit);
253 plist->symmetrize(s,overlap);
254
255 // and clean up a bit
256 ov_engine = 0;
257 s = 0;
258
259 //
260 // Compute orthogonalizer for bs
261 //
262 ExEnv::out0() << indent << "Orthogonalizing basis for space " << name << ":" << endl << incindent;
263 OverlapOrthog orthog(orthog_method,
264 overlap,
265 so_matrixkit,
266 lindep_tol,
267 0);
268 RefSCMatrix orthog_so = orthog.basis_to_orthog_basis();
269 orthog_so = orthog_so.t();
270 RefSCMatrix orthog_ao = plist->evecs_to_AO_basis(orthog_so);
271 orthog_so = 0;
272 ExEnv::out0() << decindent;
273
274 nlindep = orthog.nlindep();
275 Ref<MOIndexSpace> space = new MOIndexSpace(name,orthog_ao,bs,integral);
276
277 return space;
278}
279
280
281Ref<MOIndexSpace>
282R12IntEvalInfo::orthog_comp(const Ref<MOIndexSpace>& space1, const Ref<MOIndexSpace>& space2,
283 const std::string& name, double lindep_tol)
284{
285 if (!space1->integral()->equiv(space2->integral()))
286 throw ProgrammingError("Two MOIndexSpaces use incompatible Integral factories");
287 // Both spaces must be ordered in the same way
288 if (space1->moorder() != space2->moorder())
289 throw std::runtime_error("R12IntEvalInfo::orthog_comp() -- space1 and space2 are ordered differently ");
290
291 ExEnv::out0() << indent
292 << "SVD-projecting out " << space1->name() << " out of " << space2->name()
293 << " to obtain space " << name << endl << incindent;
294
295 // C12 = C1 * S12 * C2
296 RefSCMatrix C12;
297 compute_overlap_ints(space1,space2,C12);
298
299 //
300 // SVDecompose C12 = U Sigma V and throw out columns of V
301 //
302 Ref<SCMatrixKit> ao_matrixkit = space1->basis()->matrixkit();
303 Ref<SCMatrixKit> so_matrixkit = space1->basis()->so_matrixkit();
304 int nblocks = C12.nblock();
305 const double toler = lindep_tol;
306 double min_sigma = 1.0;
307 double max_sigma = 0.0;
308 int* nvec_per_block = new int[nblocks];
309 // basis for orthogonal complement is a vector of nvecs by nbasis2
310 // we don't know nvecs yet, so use rank2
311 RefSCMatrix orthog2 = space2->coefs();
312 int rank2 = orthog2.coldim().n();
313 int nbasis2 = orthog2.rowdim().n();
314 double* vecs = new double[rank2 * nbasis2];
315 int nlindep = 0;
316
317 int v_offset = 0;
318 for(int b=0; b<nblocks; b++) {
319
320 RefSCDimension rowd = C12.rowdim()->blocks()->subdim(b);
321 RefSCDimension cold = C12.coldim()->blocks()->subdim(b);
322 int nrow = rowd.n();
323 int ncol = cold.n();
324 if (nrow && ncol) {
325
326 RefSCMatrix C12_b = C12.block(b);
327 RefSCDimension sigd = nrow < ncol ? rowd : cold;
328 int nsigmas = sigd.n();
329
330 RefSCMatrix U(rowd, rowd, ao_matrixkit);
331 RefSCMatrix V(cold, cold, ao_matrixkit);
332 RefDiagSCMatrix Sigma(sigd, ao_matrixkit);
333
334 // C12_b.svd(U,Sigma,V);
335 exp::lapack_svd(C12_b,U,Sigma,V);
336
337 // Transform V into AO basis. Vectors are in rows
338 RefSCMatrix orthog2_b = orthog2.block(b);
339 V = V * orthog2_b.t();
340
341 // Figure out how many sigmas are too small, i.e. how many vectors from space2 overlap
342 // only weakly with space1.
343 // NOTE: Sigma values returned by svd() are in descending order
344 int nzeros = 0;
345 for(int s=0; s<nsigmas; s++) {
346 double sigma = Sigma(s);
347 if (sigma < toler)
348 nzeros++;
349 if (sigma < min_sigma)
350 min_sigma = sigma;
351 if (sigma > max_sigma)
352 max_sigma = sigma;
353 }
354
355 // number of vectors that span the orthogonal space
356 nvec_per_block[b] = nzeros + ncol - nsigmas;
357 nlindep += nsigmas - nzeros;
358
359 if (nvec_per_block[b]) {
360 int v_first = nsigmas - nzeros;
361 int v_last = ncol - 1;
362 double* v_ptr = vecs + v_offset*nbasis2;
363 RefSCMatrix vtmp = V.get_subblock(v_first,v_last,0,nbasis2-1);
364 vtmp.convert(v_ptr);
365 }
366 }
367 else {
368 nvec_per_block[b] = ncol;
369
370 if (nvec_per_block[b]) {
371 RefSCMatrix orthog2_b = orthog2.block(b);
372 orthog2_b = orthog2_b.t();
373 double* v_ptr = vecs + v_offset*nbasis2;
374 orthog2_b.convert(v_ptr);
375 }
376 }
377
378 v_offset += nvec_per_block[b];
379 }
380
381 // Modify error message
382 if (v_offset == 0) {
383 const std::string errmsg = "R12IntEvalInfo::orthog_comp() -- " + space2->name()
384 + " has null projection on orthogonal complement to " + space2->name()
385 + "Modify/increase basis for " + space2->name() + ".";
386 throw std::runtime_error(errmsg.c_str());
387 }
388
389 // convert vecs into orthog2
390 // modify for the dimension
391 RefSCDimension orthog_dim = new SCDimension(v_offset, nblocks, nvec_per_block, "");
392 for(int b=0; b<nblocks; b++)
393 orthog_dim->blocks()->set_subdim(b, new SCDimension(nvec_per_block[b]));
394 RefSCMatrix orthog_vecs(orthog_dim,orthog2.rowdim(),so_matrixkit);
395 orthog_vecs.assign(vecs);
396 orthog2 = orthog_vecs.t();
397
398 ExEnv::out0() << indent
399 << nlindep << " basis function"
400 << (nlindep>1?"s":"")
401 << " projected out of " << space2->name() << "."
402 << endl;
403 ExEnv::out0() << indent
404 << "n(basis): ";
405 for (int i=0; i<orthog_dim->blocks()->nblock(); i++) {
406 ExEnv::out0() << scprintf(" %5d", orthog_dim->blocks()->size(i));
407 }
408 ExEnv::out0() << endl;
409 ExEnv::out0() << indent
410 << "Maximum singular value = "
411 << max_sigma << endl
412 << indent
413 << "Minimum singular value = "
414 << min_sigma << endl;
415 ExEnv::out0() << decindent;
416
417 delete[] vecs;
418 delete[] nvec_per_block;
419
420 Ref<MOIndexSpace> orthog_comp_space = new MOIndexSpace(name,orthog2,space2->basis(),space2->integral());
421
422 return orthog_comp_space;
423}
424
425
426Ref<MOIndexSpace>
427R12IntEvalInfo::gen_project(const Ref<MOIndexSpace>& space1, const Ref<MOIndexSpace>& space2,
428 const std::string& name, double lindep_tol)
429{
430 //
431 // Projection works as follows:
432 // 1) Compute overlap matrix between orthonormal spaces 1 and 2: C12 = C1 * S12 * C2
433 // 2) SVDecompose C12 = U Sigma V^t, throw out (near)singular triplets
434 // 3) Projected vectors (in AO basis) are X2 = C2 * V * Sigma^{-1} * U^t, where Sigma^{-1} is the generalized inverse
435 //
436
437 // Check integral factories
438 if (!space1->integral()->equiv(space2->integral()))
439 throw ProgrammingError("Two MOIndexSpaces use incompatible Integral factories");
440 // Both spaces must be ordered in the same way
441 if (space1->moorder() != space2->moorder())
442 throw std::runtime_error("R12IntEvalInfo::orthog_comp() -- space1 and space2 are ordered differently ");
443
444 ExEnv::out0() << indent
445 << "Projecting " << space1->name() << " onto " << space2->name()
446 << " exactly to obtain space " << name << endl << incindent;
447
448 // C12 = C1 * S12 * C2
449 RefSCMatrix C12;
450 compute_overlap_ints(space1,space2,C12);
451 C12.print("C12 matrix");
452
453 // Check dimensions of C12 to make sure that projection makes sense
454
455
456 Ref<SCMatrixKit> ao_matrixkit = space1->basis()->matrixkit();
457 Ref<SCMatrixKit> so_matrixkit = space1->basis()->so_matrixkit();
458 int nblocks = C12.nblock();
459 const double toler = lindep_tol;
460 double min_sigma = 1.0;
461 double max_sigma = 0.0;
462 int* nvec_per_block = new int[nblocks];
463
464 // projected vectors are a matrix of nvecs by nbasis2
465 // we don't know nvecs yet, so use rank1
466 RefSCMatrix C1 = space1->coefs();
467 RefSCMatrix C2 = space2->coefs();
468 int rank1 = space1->coefs()->ncol();
469 int nbasis2 = C2->nrow();
470 double* vecs = new double[rank1 * nbasis2];
471 int nweakovlp = 0;
472
473 int v_offset = 0;
474 for(int b=0; b<nblocks; b++) {
475
476 RefSCDimension rowd = C12.rowdim()->blocks()->subdim(b);
477 RefSCDimension cold = C12.coldim()->blocks()->subdim(b);
478 int nrow = rowd.n();
479 int ncol = cold.n();
480
481 // Cannot project if rank of the target space is smaller than the rank of the source space
482 if (nrow > ncol)
483 throw std::runtime_error("R12IntEvalInfo::svd_project() -- rank of the target space is smaller than the rank of the source space");
484
485 if (nrow && ncol) {
486
487 RefSCMatrix C12_b = C12.block(b);
488 RefSCDimension sigd = rowd;
489 int nsigmas = sigd.n();
490
491 RefSCMatrix U(rowd, rowd, ao_matrixkit);
492 RefSCMatrix V(cold, cold, ao_matrixkit);
493 RefDiagSCMatrix Sigma(sigd, ao_matrixkit);
494
495 //
496 // Compute C12 = U * Sigma * V
497 //
498 /* C12_b.svd(U,Sigma,V); */
499 exp::lapack_svd(C12_b,U,Sigma,V);
500
501 // Figure out how many sigmas are too small, i.e. how many vectors from space2 overlap
502 // only weakly with space1.
503 // NOTE: Sigma values returned by svd() are in descending order
504 int nzeros = 0;
505 for(int s=0; s<nsigmas; s++) {
506 double sigma = Sigma(s);
507 if (sigma < toler)
508 nzeros++;
509 if (sigma < min_sigma)
510 min_sigma = sigma;
511 if (sigma > max_sigma)
512 max_sigma = sigma;
513 }
514
515 // number of vectors that span the projected space
516 nvec_per_block[b] = nsigmas - nzeros;
517 if (nvec_per_block[b] < nrow)
518 throw std::runtime_error("R12IntEvalInfo::gen_project() -- space 1 is not fully spanned by space 2");
519 nweakovlp += nzeros + ncol - nrow;
520
521 if (nvec_per_block[b]) {
522 int s_first = 0;
523 int s_last = nvec_per_block[b]-1;
524 RefSCMatrix vtmp = V.get_subblock(s_first,s_last,0,ncol-1);
525 RefSCDimension rowdim = vtmp.rowdim();
526 RefDiagSCMatrix stmp = vtmp.kit()->diagmatrix(rowdim);
527 for(int i=0; i<nvec_per_block[b]; i++)
528 stmp(i) = 1.0/(Sigma(i));
529 RefSCMatrix utmp = U.get_subblock(0,nrow-1,s_first,s_last);
530 RefSCMatrix C12_inv_t = (utmp * stmp) * vtmp;
531
532 (C12_b * C12_inv_t.t()).print("C12 * C12^{-1}");
533 (C12_inv_t * C12_b.t()).print("C12^{-1} * C12");
534
535 // Transform V into AO basis and transpose so that vectors are in rows
536 RefSCMatrix C2_b = C2.block(b);
537 RefSCMatrix X2_t = C12_inv_t * C2_b.t();
538 double* x2t_ptr = vecs + v_offset*nbasis2;
539 X2_t.convert(x2t_ptr);
540 }
541 }
542 else {
543 nvec_per_block[b] = 0;
544 }
545
546
547 v_offset += nvec_per_block[b];
548 }
549
550 // convert vecs into proj
551 RefSCMatrix proj(C1.coldim(),C2.rowdim(),so_matrixkit);
552 proj.assign(vecs);
553 proj = proj.t();
554
555 ExEnv::out0() << indent
556 << nweakovlp << " basis function"
557 << (nweakovlp>1?"s":"")
558 << " in " << space2->name() << " did not overlap significantly with "
559 << space1->name() << "." << endl;
560 ExEnv::out0() << indent
561 << "n(basis): ";
562 for (int i=0; i<proj.coldim()->blocks()->nblock(); i++) {
563 ExEnv::out0() << scprintf(" %5d", proj.coldim()->blocks()->size(i));
564 }
565 ExEnv::out0() << endl;
566 ExEnv::out0() << indent
567 << "Maximum singular value = "
568 << max_sigma << endl
569 << indent
570 << "Minimum singular value = "
571 << min_sigma << endl;
572 ExEnv::out0() << decindent;
573
574 delete[] vecs;
575 delete[] nvec_per_block;
576
577 Ref<MOIndexSpace> proj_space = new MOIndexSpace(name,proj,space2->basis(),space2->integral());
578
579 RefSCMatrix S12; compute_overlap_ints(space1,proj_space,S12);
580 S12.print("Check: overlap between space1 and projected space");
581
582 return proj_space;
583}
584
585/////////////////////////////////////////////////////////////////////////////
586
587// Local Variables:
588// mode: c++
589// c-file-style: "CLJ-CONDENSED"
590// End:
Note: See TracBrowser for help on using the repository browser.