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