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 "dft.h" 35 #include "util.h" 36 //#include <moldft/xc/f2c.h> 37 #include <vector> 38 #include "poperator.h" 39 #include "libxc.h" 40 41 typedef madness::Vector<double,3> coordT; 42 43 namespace madness 44 { 45 //*************************************************************************** 46 template <typename T, int NDIM> DFTNuclearChargeDensityOp(World & world,funcT rhon,double coeff,double thresh,bool periodic)47 DFTNuclearChargeDensityOp<T,NDIM>::DFTNuclearChargeDensityOp(World& world, funcT rhon, 48 double coeff, double thresh, bool periodic) : EigSolverOp<T,NDIM>(world, coeff, thresh) 49 { 50 // Message for the matrix element output 51 this->messageME("NuclearChargeDensityOp"); 52 _rhon = rhon; 53 SeparatedConvolution<T,NDIM>* cop; 54 if (periodic) 55 { 56 Tensor<double> L = FunctionDefaults<NDIM>::get_cell_width(); 57 cop = PeriodicCoulombOpPtr<T,NDIM>(world, FunctionDefaults<NDIM>::get_k(), 58 1e-8, thresh * 0.1, L); 59 } 60 else 61 { 62 cop = 63 CoulombOperatorPtr<T,NDIM>(world, 64 FunctionDefaults<NDIM>::get_k(), 1e-8, thresh * 0.1); 65 } 66 // Apply operator to get potential 67 _Vnuc = apply(*cop, rhon); 68 delete cop; 69 } 70 //*************************************************************************** 71 72 //*************************************************************************** 73 template <typename T, int NDIM> DFTNuclearPotentialOp(World & world,funcT V,double coeff,double thresh)74 DFTNuclearPotentialOp<T,NDIM>::DFTNuclearPotentialOp(World& world, funcT V, 75 double coeff, double thresh) : EigSolverOp<T,NDIM>(world, coeff, thresh) 76 { 77 // Message for the matrix element output 78 this->messageME("NuclearPotentialOp"); 79 _V = copy(V); 80 } 81 //*************************************************************************** 82 83 //************************************************************************* 84 template <typename T, int NDIM> DFTCoulombOp(World & world,double coeff,double thresh)85 DFTCoulombOp<T,NDIM>::DFTCoulombOp(World& world, double coeff, 86 double thresh) : EigSolverOp<T,NDIM>(world, coeff, thresh) 87 { 88 // Message for the matrix element output 89 this->messageME("CoulombOp"); 90 // For now, no spin polarized 91 _spinpol = false; 92 // Create Coulomb operator 93 _cop = CoulombOperatorPtr<T,NDIM>(world, FunctionDefaults<NDIM>::get_k(), 94 1e-4, thresh); 95 // Initialize potential 96 _Vc = FunctionFactory<T,NDIM>(world); 97 } 98 //************************************************************************* 99 100 //************************************************************************* 101 template <typename T, int NDIM> DFTCoulombPeriodicOp(World & world,funcT rhon,double coeff,double thresh)102 DFTCoulombPeriodicOp<T,NDIM>::DFTCoulombPeriodicOp(World& world, funcT rhon, double coeff, 103 double thresh) : EigSolverOp<T,NDIM>(world, coeff, thresh) 104 { 105 // Nuclear charge density 106 _rhon = rhon; 107 // Message for the matrix element output 108 this->messageME("PeriodicCoulombOp"); 109 // For now, no spin polarized 110 _spinpol = false; 111 // Create Coulomb operator 112 Tensor<double> L = FunctionDefaults<NDIM>::get_cell_width(); 113 _cop = PeriodicCoulombOpPtr<T,NDIM>(world, FunctionDefaults<NDIM>::get_k(), 114 1e-8, thresh * 0.1, L); 115 } 116 //************************************************************************* 117 118 //*************************************************************************** 119 template <typename T, int NDIM> prepare_op(Function<double,NDIM> rho)120 void DFTCoulombOp<T,NDIM>::prepare_op(Function<double,NDIM> rho) 121 { 122 _Vc = apply(*_cop, rho); 123 } 124 //*************************************************************************** 125 126 //*************************************************************************** 127 template <typename T, int NDIM> prepare_op(Function<double,NDIM> rho)128 void DFTCoulombPeriodicOp<T,NDIM>::prepare_op(Function<double,NDIM> rho) 129 { 130 _Vc = apply(*_cop, rho + _rhon); 131 } 132 //*************************************************************************** 133 134 //*************************************************************************** 135 template <typename T, int NDIM> op_r(const funcT & rho,const funcT & psi)136 Function<T,NDIM> DFTNuclearChargeDensityOp<T,NDIM>::op_r(const funcT& rho, const funcT& psi) 137 { 138 funcT rfunc = _Vnuc * psi; 139 return rfunc; 140 } 141 //*************************************************************************** 142 143 //*************************************************************************** 144 template <typename T, int NDIM> op_r(const funcT & rho,const funcT & psi)145 Function<T,NDIM> DFTNuclearPotentialOp<T,NDIM>::op_r(const funcT& rho, const funcT& psi) 146 { 147 funcT rfunc = _V * psi; 148 return rfunc; 149 } 150 //*************************************************************************** 151 152 //************************************************************************* 153 template <typename T, int NDIM> op_r(const funcT & rho,const funcT & psi)154 Function<T,NDIM> DFTCoulombOp<T,NDIM>::op_r(const funcT& rho, const funcT& psi) 155 { 156 funcT rfunc = _Vc * psi; 157 return rfunc; 158 } 159 //************************************************************************* 160 161 //************************************************************************* 162 template <typename T, int NDIM> op_r(const funcT & rho,const funcT & psi)163 Function<T,NDIM> DFTCoulombPeriodicOp<T,NDIM>::op_r(const funcT& rho, const funcT& psi) 164 { 165 funcT rfunc = _Vc * psi; 166 return rfunc; 167 } 168 //************************************************************************* 169 170 //*************************************************************************** 171 template <typename T, int NDIM> XCFunctionalLDA(World & world,double coeff,double thresh)172 XCFunctionalLDA<T,NDIM>::XCFunctionalLDA(World& world, double coeff, double thresh) 173 : EigSolverOp<T,NDIM>(world, coeff, thresh) 174 { 175 // Message for the matrix element output 176 this->messageME("XCFunctionalLDA"); 177 } 178 //*************************************************************************** 179 180 //*************************************************************************** 181 template <typename T, int NDIM> op_r(const funcT & rho,const funcT & psi)182 Function<T,NDIM> XCFunctionalLDA<T,NDIM>::op_r(const funcT& rho, const funcT& psi) 183 { 184 Function<T,NDIM> V_rho = copy(rho); 185 V_rho.scale(0.5); 186 V_rho.unaryop(&::libxc_ldaop); 187 funcT rfunc = V_rho * psi; 188 return rfunc; 189 } 190 //*************************************************************************** 191 } 192 193 namespace madness 194 { 195 //*************************************************************************** 196 template <typename T, int NDIM> DFT(World & world,funcT vnucrhon,std::vector<funcT> phis,std::vector<double> eigs,ElectronicStructureParams params)197 DFT<T,NDIM>::DFT(World& world, funcT vnucrhon, std::vector<funcT> phis, 198 std::vector<double> eigs, ElectronicStructureParams params) 199 : _world(world), _vnucrhon(vnucrhon), _params(params) 200 { 201 202 if (world.rank() == 0 && !params.periodic) printf("DFT constructor (non-peridic) ...\n\n"); 203 if (world.rank() == 0 && params.periodic) printf("DFT constructor (periodic) ...\n\n"); 204 205 // Create ops list 206 std::vector<EigSolverOp<T,NDIM>*> ops; 207 // Add nuclear potential to ops list 208 // ops.push_back(new DFTNuclearPotentialOp<T,NDIM>(world, V, 1.0, thresh)); 209 if (params.periodic) 210 { 211 ops.push_back(new DFTCoulombPeriodicOp<T,NDIM>(world, vnucrhon, 1.0, params.thresh)); 212 } 213 else 214 { 215 if (params.ispotential) 216 { 217 ops.push_back(new DFTNuclearPotentialOp<T,NDIM>(world, vnucrhon, 1.0, params.thresh)); 218 } 219 else 220 { 221 ops.push_back(new DFTNuclearChargeDensityOp<T,NDIM>(world, vnucrhon, 1.0, params.thresh, false)); 222 } 223 ops.push_back(new DFTCoulombOp<T,NDIM>(world, 1.0, params.thresh)); 224 } 225 _xcfunc = new XCFunctionalLDA<T,NDIM>(world, 1.0, params.thresh); 226 ops.push_back(_xcfunc); 227 228 // Create solver 229 if (params.periodic) 230 { 231 std::vector<kvecT> kpoints; 232 kvecT gammap(0.0); 233 kpoints.push_back(gammap); 234 _solver = new EigSolver<T,NDIM>(world, phis, eigs, ops, kpoints, params); 235 } 236 else 237 { 238 _solver = new EigSolver<T,NDIM>(world, phis, eigs, ops, params); 239 } 240 _solver->addObserver(this); 241 } 242 //*************************************************************************** 243 244 //*************************************************************************** 245 template <typename T, int NDIM> ~DFT()246 DFT<T,NDIM>::~DFT() 247 { 248 delete _solver; 249 } 250 //*************************************************************************** 251 252 //*************************************************************************** 253 template <typename T, int NDIM> solve(int maxits)254 void DFT<T,NDIM>::solve(int maxits) 255 { 256 _solver->multi_solve(maxits); 257 } 258 //*************************************************************************** 259 260 //*************************************************************************** 261 template <typename T, int NDIM> calculate_ke_sp(funcT psi,bool periodic)262 double DFT<T,NDIM>::calculate_ke_sp(funcT psi, bool periodic) 263 { 264 // Do calculation 265 double kenergy = 0.0; 266 for (int axis = 0; axis < 3; axis++) 267 { 268 funcT dpsi = diff(psi, axis); 269 kenergy += 0.5 * inner(dpsi, dpsi); 270 } 271 return kenergy; 272 } 273 //*************************************************************************** 274 275 //*************************************************************************** 276 template <typename T, int NDIM> calculate_tot_ke_sp(const std::vector<funcT> & phis,bool spinpol,bool periodic)277 double DFT<T,NDIM>::calculate_tot_ke_sp(const std::vector<funcT>& phis, bool spinpol, 278 bool periodic) 279 { 280 double tot_ke = 0.0; 281 for (unsigned int pi = 0; pi < phis.size(); pi++) 282 { 283 // Get psi from collection 284 const funcT psi = phis[pi]; 285 // Calculate kinetic energy contribution from psi 286 tot_ke += calculate_ke_sp(psi); 287 } 288 if (!spinpol) tot_ke *= 2.0; 289 return tot_ke; 290 } 291 //*************************************************************************** 292 293 //*************************************************************************** 294 template <typename T, int NDIM> calculate_tot_pe_sp(const World & world,const Function<double,NDIM> & rho,const Function<double,NDIM> & vnucrhon,bool spinpol,const double thresh,bool periodic,bool ispotential)295 double DFT<T,NDIM>::calculate_tot_pe_sp(const World& world, const Function<double,NDIM>& rho, 296 const Function<double,NDIM>& vnucrhon, bool spinpol, const double thresh, bool periodic, 297 bool ispotential) 298 { 299 funcT vnuc = copy(vnucrhon); 300 if (!ispotential) 301 { 302 // Create Coulomb operator 303 SeparatedConvolution<T,NDIM>* op = 0; 304 if (periodic) 305 { 306 Tensor<double> L = FunctionDefaults<NDIM>::get_cell_width(); 307 op = CoulombOperatorPtr<T,NDIM>(const_cast<World&>(world), 308 FunctionDefaults<NDIM>::get_k(), 1e-8, thresh * 0.1); 309 } 310 else 311 { 312 op = CoulombOperatorPtr<T,NDIM>(const_cast<World&>(world), 313 FunctionDefaults<NDIM>::get_k(), 1e-8, thresh * 0.1); 314 } 315 // Apply Coulomb operator and trace with the density 316 vnuc = apply(*op, vnucrhon); 317 delete op; 318 } 319 double tot_pe = inner(vnuc, rho); 320 return tot_pe; 321 } 322 //*************************************************************************** 323 324 //*************************************************************************** 325 template <typename T, int NDIM> calculate_tot_coulomb_energy(const World & world,const Function<double,NDIM> & rho,bool spinpol,const double thresh,bool periodic)326 double DFT<T,NDIM>::calculate_tot_coulomb_energy(const World& world, const Function<double, NDIM>& rho, 327 bool spinpol, const double thresh, bool periodic) 328 { 329 // Create Coulomb operator 330 SeparatedConvolution<T,NDIM>* op; 331 if (periodic) 332 { 333 Tensor<double> L = FunctionDefaults<NDIM>::get_cell_width(); 334 op = PeriodicCoulombOpPtr<T,NDIM>(const_cast<World&>(world), 335 FunctionDefaults<NDIM>::get_k(), 1e-4, thresh, L); 336 } 337 else 338 { 339 op = CoulombOperatorPtr<T,NDIM>(const_cast<World&>(world), 340 FunctionDefaults<NDIM>::get_k(), 1e-4, thresh); 341 } 342 // Apply Coulomb operator and trace with the density 343 funcT Vc = apply(*op, rho); 344 345 double tot_ce = 0.5 * Vc.inner(rho); 346 delete op; 347 return tot_ce; 348 } 349 //*************************************************************************** 350 351 //*************************************************************************** 352 template <typename T, int NDIM> calculate_tot_xc_energy(const Function<double,NDIM> & rho)353 double DFT<T,NDIM>::calculate_tot_xc_energy(const Function<double, NDIM>& rho) 354 { 355 funcT enefunc = copy(rho); 356 enefunc.scale(0.5); 357 enefunc.unaryop(&::ldaeop); 358 return enefunc.trace(); 359 } 360 //*************************************************************************** 361 362 //*************************************************************************** 363 template <typename T, int NDIM> iterateOutput(const std::vector<funcT> & phis,const std::vector<double> & eigs,const Function<double,NDIM> & rho,const int & iter,bool periodic)364 void DFT<T,NDIM>::iterateOutput(const std::vector<funcT>& phis, 365 const std::vector<double>& eigs, const Function<double, NDIM>& rho, 366 const int& iter, bool periodic) 367 { 368 if (iter%3 == 0) 369 { 370 if (world().rank() == 0) printf("Calculating energies ...\n"); 371 if (world().rank() == 0) printf("Calculating KE ...\n"); 372 double ke = DFT::calculate_tot_ke_sp(phis, false, periodic); 373 // if (world().rank() == 0) printf("Calculating PE and CE...\n"); 374 double pe = DFT::calculate_tot_pe_sp(_world, rho, _vnucrhon, _params.spinpol, _params.thresh, _params.periodic, _params.ispotential); 375 if (world().rank() == 0) printf("Calculating CE ...\n"); 376 double ce = DFT::calculate_tot_coulomb_energy(_world, rho, _params.spinpol, _params.thresh, _params.periodic); 377 if (world().rank() == 0) printf("Calculating EE ...\n"); 378 double xce = DFT::calculate_tot_xc_energy(rho); 379 if (world().rank() == 0) printf("Calculating NE ...\n"); 380 double ne = 0.0; 381 if (world().rank() == 0) printf("Kinetic energy:\t\t\t\t %.8f\n", ke); 382 if (world().rank() == 0) printf("Potential energy:\t\t\t %.8f\n", pe); 383 if (world().rank() == 0) printf("Coulomb energy:\t\t\t\t %.8f\n", ce); 384 if (world().rank() == 0) printf("XC energy:\t\t\t\t %.8f\n", xce); 385 if (world().rank() == 0) printf("Total energy:\t\t\t\t %.8f\n", ke + pe + ce + xce + ne); 386 if (world().rank() == 0) printf("gs ene\t\t\t\t\t%.4f\n", eigs[0]); 387 if (world().rank() == 0) printf("1st es ene\t\t\t\t%.4f\n", eigs[1]); 388 T mtxe = matrix_element(phis[0], phis[0]); 389 if (world().rank() == 0) printf("\nKS matrix element:\t\t\t%.8f\n\n", mtxe); 390 print_matrix_elements(phis[0], phis[0]); 391 } 392 } 393 //*************************************************************************** 394 395 //*************************************************************************** 396 // template class DFT<double, 1>; 397 // template class DFT<double, 2>; 398 template class DFT<double, 3>; 399 400 401 // template class DFT< std::complex<double>, 3>; 402 //*************************************************************************** 403 } 404