4
// Copyright (C) 2004 Edward Valeev
6
// Author: Edward Valeev <edward.valeev@chemistry.gatech.edu>
9
// This file is part of the SC Toolkit.
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)
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.
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.
25
// The U.S. Government is granted a limited license as per AL 91-7.
29
#pragma implementation
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>
47
R12IntEvalInfo::construct_ri_basis_(bool safe)
49
if (bs_aux_->equiv(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");
57
case LinearR12::ABS_ABS:
58
construct_ri_basis_ks_(safe);
60
case LinearR12::ABS_ABSPlus:
61
construct_ri_basis_ksplus_(safe);
63
case LinearR12::ABS_CABS:
64
construct_ri_basis_ev_(safe);
66
case LinearR12::ABS_CABSPlus:
67
construct_ri_basis_evplus_(safe);
70
throw std::runtime_error("R12IntEvalInfo::construct_ri_basis_ -- invalid ABS method");
76
R12IntEvalInfo::construct_ri_basis_ks_(bool safe)
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;
82
throw std::runtime_error("R12IntEvalInfo::construct_ri_basis_ks_ -- auxiliary basis is not safe to use with the given orbital basis");
87
R12IntEvalInfo::construct_ri_basis_ksplus_(bool safe)
89
GaussianBasisSet& abs = *(bs_aux_.pointer());
91
construct_orthog_ri_();
95
R12IntEvalInfo::construct_ri_basis_ev_(bool safe)
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;
101
throw std::runtime_error("R12IntEvalInfo::construct_ri_basis_ev_ -- auxiliary basis is not safe to use with the given orbital basis");
103
construct_ortho_comp_svd_();
107
R12IntEvalInfo::construct_ri_basis_evplus_(bool safe)
109
GaussianBasisSet& abs = *(bs_aux_.pointer());
111
construct_ortho_comp_svd_();
115
R12IntEvalInfo::construct_orthog_aux_()
117
if (abs_space_.nonnull())
120
abs_space_ = orthogonalize("ABS", bs_aux_, integral(), ref_->orthog_method(), ref_->lindep_tol(), nlindep_aux_);
122
if (bs_aux_ == bs_ri_)
123
ribs_space_ = abs_space_;
127
R12IntEvalInfo::construct_orthog_vir_()
129
if (vir_space_.nonnull())
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);
143
act_vir_space_ = vir_space_;
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_);
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());
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
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_;
166
R12IntEvalInfo::construct_orthog_ri_()
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())
175
ribs_space_ = orthogonalize("RI-BS", bs_ri_, integral(), ref_->orthog_method(), ref_->lindep_tol(), nlindep_ri_);
179
R12IntEvalInfo::abs_spans_obs_()
181
construct_orthog_aux_();
183
// Compute the bumber of linear dependencies in OBS+ABS
184
GaussianBasisSet& abs = *(bs_aux_.pointer());
185
Ref<GaussianBasisSet> ri_basis = abs + bs_;
187
if (bs_ri_.nonnull() && ri_basis->equiv(bs_ri_)) {
188
construct_orthog_ri_();
189
nlindep_ri = nlindep_ri_;
192
Ref<MOIndexSpace> ribs_space = orthogonalize("OBS+ABS", ri_basis, integral(), ref_->orthog_method(), ref_->lindep_tol(), nlindep_ri);
195
if (nlindep_ri - nlindep_aux_ - mo_space_->rank() == 0)
201
/////////////////////////////////////////////////////////////////////////////
204
R12IntEvalInfo::construct_ortho_comp_svd_()
206
construct_orthog_aux_();
207
construct_orthog_vir_();
208
construct_orthog_ri_();
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");
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());
224
R12IntEvalInfo::orthogonalize(const std::string& name, const Ref<GaussianBasisSet>& bs,
225
const Ref<Integral>& ints,
226
OverlapOrthog::OrthogMethod orthog_method, double lindep_tol,
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();
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));
245
// std::string s_label = "AO skeleton overlap (" + name + "/" + name + ")";
246
// s.print(s_label.c_str());
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);
255
// and clean up a bit
260
// Compute orthogonalizer for bs
262
ExEnv::out0() << indent << "Orthogonalizing basis for space " << name << ":" << endl << incindent;
263
OverlapOrthog orthog(orthog_method,
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);
272
ExEnv::out0() << decindent;
274
nlindep = orthog.nlindep();
275
Ref<MOIndexSpace> space = new MOIndexSpace(name,orthog_ao,bs,integral);
282
R12IntEvalInfo::orthog_comp(const Ref<MOIndexSpace>& space1, const Ref<MOIndexSpace>& space2,
283
const std::string& name, double lindep_tol)
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 ");
291
ExEnv::out0() << indent
292
<< "SVD-projecting out " << space1->name() << " out of " << space2->name()
293
<< " to obtain space " << name << endl << incindent;
295
// C12 = C1 * S12 * C2
297
compute_overlap_ints(space1,space2,C12);
300
// SVDecompose C12 = U Sigma V and throw out columns of V
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];
318
for(int b=0; b<nblocks; b++) {
320
RefSCDimension rowd = C12.rowdim()->blocks()->subdim(b);
321
RefSCDimension cold = C12.coldim()->blocks()->subdim(b);
326
RefSCMatrix C12_b = C12.block(b);
327
RefSCDimension sigd = nrow < ncol ? rowd : cold;
328
int nsigmas = sigd.n();
330
RefSCMatrix U(rowd, rowd, ao_matrixkit);
331
RefSCMatrix V(cold, cold, ao_matrixkit);
332
RefDiagSCMatrix Sigma(sigd, ao_matrixkit);
334
// C12_b.svd(U,Sigma,V);
335
exp::lapack_svd(C12_b,U,Sigma,V);
337
// Transform V into AO basis. Vectors are in rows
338
RefSCMatrix orthog2_b = orthog2.block(b);
339
V = V * orthog2_b.t();
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
345
for(int s=0; s<nsigmas; s++) {
346
double sigma = Sigma(s);
349
if (sigma < min_sigma)
351
if (sigma > max_sigma)
355
// number of vectors that span the orthogonal space
356
nvec_per_block[b] = nzeros + ncol - nsigmas;
357
nlindep += nsigmas - nzeros;
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);
368
nvec_per_block[b] = ncol;
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);
378
v_offset += nvec_per_block[b];
381
// Modify error message
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());
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();
398
ExEnv::out0() << indent
399
<< nlindep << " basis function"
400
<< (nlindep>1?"s":"")
401
<< " projected out of " << space2->name() << "."
403
ExEnv::out0() << indent
405
for (int i=0; i<orthog_dim->blocks()->nblock(); i++) {
406
ExEnv::out0() << scprintf(" %5d", orthog_dim->blocks()->size(i));
408
ExEnv::out0() << endl;
409
ExEnv::out0() << indent
410
<< "Maximum singular value = "
413
<< "Minimum singular value = "
414
<< min_sigma << endl;
415
ExEnv::out0() << decindent;
418
delete[] nvec_per_block;
420
Ref<MOIndexSpace> orthog_comp_space = new MOIndexSpace(name,orthog2,space2->basis(),space2->integral());
422
return orthog_comp_space;
427
R12IntEvalInfo::gen_project(const Ref<MOIndexSpace>& space1, const Ref<MOIndexSpace>& space2,
428
const std::string& name, double lindep_tol)
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
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 ");
444
ExEnv::out0() << indent
445
<< "Projecting " << space1->name() << " onto " << space2->name()
446
<< " exactly to obtain space " << name << endl << incindent;
448
// C12 = C1 * S12 * C2
450
compute_overlap_ints(space1,space2,C12);
451
C12.print("C12 matrix");
453
// Check dimensions of C12 to make sure that projection makes sense
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];
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];
474
for(int b=0; b<nblocks; b++) {
476
RefSCDimension rowd = C12.rowdim()->blocks()->subdim(b);
477
RefSCDimension cold = C12.coldim()->blocks()->subdim(b);
481
// Cannot project if rank of the target space is smaller than the rank of the source space
483
throw std::runtime_error("R12IntEvalInfo::svd_project() -- rank of the target space is smaller than the rank of the source space");
487
RefSCMatrix C12_b = C12.block(b);
488
RefSCDimension sigd = rowd;
489
int nsigmas = sigd.n();
491
RefSCMatrix U(rowd, rowd, ao_matrixkit);
492
RefSCMatrix V(cold, cold, ao_matrixkit);
493
RefDiagSCMatrix Sigma(sigd, ao_matrixkit);
496
// Compute C12 = U * Sigma * V
498
/* C12_b.svd(U,Sigma,V); */
499
exp::lapack_svd(C12_b,U,Sigma,V);
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
505
for(int s=0; s<nsigmas; s++) {
506
double sigma = Sigma(s);
509
if (sigma < min_sigma)
511
if (sigma > max_sigma)
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;
521
if (nvec_per_block[b]) {
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;
532
(C12_b * C12_inv_t.t()).print("C12 * C12^{-1}");
533
(C12_inv_t * C12_b.t()).print("C12^{-1} * C12");
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);
543
nvec_per_block[b] = 0;
547
v_offset += nvec_per_block[b];
550
// convert vecs into proj
551
RefSCMatrix proj(C1.coldim(),C2.rowdim(),so_matrixkit);
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
562
for (int i=0; i<proj.coldim()->blocks()->nblock(); i++) {
563
ExEnv::out0() << scprintf(" %5d", proj.coldim()->blocks()->size(i));
565
ExEnv::out0() << endl;
566
ExEnv::out0() << indent
567
<< "Maximum singular value = "
570
<< "Minimum singular value = "
571
<< min_sigma << endl;
572
ExEnv::out0() << decindent;
575
delete[] nvec_per_block;
577
Ref<MOIndexSpace> proj_space = new MOIndexSpace(name,proj,space2->basis(),space2->integral());
579
RefSCMatrix S12; compute_overlap_ints(space1,proj_space,S12);
580
S12.print("Check: overlap between space1 and projected space");
585
/////////////////////////////////////////////////////////////////////////////
589
// c-file-style: "CLJ-CONDENSED"