1 /* 2 This file is part of MADNESS. 3 4 Copyright (C) 2007,2010 Oak Ridge National Laboratory 5 6 This program is free software; you can redistribute it and/or modify 7 it under the terms of the GNU General Public License as published by 8 the Free Software Foundation; either version 2 of the License, or 9 (at your option) any later version. 10 11 This program is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 GNU General Public License for more details. 15 16 You should have received a copy of the GNU General Public License 17 along with this program; if not, write to the Free Software 18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 20 For more information please contact: 21 22 Robert J. Harrison 23 Oak Ridge National Laboratory 24 One Bethel Valley Road 25 P.O. Box 2008, MS-6367 26 27 email: harrisonrj@ornl.gov 28 tel: 865-241-3937 29 fax: 865-572-0680 30 31 $Id$ 32 */ 33 //#define WORLD_INSTANTIATE_STATIC_TEMPLATES 34 #include "hartreefock.h" 35 36 namespace madness 37 { 38 39 //************************************************************************* 40 template <typename T, int NDIM> HartreeFockNuclearPotentialOp(World & world,funcT V,double coeff,double thresh)41 HartreeFockNuclearPotentialOp<T,NDIM>::HartreeFockNuclearPotentialOp(World& world, 42 funcT V, double coeff, double thresh) : 43 EigSolverOp<T,NDIM>(world, coeff, thresh) 44 { 45 // Message for the matrix element output 46 this->messageME("HartreeFockNuclearPotentialOp"); 47 _V = V; 48 } 49 //************************************************************************* 50 51 //************************************************************************* 52 template <typename T, int NDIM> HartreeFockCoulombOp(World & world,double coeff,double thresh)53 HartreeFockCoulombOp<T,NDIM>::HartreeFockCoulombOp(World& world, double coeff, 54 double thresh) : EigSolverOp<T,NDIM>(world, coeff, thresh) 55 { 56 // Message for the matrix element output 57 this->messageME("HartreeFockCoulombOp"); 58 } 59 //************************************************************************* 60 61 //************************************************************************* 62 template <typename T, int NDIM> HartreeFockExchangeOp(World & world,double coeff,double thresh)63 HartreeFockExchangeOp<T,NDIM>::HartreeFockExchangeOp(World& world, double coeff, 64 double thresh) : EigSolverOp<T,NDIM>(world, coeff, thresh) 65 { 66 // Message for the matrix element output 67 this->messageME("HartreeFockExchangeOp"); 68 } 69 //************************************************************************* 70 71 //************************************************************************* 72 template <typename T, int NDIM> op_r(const funcT & rho,const funcT & rhon,const funcT & psi)73 Function<T,NDIM> HartreeFockNuclearPotentialOp<T,NDIM>::op_r(const funcT& rho, const funcT& rhon, const funcT& psi) 74 { 75 funcT rfunc = _V*psi; 76 return rfunc; 77 } 78 //************************************************************************* 79 80 //************************************************************************* 81 template <typename T, int NDIM> op_r(const funcT & rho,const funcT & rhon,const funcT & psi)82 Function<T,NDIM> HartreeFockCoulombOp<T,NDIM>::op_r(const funcT& rho, const funcT& rhon, const funcT& psi) 83 { 84 // Create Coulomb operator 85 SeparatedConvolution<T,3> cop = 86 CoulombOperator<T,3>(this->world(), FunctionDefaults<3>::get_k(), 1e-4, this->thresh()); 87 // Apply the Coulomb operator 88 funcT Vc = apply(cop, rho); 89 funcT rfunc = Vc*psi; 90 return rfunc; 91 } 92 //************************************************************************* 93 94 //************************************************************************* 95 template <typename T, int NDIM> op_o(const std::vector<funcT> & phis,const funcT & rhon,const funcT & psi)96 Function<T,NDIM> HartreeFockExchangeOp<T,NDIM>::op_o(const std::vector<funcT>& phis, const funcT& rhon, const funcT& psi) 97 { 98 // Return value 99 funcT rfunc = FunctionFactory<T,3>(this->world()); 100 // Create Coulomb operator 101 SeparatedConvolution<T,NDIM> cop = CoulombOperator<T,NDIM>(this->world(), 102 FunctionDefaults<3>::get_k(), 1e-4, this->thresh()); 103 // Use the psi and pj wavefunctions to build a product so that the K 104 // operator can be applied to the wavefunction indexed by pj, NOT PSI. 105 for (typename std::vector<funcT>::const_iterator pj = phis.begin(); pj != phis.end(); ++pj) 106 { 107 // Get phi(j) from iterator 108 const funcT& phij = (*pj); 109 // NOTE that psi is involved in this calculation 110 funcT prod = phij*psi; 111 // Transform Coulomb operator into a function (stubbed) 112 prod.truncate(this->thresh()); 113 funcT Vex = apply(cop, prod); 114 // NOTE that the index is j. 115 rfunc += Vex*phij; 116 } 117 return rfunc; 118 119 } 120 //************************************************************************* 121 122 //************************************************************************* 123 template <typename T, int NDIM> HartreeFock(World & world,funcT V,std::vector<funcT> phis,std::vector<double> eigs,bool bCoulomb,bool bExchange,double thresh)124 HartreeFock<T,NDIM>::HartreeFock(World& world, funcT V, std::vector<funcT> phis, 125 std::vector<double> eigs, bool bCoulomb, bool bExchange, double thresh) 126 : _world(world), _V(V), _thresh(thresh) 127 { 128 _bCoulomb = bCoulomb; 129 _bExchange = bExchange; 130 // Create ops list 131 std::vector<EigSolverOp<T,NDIM>*> ops; 132 // Add nuclear potential to ops list 133 ops.push_back(new HartreeFockNuclearPotentialOp<T,NDIM>(world, V, 1.0, thresh)); 134 // Check for coulomb and exchange, and add as appropriate 135 if (bCoulomb) 136 { 137 ops.push_back(new HartreeFockCoulombOp<T,NDIM>(world, 2.0, thresh)); 138 } 139 if (bExchange) 140 { 141 ops.push_back(new HartreeFockExchangeOp<T,NDIM>(world, -1.0, thresh)); 142 } 143 // Create solver 144 _solver = new EigSolver<T,NDIM>(world, phis, eigs, ops, thresh, false); 145 _solver->addObserver(this); 146 } 147 //*************************************************************************** 148 149 //*************************************************************************** 150 template <typename T, int NDIM> ~HartreeFock()151 HartreeFock<T,NDIM>::~HartreeFock() 152 { 153 delete _solver; 154 } 155 //*************************************************************************** 156 157 //*************************************************************************** 158 template <typename T, int NDIM> hartree_fock(int maxits)159 void HartreeFock<T,NDIM>::hartree_fock(int maxits) 160 { 161 _solver->solve(maxits); 162 } 163 //*************************************************************************** 164 165 //*************************************************************************** 166 template <typename T, int NDIM> calculate_ke_sp(funcT psi)167 double HartreeFock<T,NDIM>::calculate_ke_sp(funcT psi) 168 { 169 double kenergy = 0.0; 170 for (int axis = 0; axis < 3; axis++) 171 { 172 funcT dpsi = diff(psi, axis); 173 kenergy += 0.5 * inner(dpsi, dpsi); 174 } 175 return kenergy; 176 } 177 //*************************************************************************** 178 179 //*************************************************************************** 180 template <typename T, int NDIM> calculate_pe_sp(funcT psi)181 double HartreeFock<T,NDIM>::calculate_pe_sp(funcT psi) 182 { 183 funcT vpsi = _V*psi; 184 return vpsi.inner(psi); 185 } 186 //*************************************************************************** 187 188 //*************************************************************************** 189 template <typename T, int NDIM> calculate_coulomb_energy(const std::vector<funcT> & phis,const funcT & psi)190 double HartreeFock<T,NDIM>::calculate_coulomb_energy(const std::vector<funcT>& phis, const funcT& psi) 191 { 192 if (include_coulomb()) 193 { 194 // Electron density 195 funcT density = FunctionFactory<T,3>(_world); 196 // Create Coulomb operator 197 SeparatedConvolution<T,NDIM> op = 198 CoulombOperator<T,3>(_world, FunctionDefaults<3>::get_k(), 1e-4, _thresh); 199 for (typename std::vector<funcT>::const_iterator pj = phis.begin(); pj != phis.end(); ++pj) 200 { 201 // Get phi(j) from iterator 202 const funcT& phij = (*pj); 203 // Compute the j-th density 204 funcT prod = phij*phij; 205 density += prod; 206 } 207 // Transform Coulomb operator into a function (stubbed) 208 density.truncate(_thresh); 209 funcT Vc = apply(op, density); 210 // Note that we are not using psi 211 // The density is built from all of the wavefunctions. The contribution 212 // psi will be subtracted out later during the exchange. 213 funcT vpsi = Vc*psi; 214 return inner(vpsi, psi); 215 } 216 return 0.0; 217 } 218 //*************************************************************************** 219 220 //*************************************************************************** 221 template <typename T, int NDIM> calculate_exchange_energy(const std::vector<funcT> & phis,const funcT & psi)222 double HartreeFock<T,NDIM>::calculate_exchange_energy(const std::vector<funcT>& phis, 223 const funcT& psi) 224 { 225 // Return value 226 funcT rfunc = FunctionFactory<double,NDIM>(world()); 227 if (include_exchange()) 228 { 229 // Create Coulomb operator 230 SeparatedConvolution<T,NDIM> op = CoulombOperator<T,NDIM>(world(), 231 FunctionDefaults<NDIM>::get_k(), 1e-4, thresh()); 232 // Use the psi and pj wavefunctions to build a product so that the K 233 // operator can be applied to the wavefunction indexed by pj, NOT PSI. 234 for (typename std::vector<funcT>::const_iterator pj = phis.begin(); pj != phis.end(); ++pj) 235 { 236 // Get phi(j) from iterator 237 const funcT& phij = (*pj); 238 // NOTE that psi is involved in this calculation 239 funcT prod = phij*psi; 240 // Transform Coulomb operator into a function (stubbed) 241 funcT Vex = apply(op, prod); 242 // NOTE that the index is j. 243 rfunc += Vex*phij; 244 } 245 } 246 return inner(rfunc, psi); 247 } 248 //*************************************************************************** 249 250 //*************************************************************************** 251 template <typename T, int NDIM> calculate_tot_ke_sp(const std::vector<funcT> & phis)252 double HartreeFock<T,NDIM>::calculate_tot_ke_sp(const std::vector<funcT>& phis) 253 { 254 double tot_ke = 0.0; 255 for (unsigned int pi = 0; pi < phis.size(); pi++) 256 { 257 // Get psi from collection 258 const funcT psi = phis[pi]; 259 // Calculate kinetic energy contribution from psi 260 tot_ke += calculate_ke_sp(psi); 261 } 262 return tot_ke; 263 } 264 //*************************************************************************** 265 266 //*************************************************************************** 267 template <typename T, int NDIM> calculate_tot_pe_sp(const std::vector<funcT> & phis)268 double HartreeFock<T,NDIM>::calculate_tot_pe_sp(const std::vector<funcT>& phis) 269 { 270 double tot_pe = 0.0; 271 for (unsigned int pi = 0; pi < phis.size(); pi++) 272 { 273 // Get psi from collection 274 const funcT psi = phis[pi]; 275 // Calculate potential energy contribution from psi 276 tot_pe += calculate_pe_sp(psi); 277 } 278 return tot_pe; 279 } 280 //*************************************************************************** 281 282 //*************************************************************************** 283 template <typename T, int NDIM> calculate_tot_coulomb_energy(const std::vector<funcT> & phis)284 double HartreeFock<T,NDIM>::calculate_tot_coulomb_energy(const std::vector<funcT>& phis) 285 { 286 double tot_ce = 0.0; 287 for (unsigned int pi = 0; pi < phis.size(); pi++) 288 { 289 // Get psi from collection 290 const funcT psi = phis[pi]; 291 // Calculate coulomb energy contribution from psi 292 tot_ce += calculate_coulomb_energy(phis, psi); 293 } 294 return tot_ce; 295 } 296 //*************************************************************************** 297 298 //*************************************************************************** 299 template <typename T, int NDIM> calculate_tot_exchange_energy(const std::vector<funcT> & phis)300 double HartreeFock<T,NDIM>::calculate_tot_exchange_energy(const std::vector<funcT>& phis) 301 { 302 double tot_ee = 0.0; 303 for (unsigned int pi = 0; pi < phis.size(); pi++) 304 { 305 // Get psi from collection 306 const funcT psi = phis[pi]; 307 // Calculate exchange energy contribution from psi 308 tot_ee += calculate_exchange_energy(phis, psi); 309 } 310 return tot_ee; 311 } 312 //*************************************************************************** 313 314 //*************************************************************************** 315 template <typename T, int NDIM> iterateOutput(const std::vector<funcT> & phis,const std::vector<double> & eigs,const funcT & rho,const int & iter)316 void HartreeFock<T,NDIM>::iterateOutput(const std::vector<funcT>& phis, 317 const std::vector<double>& eigs,const funcT& rho, const int& iter) 318 { 319 if (iter%3 == 0) 320 { 321 if (world().rank() == 0) printf("Calculating energies ...\n"); 322 if (world().rank() == 0) printf("Calculating KE ...\n"); 323 double ke = 2.0 * calculate_tot_ke_sp(phis); 324 if (world().rank() == 0) printf("Calculating PE ...\n"); 325 double pe = 2.0 * calculate_tot_pe_sp(phis); 326 if (world().rank() == 0) printf("Calculating CE ...\n"); 327 double ce = calculate_tot_coulomb_energy(phis); 328 if (world().rank() == 0) printf("Calculating EE ...\n"); 329 double ee = calculate_tot_exchange_energy(phis); 330 if (world().rank() == 0) printf("Calculating NE ...\n"); 331 double ne = 0.0; 332 if (world().rank() == 0) printf("Kinetic energy:\t\t\t %.8f\n", ke); 333 if (world().rank() == 0) printf("Potential energy:\t\t %.8f\n", pe); 334 if (world().rank() == 0) printf("Two-electron energy:\t\t %.8f\n", 2.0*ce - ee); 335 if (world().rank() == 0) printf("Total energy:\t\t\t %.8f\n", ke + pe + 2.0*ce - ee + ne); 336 if (world().rank() == 0) printf("gs eigv: = \t\t\t %.4f\n", eigs[0]); 337 } 338 } 339 //*************************************************************************** 340 } 341 //***************************************************************************** 342 343