// // ri_basis.cc // // Copyright (C) 2004 Edward Valeev // // Author: Edward Valeev // Maintainer: EV // // This file is part of the SC Toolkit. // // The SC Toolkit is free software; you can redistribute it and/or modify // it under the terms of the GNU Library General Public License as published by // the Free Software Foundation; either version 2, or (at your option) // any later version. // // The SC Toolkit is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Library General Public License for more details. // // You should have received a copy of the GNU Library General Public License // along with the SC Toolkit; see the file COPYING.LIB. If not, write to // the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. // // The U.S. Government is granted a limited license as per AL 91-7. // #ifdef __GNUC__ #pragma implementation #endif #include #include #include #include #include #include #include #include #include using namespace sc; using namespace std; void R12IntEvalInfo::construct_ri_basis_(bool safe) { if (bs_aux_->equiv(bs_)) { bs_ri_ = bs_; if (abs_method_ == LinearR12::ABS_CABS || abs_method_ == LinearR12::ABS_CABSPlus) throw std::runtime_error("R12IntEvalInfo::construct_ri_basis_ -- ABS methods CABS and CABS+ can only be used when ABS != OBS"); } else { switch(abs_method_) { case LinearR12::ABS_ABS: construct_ri_basis_ks_(safe); break; case LinearR12::ABS_ABSPlus: construct_ri_basis_ksplus_(safe); break; case LinearR12::ABS_CABS: construct_ri_basis_ev_(safe); break; case LinearR12::ABS_CABSPlus: construct_ri_basis_evplus_(safe); break; default: throw std::runtime_error("R12IntEvalInfo::construct_ri_basis_ -- invalid ABS method"); } } } void R12IntEvalInfo::construct_ri_basis_ks_(bool safe) { bs_ri_ = bs_aux_; if (!abs_spans_obs_()) { ExEnv::out0() << endl << indent << "WARNING: the auxiliary basis is not safe to use with the given orbital basis" << endl << endl; if (safe) throw std::runtime_error("R12IntEvalInfo::construct_ri_basis_ks_ -- auxiliary basis is not safe to use with the given orbital basis"); } } void R12IntEvalInfo::construct_ri_basis_ksplus_(bool safe) { GaussianBasisSet& abs = *(bs_aux_.pointer()); bs_ri_ = abs + bs_; construct_orthog_ri_(); } void R12IntEvalInfo::construct_ri_basis_ev_(bool safe) { bs_ri_ = bs_aux_; if (!abs_spans_obs_()) { ExEnv::out0() << endl << indent << "WARNING: the auxiliary basis is not safe to use with the given orbital basis" << endl << endl; if (safe) throw std::runtime_error("R12IntEvalInfo::construct_ri_basis_ev_ -- auxiliary basis is not safe to use with the given orbital basis"); } construct_ortho_comp_svd_(); } void R12IntEvalInfo::construct_ri_basis_evplus_(bool safe) { GaussianBasisSet& abs = *(bs_aux_.pointer()); bs_ri_ = abs + bs_; construct_ortho_comp_svd_(); } void R12IntEvalInfo::construct_orthog_aux_() { if (abs_space_.nonnull()) return; abs_space_ = orthogonalize("ABS", bs_aux_, integral(), ref_->orthog_method(), ref_->lindep_tol(), nlindep_aux_); if (bs_aux_ == bs_ri_) ribs_space_ = abs_space_; } void R12IntEvalInfo::construct_orthog_vir_() { if (vir_space_.nonnull()) return; if (bs_ == bs_vir_) { // If virtuals are from the same space as occupieds, then everything is easy vir_space_ = new MOIndexSpace("unoccupied MOs sorted by energy", mo_space_->coefs(), mo_space_->basis(), mo_space_->integral(), mo_space_->evals(), nocc_, 0); // If virtuals are from the same space as occupieds, then everything is easy vir_space_symblk_ = new MOIndexSpace("unoccupied MOs symmetry-blocked", mo_space_->coefs(), mo_space_->basis(), mo_space_->integral(), mo_space_->evals(), nocc_, 0, MOIndexSpace::symmetry); if (nfzv_ == 0) act_vir_space_ = vir_space_; else act_vir_space_ = new MOIndexSpace("active unoccupied MOs sorted by energy", mo_space_->coefs(), mo_space_->basis(), mo_space_->integral(), mo_space_->evals(), nocc_, nfzv_); nlindep_vir_ = 0; } else { // This is a set of orthonormal functions that span VBS Ref vir_space = orthogonalize("VBS", bs_vir_, integral(), ref_->orthog_method(), ref_->lindep_tol(), nlindep_vir_); // Now project out occupied MOs vir_space_symblk_ = orthog_comp(occ_space_symblk_, vir_space, "VBS", ref_->lindep_tol()); // Design flaw!!! Need to compute Fock matrix right here but can't since Fock is built into R12IntEval // Need to move all relevant code outside of MBPT2-R12 code if (nfzv_ != 0) throw std::runtime_error("R12IntEvalInfo::construct_orthog_vir_() -- nfzv_ != 0 is not allowed yet"); vir_space_ = vir_space_symblk_; act_vir_space_ = vir_space_symblk_; } } void R12IntEvalInfo::construct_orthog_ri_() { if (bs_ri_.null()) throw std::runtime_error("R12IntEvalInfo::construct_orthog_ri_ -- RI basis has not been set yet"); if (bs_aux_ == bs_ri_) construct_orthog_aux_(); if (ribs_space_.nonnull()) return; ribs_space_ = orthogonalize("RI-BS", bs_ri_, integral(), ref_->orthog_method(), ref_->lindep_tol(), nlindep_ri_); } bool R12IntEvalInfo::abs_spans_obs_() { construct_orthog_aux_(); // Compute the bumber of linear dependencies in OBS+ABS GaussianBasisSet& abs = *(bs_aux_.pointer()); Ref ri_basis = abs + bs_; int nlindep_ri = 0; if (bs_ri_.nonnull() && ri_basis->equiv(bs_ri_)) { construct_orthog_ri_(); nlindep_ri = nlindep_ri_; } else { Ref ribs_space = orthogonalize("OBS+ABS", ri_basis, integral(), ref_->orthog_method(), ref_->lindep_tol(), nlindep_ri); } if (nlindep_ri - nlindep_aux_ - mo_space_->rank() == 0) return true; else return false; } ///////////////////////////////////////////////////////////////////////////// void R12IntEvalInfo::construct_ortho_comp_svd_() { construct_orthog_aux_(); construct_orthog_vir_(); construct_orthog_ri_(); if (debug_ > 1) { occ_space_symblk_->coefs().print("Occupied MOs (symblocked)"); vir_space_symblk_->coefs().print("Virtual MOs (symblocked)"); obs_space_->coefs().print("All MOs"); act_occ_space_->coefs().print("Active occupied MOs"); act_vir_space_->coefs().print("Active virtual MOs"); ribs_space_->coefs().print("Orthogonal RI-BS"); } ribs_space_ = orthog_comp(occ_space_symblk_, ribs_space_, "RI-BS", ref_->lindep_tol()); ribs_space_ = orthog_comp(vir_space_symblk_, ribs_space_, "RI-BS", ref_->lindep_tol()); } Ref R12IntEvalInfo::orthogonalize(const std::string& name, const Ref& bs, const Ref& ints, OverlapOrthog::OrthogMethod orthog_method, double lindep_tol, int& nlindep) { // Make an Integral and initialize with bs_aux Ref integral = ints->clone(); integral->set_basis(bs); Ref plist = integral->petite_list(); Ref ov_engine = integral->overlap(); // form skeleton s matrix Ref matrixkit = bs->matrixkit(); RefSymmSCMatrix s(bs->basisdim(), matrixkit); Ref ov = new OneBodyIntOp(new SymmOneBodyIntIter(ov_engine, plist)); s.assign(0.0); s.element_op(ov); ov=0; //if (debug_ > 1) { // std::string s_label = "AO skeleton overlap (" + name + "/" + name + ")"; // s.print(s_label.c_str()); //} // then symmetrize it RefSCDimension sodim = plist->SO_basisdim(); Ref so_matrixkit = bs->so_matrixkit(); RefSymmSCMatrix overlap(sodim, so_matrixkit); plist->symmetrize(s,overlap); // and clean up a bit ov_engine = 0; s = 0; // // Compute orthogonalizer for bs // ExEnv::out0() << indent << "Orthogonalizing basis for space " << name << ":" << endl << incindent; OverlapOrthog orthog(orthog_method, overlap, so_matrixkit, lindep_tol, 0); RefSCMatrix orthog_so = orthog.basis_to_orthog_basis(); orthog_so = orthog_so.t(); RefSCMatrix orthog_ao = plist->evecs_to_AO_basis(orthog_so); orthog_so = 0; ExEnv::out0() << decindent; nlindep = orthog.nlindep(); Ref space = new MOIndexSpace(name,orthog_ao,bs,integral); return space; } Ref R12IntEvalInfo::orthog_comp(const Ref& space1, const Ref& space2, const std::string& name, double lindep_tol) { if (!space1->integral()->equiv(space2->integral())) throw ProgrammingError("Two MOIndexSpaces use incompatible Integral factories"); // Both spaces must be ordered in the same way if (space1->moorder() != space2->moorder()) throw std::runtime_error("R12IntEvalInfo::orthog_comp() -- space1 and space2 are ordered differently "); ExEnv::out0() << indent << "SVD-projecting out " << space1->name() << " out of " << space2->name() << " to obtain space " << name << endl << incindent; // C12 = C1 * S12 * C2 RefSCMatrix C12; compute_overlap_ints(space1,space2,C12); // // SVDecompose C12 = U Sigma V and throw out columns of V // Ref ao_matrixkit = space1->basis()->matrixkit(); Ref so_matrixkit = space1->basis()->so_matrixkit(); int nblocks = C12.nblock(); const double toler = lindep_tol; double min_sigma = 1.0; double max_sigma = 0.0; int* nvec_per_block = new int[nblocks]; // basis for orthogonal complement is a vector of nvecs by nbasis2 // we don't know nvecs yet, so use rank2 RefSCMatrix orthog2 = space2->coefs(); int rank2 = orthog2.coldim().n(); int nbasis2 = orthog2.rowdim().n(); double* vecs = new double[rank2 * nbasis2]; int nlindep = 0; int v_offset = 0; for(int b=0; bblocks()->subdim(b); RefSCDimension cold = C12.coldim()->blocks()->subdim(b); int nrow = rowd.n(); int ncol = cold.n(); if (nrow && ncol) { RefSCMatrix C12_b = C12.block(b); RefSCDimension sigd = nrow < ncol ? rowd : cold; int nsigmas = sigd.n(); RefSCMatrix U(rowd, rowd, ao_matrixkit); RefSCMatrix V(cold, cold, ao_matrixkit); RefDiagSCMatrix Sigma(sigd, ao_matrixkit); // C12_b.svd(U,Sigma,V); exp::lapack_svd(C12_b,U,Sigma,V); // Transform V into AO basis. Vectors are in rows RefSCMatrix orthog2_b = orthog2.block(b); V = V * orthog2_b.t(); // Figure out how many sigmas are too small, i.e. how many vectors from space2 overlap // only weakly with space1. // NOTE: Sigma values returned by svd() are in descending order int nzeros = 0; for(int s=0; s max_sigma) max_sigma = sigma; } // number of vectors that span the orthogonal space nvec_per_block[b] = nzeros + ncol - nsigmas; nlindep += nsigmas - nzeros; if (nvec_per_block[b]) { int v_first = nsigmas - nzeros; int v_last = ncol - 1; double* v_ptr = vecs + v_offset*nbasis2; RefSCMatrix vtmp = V.get_subblock(v_first,v_last,0,nbasis2-1); vtmp.convert(v_ptr); } } else { nvec_per_block[b] = ncol; if (nvec_per_block[b]) { RefSCMatrix orthog2_b = orthog2.block(b); orthog2_b = orthog2_b.t(); double* v_ptr = vecs + v_offset*nbasis2; orthog2_b.convert(v_ptr); } } v_offset += nvec_per_block[b]; } // Modify error message if (v_offset == 0) { const std::string errmsg = "R12IntEvalInfo::orthog_comp() -- " + space2->name() + " has null projection on orthogonal complement to " + space2->name() + "Modify/increase basis for " + space2->name() + "."; throw std::runtime_error(errmsg.c_str()); } // convert vecs into orthog2 // modify for the dimension RefSCDimension orthog_dim = new SCDimension(v_offset, nblocks, nvec_per_block, ""); for(int b=0; bblocks()->set_subdim(b, new SCDimension(nvec_per_block[b])); RefSCMatrix orthog_vecs(orthog_dim,orthog2.rowdim(),so_matrixkit); orthog_vecs.assign(vecs); orthog2 = orthog_vecs.t(); ExEnv::out0() << indent << nlindep << " basis function" << (nlindep>1?"s":"") << " projected out of " << space2->name() << "." << endl; ExEnv::out0() << indent << "n(basis): "; for (int i=0; iblocks()->nblock(); i++) { ExEnv::out0() << scprintf(" %5d", orthog_dim->blocks()->size(i)); } ExEnv::out0() << endl; ExEnv::out0() << indent << "Maximum singular value = " << max_sigma << endl << indent << "Minimum singular value = " << min_sigma << endl; ExEnv::out0() << decindent; delete[] vecs; delete[] nvec_per_block; Ref orthog_comp_space = new MOIndexSpace(name,orthog2,space2->basis(),space2->integral()); return orthog_comp_space; } Ref R12IntEvalInfo::gen_project(const Ref& space1, const Ref& space2, const std::string& name, double lindep_tol) { // // Projection works as follows: // 1) Compute overlap matrix between orthonormal spaces 1 and 2: C12 = C1 * S12 * C2 // 2) SVDecompose C12 = U Sigma V^t, throw out (near)singular triplets // 3) Projected vectors (in AO basis) are X2 = C2 * V * Sigma^{-1} * U^t, where Sigma^{-1} is the generalized inverse // // Check integral factories if (!space1->integral()->equiv(space2->integral())) throw ProgrammingError("Two MOIndexSpaces use incompatible Integral factories"); // Both spaces must be ordered in the same way if (space1->moorder() != space2->moorder()) throw std::runtime_error("R12IntEvalInfo::orthog_comp() -- space1 and space2 are ordered differently "); ExEnv::out0() << indent << "Projecting " << space1->name() << " onto " << space2->name() << " exactly to obtain space " << name << endl << incindent; // C12 = C1 * S12 * C2 RefSCMatrix C12; compute_overlap_ints(space1,space2,C12); C12.print("C12 matrix"); // Check dimensions of C12 to make sure that projection makes sense Ref ao_matrixkit = space1->basis()->matrixkit(); Ref so_matrixkit = space1->basis()->so_matrixkit(); int nblocks = C12.nblock(); const double toler = lindep_tol; double min_sigma = 1.0; double max_sigma = 0.0; int* nvec_per_block = new int[nblocks]; // projected vectors are a matrix of nvecs by nbasis2 // we don't know nvecs yet, so use rank1 RefSCMatrix C1 = space1->coefs(); RefSCMatrix C2 = space2->coefs(); int rank1 = space1->coefs()->ncol(); int nbasis2 = C2->nrow(); double* vecs = new double[rank1 * nbasis2]; int nweakovlp = 0; int v_offset = 0; for(int b=0; bblocks()->subdim(b); RefSCDimension cold = C12.coldim()->blocks()->subdim(b); int nrow = rowd.n(); int ncol = cold.n(); // Cannot project if rank of the target space is smaller than the rank of the source space if (nrow > ncol) throw std::runtime_error("R12IntEvalInfo::svd_project() -- rank of the target space is smaller than the rank of the source space"); if (nrow && ncol) { RefSCMatrix C12_b = C12.block(b); RefSCDimension sigd = rowd; int nsigmas = sigd.n(); RefSCMatrix U(rowd, rowd, ao_matrixkit); RefSCMatrix V(cold, cold, ao_matrixkit); RefDiagSCMatrix Sigma(sigd, ao_matrixkit); // // Compute C12 = U * Sigma * V // /* C12_b.svd(U,Sigma,V); */ exp::lapack_svd(C12_b,U,Sigma,V); // Figure out how many sigmas are too small, i.e. how many vectors from space2 overlap // only weakly with space1. // NOTE: Sigma values returned by svd() are in descending order int nzeros = 0; for(int s=0; s max_sigma) max_sigma = sigma; } // number of vectors that span the projected space nvec_per_block[b] = nsigmas - nzeros; if (nvec_per_block[b] < nrow) throw std::runtime_error("R12IntEvalInfo::gen_project() -- space 1 is not fully spanned by space 2"); nweakovlp += nzeros + ncol - nrow; if (nvec_per_block[b]) { int s_first = 0; int s_last = nvec_per_block[b]-1; RefSCMatrix vtmp = V.get_subblock(s_first,s_last,0,ncol-1); RefSCDimension rowdim = vtmp.rowdim(); RefDiagSCMatrix stmp = vtmp.kit()->diagmatrix(rowdim); for(int i=0; i1?"s":"") << " in " << space2->name() << " did not overlap significantly with " << space1->name() << "." << endl; ExEnv::out0() << indent << "n(basis): "; for (int i=0; iblocks()->nblock(); i++) { ExEnv::out0() << scprintf(" %5d", proj.coldim()->blocks()->size(i)); } ExEnv::out0() << endl; ExEnv::out0() << indent << "Maximum singular value = " << max_sigma << endl << indent << "Minimum singular value = " << min_sigma << endl; ExEnv::out0() << decindent; delete[] vecs; delete[] nvec_per_block; Ref proj_space = new MOIndexSpace(name,proj,space2->basis(),space2->integral()); RefSCMatrix S12; compute_overlap_ints(space1,proj_space,S12); S12.print("Check: overlap between space1 and projected space"); return proj_space; } ///////////////////////////////////////////////////////////////////////////// // Local Variables: // mode: c++ // c-file-style: "CLJ-CONDENSED" // End: