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