1
2 #define WORLD_INSTANTIATE_STATIC_TEMPLATES
3 #include <type_traits>
4 #include <madness/mra/mra.h>
5 #include <madness/mra/operator.h>
6 #include <madness/mra/nonlinsol.h>
7
8 #include <madness/mra/lbdeux.h>
9 #include <madness/mra/qmprop.h>
10
11 #include <madness/misc/misc.h>
12 #include <madness/misc/ran.h>
13
14 #include <madness/tensor/systolic.h>
15 #include <madness/tensor/solvers.h>
16 #include <madness/tensor/elem.h>
17
18
19 #include <chem/xcfunctional.h>
20
21 #include <madness/mra/legendre.h>
22
23 #include <chem/xcfunctional.h>
24
25
26 using namespace madness;
27
28
29 typedef std::shared_ptr< WorldDCPmapInterface< Key<3> > > pmapT;
30 typedef Vector<double,3> coordT;
31 typedef std::shared_ptr< FunctionFunctorInterface<double,3> > functorT;
32 typedef Function<double,3> functionT;
33 typedef std::vector<functionT> vecfuncT;
34 typedef std::pair<vecfuncT,vecfuncT> pairvecfuncT;
35 typedef std::vector<pairvecfuncT> subspaceT;
36 typedef Tensor<double> tensorT;
37 typedef FunctionFactory<double,3> factoryT;
38 typedef SeparatedConvolution<double,3> operatorT;
39 typedef std::shared_ptr<operatorT> poperatorT;
40 typedef Function<std::complex<double>,3> complex_functionT;
41 typedef std::vector<complex_functionT> cvecfuncT;
42 typedef Convolution1D<double_complex> complex_operatorT;
43
44
45 //static const double R = 1.4; // bond length
46 //static const double L = 64.0*R; // box size
47 static const double L = 50.0; // box size
48 static const long k = 8; // wavelet order
49 static const double thresh = 1e-5; // precision
50 static const int maxiter=10;
51 static const int maxiterp=10;
52
53 std::vector< std::shared_ptr<real_derivative_3d> > gradop;
54 XCfunctional xc;
55
56 //prod/// simple projector class for 1- and 2-particle projectors
57 //prodtemplate<typename T, std::size_t NDIM>
58 //prodclass Projector {
59 //prod
60 //prod int particle_;
61 //prod std::vector<Function<T,NDIM> > p_;
62 //prod
63 //prodpublic:
64 //prod
65 //prod Projector() : p_(std::vector<Function<T,NDIM> >()) {}
66 //prod
67 //prod /// simple constructor with only one orbital to project out
68 //prod Projector(const Function<T,NDIM>& p, const int particle=0)
69 //prod : particle_(particle), p_(std::vector<Function<T,NDIM> >(1,p)) {
70 //prod MADNESS_ASSERT(particle_==0 or particle_==1);
71 //prod MADNESS_ASSERT(p_.size()>0);
72 //prod }
73 //prod
74 //prod /// constructor with a set of orbitals to project out
75 //prod Projector(const std::vector<Function<T,NDIM> >& p, const int particle=0) : particle_(particle), p_(p) {
76 //prod MADNESS_ASSERT(particle_==0 or particle_==1);
77 //prod MADNESS_ASSERT(p_.size()>0);
78 //prod }
79 //prod
80 //prod int& particle() {return particle_;}
81 //prod const int& particle() const {return particle_;}
82 //prod
83 //prod /// get a const reference to the orbitals
84 //prod const std::vector<Function<T,NDIM> >& p() const {return p_;}
85 //prod
86 //prod /// project f on p: |result> = | p><p | f>
87 //prod template<std::size_t FDIM>
88 //prod typename std::enable_if<NDIM==FDIM, Function<T,FDIM> >::type
89 //prod operator()(const Function<T,FDIM>& f) const {
90 //prod
91 //prod const double ovlp=inner(f,p_[0]);
92 //prod Function<T,NDIM> sum=ovlp*p_[0];
93 //prod
94 //prod for (unsigned int i=1; i<p_.size(); ++i) {
95 //prod const double ovlp2=inner(f,p_[i]);
96 //prod sum=(sum+ovlp2*p_[i]).truncate().reduce_rank();
97 //prod }
98 //prod return sum;
99 //prod }
100 //prod
101 //prod /// project p out of f: |result(1,2)> = sum_p | p(1)><p(1) | f(1,2)>
102 //prod template<std::size_t FDIM>
103 //prod typename std::enable_if<2*NDIM==FDIM, Function<T,FDIM> >::type
104 //prod operator()(const Function<T,FDIM>& f) const {
105 //prod real_function_6d sum=real_factory_6d(p_.begin()->world());
106 //prod for (unsigned int i=0; i<p_.size(); ++i) {
107 //prod const real_function_3d pf2=f.project_out(p_[i],particle_);
108 //prod real_function_6d tmp;
109 //prod MADNESS_EXCEPTION("Projector class: the hartree product is inaccurate -- don't use it",1);
110 //prod if (particle_==0) tmp=hartree_product(p_[i],pf2);
111 //prod else tmp=hartree_product(pf2,p_[i]);
112 //prod sum=(sum+tmp);
113 //prod }
114 //prod sum.truncate();
115 //prod return sum;
116 //prod }
117 //prod};
118
119
make_dft_energy(World & world,const vecfuncT & vf,int ispin)120 double make_dft_energy(World & world, const vecfuncT& vf, int ispin)
121 {
122 functionT vlda = multiop_values<double, xc_functional, 3>(xc_functional(xc, ispin), vf);
123 return vlda.trace();
124 }
125
make_dft_potential(World & world,const vecfuncT & vf,int ispin,int what)126 functionT make_dft_potential(World & world, const vecfuncT& vf, int ispin, int what)
127 {
128 return multiop_values<double, xc_potential, 3>(xc_potential(xc, ispin, what), vf);
129 }
130
make_dft_kernel(World & world,const vecfuncT & vf,int ispin,int what)131 functionT make_dft_kernel(World & world, const vecfuncT& vf, int ispin, int what)
132 {
133 return multiop_values<double, xc_kernel, 3>(xc_kernel(xc, ispin, what), vf);
134 }
135
136 //prodstatic double guess(const coord_3d& r) {
137 //prod const double x=r[0], y=r[1], z=r[2];
138 //prod return (exp(-sqrt(x*x+y*y+(z-R/2)*(z-R/2)+1e-8))+
139 //prod exp(-sqrt(x*x+y*y+(z+R/2)*(z+R/2)+1e-8)));
140 //prod}
141 //prod
142 //prodstatic double V(const coord_3d& r) {
143 //prod const double x=r[0], y=r[1], z=r[2];
144 //prod return -1.0/sqrt(x*x+y*y+(z-R/2)*(z-R/2)+1e-8)+
145 //prod -1.0/sqrt(x*x+y*y+(z+R/2)*(z+R/2)+1e-8);
146 //prod}
147
guess(const coord_3d & r)148 static double guess(const coord_3d& r) {
149 const double x=r[0], y=r[1], z=r[2];
150
151 //return (x*x+y*y+z*z);
152 return (2.0*exp(-sqrt(x*x+y*y+z*z+1e-8)));
153 //return (2.0*exp(-sqrt(x*x+y*y+z*z+1e-8))+1e-8);
154 }
155
V(const coord_3d & r)156 static double V(const coord_3d& r) {
157 const double x=r[0], y=r[1], z=r[2];
158 return -2.0/sqrt(x*x+y*y+z*z+1e-8);
159 }
160
rifunction(const coord_3d & r)161 double rifunction(const coord_3d& r) {
162 return r[2]; //z
163 }
164
iterate_ground(World & world,NonlinearSolver & solver,functionT & V,functionT & psi,double & eps)165 double iterate_ground(World& world, NonlinearSolver& solver,
166 functionT& V, functionT& psi,
167 double& eps) {
168 real_convolution_3d op = BSHOperator3D(world, sqrt(-2*eps), 0.001, 1e-6);
169 functionT Vpsi = (V*psi);
170 Vpsi.scale(-2.0).truncate();
171 functionT tmp = op(Vpsi).truncate();
172 double norm = tmp.norm2();
173 functionT r = tmp-psi;
174 double rnorm = r.norm2();
175 double eps_new = eps - 0.5*inner(Vpsi,r)/(norm*norm);
176 if (world.rank() == 0) {
177 print("norm=",norm," eps=",eps," err(psi)=",rnorm," err(eps)=",eps_new-eps);
178 }
179 psi = solver.update(psi, r);
180 psi.scale(1.0/psi.norm2());
181 eps = eps_new;
182 return rnorm;
183 }
184
iterate_excite(World & world,NonlinearSolver & solver,functionT & V,functionT & psi,functionT & dpsi,double & eps,functionT & ri)185 double iterate_excite(World& world, NonlinearSolver& solver,
186 functionT& V, functionT& psi,
187 functionT& dpsi, double& eps,
188 functionT& ri) {
189 real_convolution_3d du = CoulombOperator(world, 0.001, 1e-6);
190 functionT Vdpsi = (V*dpsi);
191 // LDA
192 functionT rho = psi*psi;
193 vecfuncT vf;
194 vf.push_back(rho);
195 functionT fxc = make_dft_kernel(world, vf, 0, 0).truncate();
196
197 functionT rhod = 2*dpsi*psi; //pert density
198
199 //functionT rhs = Vdpsi + 2.*psi*du(psi*dpsi) + ri*psi;
200 functionT Gampsi = 2.*psi*du(rhod) + psi*fxc*rhod*2.*constants::pi + ri*psi;
201 //Projector<double,3> rho0(psi);
202 double ovlp = inner(psi,Gampsi);
203 functionT rhs = Vdpsi + Gampsi - psi*ovlp;
204 //prod functionT rhs = Vdpsi + Gampsi ;
205 real_convolution_3d op = BSHOperator3D(world, sqrt(-2*eps), 0.001, 1e-6);
206
207 rhs.scale(-2.0).truncate();
208 //functionT r = op(rhs)*.7+dpsi*.3 - dpsi;
209 functionT r = op(rhs) - dpsi;
210 dpsi = solver.update(dpsi, r);
211 dpsi.truncate();
212 double dpsi_error = r.norm2();
213 if (world.rank() == 0) {
214 //printf("dpsi error = %f \t ovl = %f \n", dpsi_error, ovlp);
215 printf("dpsi error = %f \n", dpsi_error);
216 }
217 return dpsi_error;
218 }
219
220 //prod// This class is used to store information for the
221 //prod// non-linear solver used by the dynamic polarizability
222 //prodstruct F {
223 //prod functionT x, y;
224 //prod
225 //prod F(const functionT& x, const functionT& y)
226 //prod : x(x), y(y)
227 //prod {}
228 //prod
229 //prod F operator-(const F& b) {
230 //prod return F(x-b.x, y-b.y);
231 //prod }
232 //prod
233 //prod F operator+=(const F& b) { // Operator+= necessary
234 //prod x += b.x; y += b.y;
235 //prod return *this;
236 //prod }
237 //prod
238 //prod F operator*(double a) { // Scale by a constant necessary
239 //prod return F(x*a,y*a);
240 //prod }
241 //prod};
242 //prod
243 //proddouble inner(const F& a, const F& b) {
244 //prod return inner(a.x,b.x) + inner(a.y,b.y);
245 //prod}
246 //prod
247 //prod// The default constructor for functions does not initialize
248 //prod// them to any value, but the solver needs functions initialized
249 //prod// to zero for which we also need the world object.
250 //prodstruct allocator {
251 //prod World& world;
252 //prod
253 //prod allocator(World& world) : world(world) {}
254 //prod
255 //prod F operator()() {
256 //prod return F(functionT(world),functionT(world));
257 //prod }
258 //prod};
259 //prod
260 //prod
261 //prodtemplate <class solverT>
262 //proddouble iterate_xy(World& world, solverT& solver, functionT& V,
263 //prod functionT& psi, double& eps,
264 //prod functionT& ri, functionT& x,
265 //prod functionT& y, double& omega) {
266 //prod real_convolution_3d gOp= BSHOperator3D(world, sqrt(-2*eps), 0.001, 1e-6);
267 //prod real_convolution_3d uop = CoulombOperator(world, 0.001, 1e-6);
268 //prod
269 //prod functionT gp2 = uop(y*psi);
270 //prod functionT gp3 = uop(x*psi);
271 //prod// LDA
272 //prod vecfuncT vf;
273 //prod functionT rho = psi*psi;
274 //prod vf.push_back(rho);
275 //prod functionT fxc = make_dft_kernel(world, vf, 0, 0);
276 //prod
277 //prod functionT pp = (gp3 + gp2 + fxc*x + fxc*y + ri) * psi;
278 //prod
279 //prod functionT xrhs = V*x + pp;
280 //prod x.truncate();
281 //prod xrhs.gaxpy(1.0, x, -omega,false);
282 //prod
283 //prod xrhs.scale(-2.0).truncate();
284 //prod functionT new_x = gOp(xrhs).truncate();
285 //prod new_x = new_x - inner(psi,new_x)*psi;
286 //prod double xerr = (x - new_x).norm2();
287 //prod
288 //prod functionT yrhs = V*y + pp;
289 //prod y.truncate();
290 //prod yrhs.gaxpy(1.0, y, omega,false);
291 //prod
292 //prod yrhs.scale(-2.0).truncate();
293 //prod functionT new_y = gOp(yrhs).truncate();
294 //prod new_y = new_y - inner(psi,new_y)*psi;
295 //prod double yerr = (y - new_y).norm2();
296 //prod
297 //prod F xy = solver.update(F(x,y), F(new_x-x, new_y-y));
298 //prod
299 //prod x = xy.x.truncate();
300 //prod y = xy.y.truncate();
301 //prod print("dynamic", xerr, yerr);
302 //prod return xerr+yerr;
303 //prod}
304
305
main(int argc,char ** argv)306 int main(int argc, char** argv) {
307 initialize(argc, argv);
308 World world(SafeMPI::COMM_WORLD);
309
310 startup(world,argc,argv);
311 std::cout.precision(6);
312
313 FunctionDefaults<3>::set_k(k);
314 FunctionDefaults<3>::set_thresh(thresh);
315 FunctionDefaults<3>::set_refine(true);
316 FunctionDefaults<3>::set_initial_level(5);
317 FunctionDefaults<3>::set_truncate_mode(1);
318 FunctionDefaults<3>::set_cubic_cell(-L/2, L/2);
319
320 if (world.rank() == 0) print("\n Solving for the DFT/LDA wave function\n");
321 functionT Vnuc = real_factory_3d(world).f(V);
322 functionT psi = real_factory_3d(world).f(guess);
323 psi.truncate();
324 psi.scale(1.0/psi.norm2());
325
326 std::string xc_data;
327 xc_data="lda";
328 xc.initialize(xc_data, false);
329
330 // poperatorT coulop;
331 // coulop = poperatorT(CoulombOperatorPtr(world, 1e-10, thresh));
332 poperatorT coulop;
333 coulop = poperatorT(CoulombOperatorPtr(world, 1e-10, thresh));
334 gradop = gradient_operator<double,3>(world);
335 real_convolution_3d op = CoulombOperator(world, 0.001, 1e-6);
336 double eps = -0.5;
337 {
338 NonlinearSolver solver;
339 for (int iter=0; iter<maxiter; iter++) {
340 functionT rho = psi*psi;
341 // LDA
342 vecfuncT vf;
343 vf.push_back(rho);
344 //functionT fxc = make_dft_kernel(world, vf, 0, 0);
345 //functionT vxc = fxc*rho*2;
346 functionT vxc = make_dft_potential(world, vf, 0, 0);
347
348 functionT potential = Vnuc + 2.*op(rho).truncate() + vxc.truncate();
349 double err = iterate_ground(world, solver, potential, psi, eps);
350 if (err < thresh) break;
351 }
352 }
353 double kinetic_energy = 0.0;
354 for (int axis=0; axis<3; axis++) {
355 real_derivative_3d D = free_space_derivative<double,3>(world, axis);
356 functionT dpsi = D(psi);
357 kinetic_energy += inner(dpsi,dpsi);
358 }
359
360 functionT rho = square(psi);
361 rho.reconstruct();
362 vecfuncT vf;
363 vf.push_back(rho);
364 double exc=make_dft_energy(world, vf, 0); // Exc
365 double two_electron_energy = 2.*inner(op(rho),rho); // <u|rho> = <phi phi | 1/r12 | phi phi>
366 double nuclear_attraction_energy = 2.0*inner(Vnuc,rho); // <V|rho> = <phi|V|phi>
367 double densii = inner(psi,psi); // <rho>
368 // double nuclear_repulsion_energy = 1.0/R;
369 double total_energy = kinetic_energy + two_electron_energy +
370 nuclear_attraction_energy + exc;
371 // nuclear_attraction_energy + nuclear_repulsion_energy + exc;
372 double virial = (nuclear_attraction_energy + two_electron_energy ) / kinetic_energy;
373 //double virial = (nuclear_attraction_energy + two_electron_energy + nuclear_repulsion_energy) / kinetic_energy;
374
375 if (world.rank() == 0) {
376 print("");
377 print(" Kinetic energy ", kinetic_energy);
378 print(" Nuclear attraction energy ", nuclear_attraction_energy);
379 print(" Two-electron energy ", two_electron_energy);
380 // print(" Nuclear repulsion energy ", nuclear_repulsion_energy);
381 print(" XC energy ", exc);
382 print(" Total energy ", total_energy);
383 print(" Virial ", virial);
384 print(" dee ", densii);
385 }
386
387 // plot_line("psi.dat", 1001, {0.0,0.0,-20.0}, {0.0,0.0,20.0}, psi);
388
389
390 world.gop.fence();
391
392 #if 1
393 if (world.rank() == 0) print("\nSolving for the static response function\n");
394
395 functionT ri = real_factory_3d(world).f(rifunction);
396 // functionT dpsi(world); //zero
397 // dpsi = 1*psi;
398 functionT dpsi = real_factory_3d(world); //zero
399 rho = square(psi).truncate();
400 // LDA
401 //vecfuncT vf;
402 //vf.push_back(rho);
403 functionT vxc = make_dft_potential(world, vf, 0, 0);
404 functionT potential = Vnuc + 2.*op(rho).truncate() + vxc.truncate();
405 //functionT potential = Vnuc + 2.*op(rho).truncate() + vxc.truncate();
406
407 {
408 NonlinearSolver solver;
409 for(int iter=1; iter<=maxiterp; iter++) {
410 double err = iterate_excite(world, solver, potential, psi, dpsi, eps, ri);
411 if (err < 10*thresh) break;
412 }
413 }
414
415 //prod plot_line("dpsi.dat", 1001, {0.0,0.0,-20.0}, {0.0,0.0,20.0}, dpsi);
416 //prod plot_line("rho.dat", 1001, {0.0,0.0,-20.0}, {0.0,0.0,20.0}, rho);
417
418 double h1 = 0.0;
419 for (int axis=0; axis<3; axis++) {
420 real_derivative_3d D = free_space_derivative<double,3>(world, axis);
421 functionT nadp = D(dpsi);
422 h1 += 0.5*inner(nadp,nadp);
423 }
424 double h2 = inner(Vnuc*dpsi,dpsi);
425 double ans1 = h1 + h2;
426
427 functionT e1 = dpsi*psi;
428 functionT e2 = psi*psi;
429 functionT e3 = dpsi*dpsi;
430 double ans2 = inner(e1,op(e1));
431 double ans3 = inner(e3,op(e2));
432 double ans4 = inner(dpsi,dpsi);
433 double ans5 = inner(dpsi,ri*psi);
434 double d2Ea = (4*ans1) + (8*ans2) + (4*ans3) - (4*(eps*ans4)) + (8*ans5);
435 double d2Eb = 4*ans5;
436
437 if (world.rank() == 0) {
438 print("");
439 print("< dpsi | h | dpsi >", ans1);
440 print("< dpsi dpsi | psi psi >", ans2);
441 print("< dpsi psi | dpsi psi >", ans3);
442 print("< dpsi | dpsi >", ans4);
443 print("< dpsi | r | psi > ",ans5);
444 print("");
445 print(" < 1 | V - E1 | 0 > =", d2Ea);
446 print("-< 0 | H0 - E0 | 0 > =", d2Eb);
447 print("variational estimate =", 2*d2Ea - d2Eb);
448 }
449
450 #endif
451 #if 0
452 // For first frequency use zero as the initial guess but at subsequent
453 // frequencies use the previous solution as the guess.
454 functionT x = real_factory_3d(world); //zero
455 functionT y = real_factory_3d(world); //zero
456 for(int j=0; j<=1; j++) {
457 //double omega = 0.365 + (j*0.005);
458 //double omega = 0.1 + (j*0.01);
459 double omega = 0.0;
460 if (world.rank() == 0) print("\nSolving for the dynamic response function with omega =", omega,"\n");
461
462 XNonlinearSolver<F,double,allocator> solver = XNonlinearSolver<F,double,allocator>(allocator(world));
463
464 for(int iter=1; iter<=maxiterp; iter++) {
465 double err = iterate_xy(world, solver, potential, psi, eps, ri, x, y, omega);
466 if (err < 10*thresh) break;
467 }
468
469 functionT drho = (x*psi)+(psi*y);
470 double alpha_dynamic = -2*inner(ri,drho);
471 if (world.rank() == 0)
472 print("\nalpha_dynamic omega = ",omega," alpha = ",alpha_dynamic);
473
474 char fname[32];
475 sprintf(fname,"x_%6.4f.dat", omega);
476 plot_line(fname, 1001, {0.0,0.0,-20.0}, {0.0,0.0,20.0}, x);
477 sprintf(fname,"y_%6.4f.dat", omega);
478 plot_line(fname, 1001, {0.0,0.0,-20.0}, {0.0,0.0,20.0}, y);
479 sprintf(fname,"drho_%6.4f.dat", omega);
480 plot_line(fname, 1001, {0.0,0.0,-20.0}, {0.0,0.0,20.0}, drho);
481 }
482 #endif
483
484 finalize();
485 return 0;
486 }
487