// // clscf.cc --- implementation of the closed shell SCF class // // Copyright (C) 1996 Limit Point Systems, Inc. // // Author: Edward Seidl // Maintainer: LPS // // 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 #include #include #include #include #include #include using namespace std; using namespace sc; namespace sc { /////////////////////////////////////////////////////////////////////////// // CLSCF static ClassDesc CLSCF_cd( typeid(CLSCF),"CLSCF",2,"public SCF", 0, 0, 0); CLSCF::CLSCF(StateIn& s) : SavableState(s), SCF(s), cl_fock_(this) { cl_fock_.result_noupdate() = basis_matrixkit()->symmmatrix(so_dimension()); cl_fock_.restore_state(s); cl_fock_.result_noupdate().restore(s); s.get(user_occupations_); s.get(tndocc_); s.get(nirrep_); s.get(ndocc_); if (s.version(::class_desc()) >= 2) { s.get(initial_ndocc_); most_recent_pg_ << SavableState::restore_state(s); } else { initial_ndocc_ = new int[nirrep_]; memcpy(initial_ndocc_, ndocc_, sizeof(int)*nirrep_); } // now take care of memory stuff init_mem(2); } CLSCF::CLSCF(const Ref& keyval) : SCF(keyval), cl_fock_(this) { int i; int me = scf_grp_->me(); cl_fock_.compute()=0; cl_fock_.computed()=0; // calculate the total nuclear charge double Znuc=molecule()->nuclear_charge(); // check to see if this is to be a charged molecule double charge = keyval->doublevalue("total_charge"); int nelectron = (int)((Znuc-charge+1.0e-4)); // now see if ndocc was specified if (keyval->exists("ndocc")) { tndocc_ = keyval->intvalue("ndocc"); } else { tndocc_ = nelectron/2; if (nelectron%2 && me==0) { ExEnv::err0() << endl << indent << "CLSCF::init: Warning, there's a leftover electron.\n" << incindent << indent << "total_charge = " << charge << endl << indent << "total nuclear charge = " << Znuc << endl << indent << "ndocc_ = " << tndocc_ << endl << decindent; } } ExEnv::out0() << endl << indent << "CLSCF::init: total charge = " << Znuc-2*tndocc_ << endl << endl; nirrep_ = molecule()->point_group()->char_table().ncomp(); ndocc_ = read_occ(keyval, "docc", nirrep_); if (ndocc_) { user_occupations_=1; initial_ndocc_ = new int[nirrep_]; memcpy(initial_ndocc_, ndocc_, sizeof(int)*nirrep_); } else { initial_ndocc_=0; ndocc_=0; user_occupations_=0; set_occupations(0); } ExEnv::out0() << indent << "docc = ["; for (i=0; i < nirrep_; i++) ExEnv::out0() << " " << ndocc_[i]; ExEnv::out0() << " ]\n"; ExEnv::out0() << indent << "nbasis = " << basis()->nbasis() << endl; // check to see if this was done in SCF(keyval) if (!keyval->exists("maxiter")) maxiter_ = 100; // now take care of memory stuff init_mem(2); } CLSCF::~CLSCF() { if (ndocc_) { delete[] ndocc_; ndocc_=0; } delete[] initial_ndocc_; } void CLSCF::save_data_state(StateOut& s) { SCF::save_data_state(s); cl_fock_.save_data_state(s); cl_fock_.result_noupdate().save(s); s.put(user_occupations_); s.put(tndocc_); s.put(nirrep_); s.put(ndocc_,nirrep_); s.put(initial_ndocc_,nirrep_); SavableState::save_state(most_recent_pg_.pointer(),s); } double CLSCF::occupation(int ir, int i) { if (i < ndocc_[ir]) return 2.0; return 0.0; } int CLSCF::n_fock_matrices() const { return 1; } RefSymmSCMatrix CLSCF::fock(int n) { if (n > 0) { ExEnv::err0() << indent << "CLSCF::fock: there is only one fock matrix, " << scprintf("but fock(%d) was requested\n",n); abort(); } return cl_fock_.result(); } int CLSCF::spin_polarized() { return 0; } void CLSCF::print(ostream&o) const { SCF::print(o); o << indent << "CLSCF Parameters:\n" << incindent << indent << "charge = " << molecule()->nuclear_charge()-2*tndocc_ << endl << indent << "ndocc = " << tndocc_ << endl << indent << "docc = ["; for (int i=0; i < nirrep_; i++) o << " " << ndocc_[i]; o << " ]" << endl << decindent << endl; } ////////////////////////////////////////////////////////////////////////////// void CLSCF::set_occupations(const RefDiagSCMatrix& ev) { if (user_occupations_ || (initial_ndocc_ && ev.null())) { if (form_occupations(ndocc_, initial_ndocc_)) { most_recent_pg_ = new PointGroup(molecule()->point_group()); return; } ExEnv::out0() << indent << "CLSCF: WARNING: reforming occupation vector from scratch" << endl; } if (nirrep_==1) { delete[] ndocc_; ndocc_=new int[1]; ndocc_[0]=tndocc_; if (!initial_ndocc_) { initial_ndocc_=new int[1]; initial_ndocc_[0]=tndocc_; } return; } int i,j; RefDiagSCMatrix evals; if (ev.null()) { initial_vector(0); evals = eigenvalues_.result_noupdate(); } else evals = ev; // first convert evals to something we can deal with easily BlockedDiagSCMatrix *evalsb = require_dynamic_cast(evals, "CLSCF::set_occupations"); double **vals = new double*[nirrep_]; for (i=0; i < nirrep_; i++) { int nf=oso_dimension()->blocks()->size(i); if (nf) { vals[i] = new double[nf]; evalsb->block(i)->convert(vals[i]); } else { vals[i] = 0; } } // now loop to find the tndocc_ lowest eigenvalues and populate those // MO's int *newocc = new int[nirrep_]; memset(newocc,0,sizeof(int)*nirrep_); for (i=0; i < tndocc_; i++) { // find lowest eigenvalue int lir=0,ln=0; double lowest=999999999; for (int ir=0; ir < nirrep_; ir++) { int nf=oso_dimension()->blocks()->size(ir); if (!nf) continue; for (j=0; j < nf; j++) { if (vals[ir][j] < lowest) { lowest=vals[ir][j]; lir=ir; ln=j; } } } vals[lir][ln]=999999999; newocc[lir]++; } // get rid of vals for (i=0; i < nirrep_; i++) if (vals[i]) delete[] vals[i]; delete[] vals; if (!ndocc_) { ndocc_=newocc; } else if (most_recent_pg_.nonnull() && most_recent_pg_->equiv(molecule()->point_group())) { // test to see if newocc is different from ndocc_ for (i=0; i < nirrep_; i++) { if (ndocc_[i] != newocc[i]) { ExEnv::err0() << indent << "CLSCF::set_occupations: WARNING!!!!\n" << incindent << indent << scprintf("occupations for irrep %d have changed\n",i+1) << indent << scprintf("ndocc was %d, changed to %d", ndocc_[i],newocc[i]) << endl << decindent; } } memcpy(ndocc_,newocc,sizeof(int)*nirrep_); delete[] newocc; } if (!initial_ndocc_ || initial_pg_->equiv(molecule()->point_group())) { delete[] initial_ndocc_; initial_ndocc_ = new int[nirrep_]; memcpy(initial_ndocc_,ndocc_,sizeof(int)*nirrep_); } most_recent_pg_ = new PointGroup(molecule()->point_group()); } void CLSCF::symmetry_changed() { SCF::symmetry_changed(); cl_fock_.result_noupdate()=0; nirrep_ = molecule()->point_group()->char_table().ncomp(); set_occupations(0); } ////////////////////////////////////////////////////////////////////////////// // // scf things // void CLSCF::init_vector() { init_threads(); // initialize the two electron integral classes ExEnv::out0() << indent << "integral intermediate storage = " << integral()->storage_used() << " bytes" << endl; ExEnv::out0() << indent << "integral cache = " << integral()->storage_unused() << " bytes" << endl; // allocate storage for other temp matrices cl_dens_ = hcore_.clone(); cl_dens_.assign(0.0); cl_dens_diff_ = hcore_.clone(); cl_dens_diff_.assign(0.0); // gmat is in AO basis cl_gmat_ = basis()->matrixkit()->symmmatrix(basis()->basisdim()); cl_gmat_.assign(0.0); if (cl_fock_.result_noupdate().null()) { cl_fock_ = hcore_.clone(); cl_fock_.result_noupdate().assign(0.0); } // set up trial vector initial_vector(1); oso_scf_vector_ = oso_eigenvectors_.result_noupdate(); if (accumddh_.nonnull()) accumddh_->init(this); } void CLSCF::done_vector() { done_threads(); if (accumddh_.nonnull()) { accumddh_->print_summary(); accumddh_->done(); } cl_gmat_ = 0; cl_dens_ = 0; cl_dens_diff_ = 0; oso_scf_vector_ = 0; } void CLSCF::reset_density() { cl_gmat_.assign(0.0); cl_dens_diff_.assign(cl_dens_); } RefSymmSCMatrix CLSCF::density() { if (!density_.computed()) { RefSymmSCMatrix dens(so_dimension(), basis_matrixkit()); so_density(dens, 2.0); dens.scale(2.0); density_ = dens; // only flag the density as computed if the calc is converged if (!value_needed()) density_.computed() = 1; } return density_.result_noupdate(); } double CLSCF::new_density() { // copy current density into density diff and scale by -1. later we'll // add the new density to this to get the density difference. cl_dens_diff_.assign(cl_dens_); cl_dens_diff_.scale(-1.0); so_density(cl_dens_, 2.0); cl_dens_.scale(2.0); cl_dens_diff_.accumulate(cl_dens_); Ref sp(new SCElementScalarProduct); cl_dens_diff_.element_op(sp.pointer(), cl_dens_diff_); double delta = sp->result(); delta = sqrt(delta/i_offset(cl_dens_diff_.n())); return delta; } double CLSCF::scf_energy() { RefSymmSCMatrix t = cl_fock_.result_noupdate().copy(); t.accumulate(hcore_); SCFEnergy *eop = new SCFEnergy; eop->reference(); Ref op = eop; t.element_op(op,cl_dens_); op=0; eop->dereference(); double eelec = eop->result(); delete eop; return eelec; } Ref CLSCF::extrap_data() { Ref data = new SymmSCMatrixSCExtrapData(cl_fock_.result_noupdate()); return data; } RefSymmSCMatrix CLSCF::effective_fock() { // just return MO fock matrix. use fock() instead of cl_fock_ just in // case this is called from someplace outside SCF::compute_vector() RefSymmSCMatrix mofock(oso_dimension(), basis_matrixkit()); mofock.assign(0.0); if (debug_ > 1) { fock(0).print("CL Fock matrix in SO basis"); } // use eigenvectors if scf_vector_ is null if (oso_scf_vector_.null()) mofock.accumulate_transform(eigenvectors(), fock(0), SCMatrix::TransposeTransform); else mofock.accumulate_transform(so_to_orthog_so().t() * oso_scf_vector_, fock(0), SCMatrix::TransposeTransform); return mofock; } ////////////////////////////////////////////////////////////////////////////// void CLSCF::init_gradient() { // presumably the eigenvectors have already been computed by the time // we get here oso_scf_vector_ = oso_eigenvectors_.result_noupdate(); } void CLSCF::done_gradient() { cl_dens_=0; oso_scf_vector_ = 0; } ///////////////////////////////////////////////////////////////////////////// class CLLag : public BlockedSCElementOp { private: CLSCF *scf_; public: CLLag(CLSCF* s) : scf_(s) {} ~CLLag() {} int has_side_effects() { return 1; } void process(SCMatrixBlockIter& bi) { int ir=current_block(); for (bi.reset(); bi; bi++) { double occi = scf_->occupation(ir,bi.i()); if (occi==0.0) bi.set(0.0); } } }; RefSymmSCMatrix CLSCF::lagrangian() { // the MO lagrangian is just the eigenvalues of the occupied MO's RefSymmSCMatrix mofock = effective_fock(); Ref op = new CLLag(this); mofock.element_op(op); // transform MO lagrangian to SO basis RefSymmSCMatrix so_lag(so_dimension(), basis_matrixkit()); so_lag.assign(0.0); so_lag.accumulate_transform(so_to_orthog_so().t() * oso_scf_vector_, mofock); // and then from SO to AO Ref pl = integral()->petite_list(); RefSymmSCMatrix ao_lag = pl->to_AO_basis(so_lag); ao_lag->scale(-2.0); return ao_lag; } RefSymmSCMatrix CLSCF::gradient_density() { cl_dens_ = basis_matrixkit()->symmmatrix(so_dimension()); cl_dens_.assign(0.0); so_density(cl_dens_, 2.0); cl_dens_.scale(2.0); Ref pl = integral()->petite_list(basis()); cl_dens_ = pl->to_AO_basis(cl_dens_); return cl_dens_; } ///////////////////////////////////////////////////////////////////////////// void CLSCF::init_hessian() { } void CLSCF::done_hessian() { } ///////////////////////////////////////////////////////////////////////////// void CLSCF::two_body_deriv_hf(double * tbgrad, double exchange_fraction) { int i; int na3 = molecule()->natom()*3; int nthread = threadgrp_->nthread(); tim_enter("setup"); Ref m = new SCElementMaxAbs; cl_dens_.element_op(m.pointer()); double pmax = m->result(); m=0; double **grads = new double*[nthread]; Ref *tbis = new Ref[nthread]; for (i=0; i < nthread; i++) { tbis[i] = integral()->electron_repulsion_deriv(); grads[i] = new double[na3]; memset(grads[i], 0, sizeof(double)*na3); } Ref pl = integral()->petite_list(); tim_change("contribution"); // now try to figure out the matrix specialization we're dealing with. // if we're using Local matrices, then there's just one subblock, or // see if we can convert P to a local matrix if (local_ || local_dens_) { double *pmat; RefSymmSCMatrix ptmp = get_local_data(cl_dens_, pmat, SCF::Read); LocalCLHFGradContribution l(pmat); LocalTBGrad **tblds = new LocalTBGrad*[nthread]; for (i=0; i < nthread; i++) { tblds[i] = new LocalTBGrad( l, tbis[i], pl, basis(), scf_grp_, grads[i], pmax, desired_gradient_accuracy(), nthread, i, exchange_fraction); threadgrp_->add_thread(i, tblds[i]); } tim_enter("start thread"); if (threadgrp_->start_threads() < 0) { ExEnv::err0() << indent << "CLSCF: error starting threads" << endl; abort(); } tim_exit("start thread"); tim_enter("stop thread"); if (threadgrp_->wait_threads() < 0) { ExEnv::err0() << indent << "CLSCF: error waiting for threads" << endl; abort(); } tim_exit("stop thread"); for (i=0; i < nthread; i++) { if (i) { for (int j=0; j < na3; j++) grads[0][j] += grads[i][j]; delete[] grads[i]; } delete tblds[i]; } if (scf_grp_->n() > 1) scf_grp_->sum(grads[0], na3); for (i=0; i < na3; i++) tbgrad[i] += grads[0][i]; delete[] grads[0]; delete[] tblds; delete[] grads; } // for now quit else { ExEnv::err0() << indent << "CLHF::two_body_deriv: can't do gradient yet\n"; abort(); } for (i=0; i < nthread; i++) tbis[i] = 0; delete[] tbis; tim_exit("contribution"); } ///////////////////////////////////////////////////////////////////////////// } // Local Variables: // mode: c++ // c-file-style: "ETS" // End: