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 
32 //#define WORLD_INSTANTIATE_STATIC_TEMPLATES
33 
34 /*!
35  \file examples/nemo.h
36  \brief solve the HF equations using numerical exponential MOs
37 
38  The source is
39  <a href=http://code.google.com/p/m-a-d-n-e-s-s/source/browse/local
40  /trunk/src/apps/examples/nemo.h>here</a>.
41 
42  */
43 
44 #ifndef NEMO_H_
45 #define NEMO_H_
46 
47 #include <madness/mra/mra.h>
48 #include <madness/mra/funcplot.h>
49 #include <madness/mra/operator.h>
50 #include <madness/mra/lbdeux.h>
51 #include <chem/SCF.h>
52 #include <chem/SCFProtocol.h>
53 #include <chem/correlationfactor.h>
54 #include <chem/molecular_optimizer.h>
55 #include <madness/mra/nonlinsol.h>
56 #include <madness/mra/vmra.h>
57 #include <chem/pcm.h>
58 #include <chem/AC.h>
59 
60 namespace madness {
61 
62 class PNO;
63 
64 // this class needs to be moved to vmra.h !!
65 
66 // This class is used to store information for the non-linear solver
67 template<typename T, std::size_t NDIM>
68 class vecfunc {
69 public:
70 	World& world;
71 	std::vector<Function<T, NDIM> > x;
72 
vecfunc(World & world,const std::vector<Function<T,NDIM>> & x1)73 	vecfunc(World& world, const std::vector<Function<T, NDIM> >& x1) :
74 			world(world), x(x1) {
75 	}
76 
vecfunc(const std::vector<Function<T,NDIM>> & x1)77 	vecfunc(const std::vector<Function<T, NDIM> >& x1) :
78 			world(x1[0].world()), x(x1) {
79 	}
80 
vecfunc(const vecfunc & other)81 	vecfunc(const vecfunc& other) :
82 			world(other.world), x(other.x) {
83 	}
84 
85 	vecfunc& operator=(const vecfunc& other) {
86 		x = other.x;
87 		return *this;
88 	}
89 
90 	vecfunc operator-(const vecfunc& b) const {
91 		return vecfunc(world, sub(world, x, b.x));
92 	}
93 
94 	vecfunc operator+=(const vecfunc& b) { // Operator+= necessary
95 		x = add(world, x, b.x);
96 		return *this;
97 	}
98 
99 	vecfunc operator*(double a) const { // Scale by a constant necessary
100 
101 		PROFILE_BLOCK(Vscale);
102 		std::vector<Function<T,NDIM> > result(x.size());
103 		for (unsigned int i=0; i<x.size(); ++i) {
104 			result[i]=mul(a,x[i],false);
105 		}
106 		world.gop.fence();
107 
108 //		scale(world, x, a);
109 		return result;
110 	}
111 };
112 
113 /// the non-linear solver requires an inner product
114 template<typename T, std::size_t NDIM>
inner(const vecfunc<T,NDIM> & a,const vecfunc<T,NDIM> & b)115 T inner(const vecfunc<T, NDIM>& a, const vecfunc<T, NDIM>& b) {
116 	Tensor<T> i = inner(a.world, a.x, b.x);
117 	return i.sum();
118 }
119 
120 // The default constructor for functions does not initialize
121 // them to any value, but the solver needs functions initialized
122 // to zero for which we also need the world object.
123 template<typename T, std::size_t NDIM>
124 struct allocator {
125 	World& world;
126 	const int n;
127 
128 	/// @param[in]	world	the world
129 	/// @param[in]	nn		the number of functions in a given vector
allocatorallocator130 	allocator(World& world, const int nn) :
131 			world(world), n(nn) {
132 	}
133 
134 	/// allocate a vector of n empty functions
operatorallocator135 	vecfunc<T, NDIM> operator()() {
136 		return vecfunc<T, NDIM>(world, zero_functions<T, NDIM>(world, n));
137 	}
138 };
139 
140 
141 
142 /// The Nemo class
143 class Nemo: public MolecularOptimizationTargetInterface {
144 	typedef std::shared_ptr<real_convolution_3d> poperatorT;
145 	friend class PNO;
146 
147 public:
148 
149 	/// ctor
150 
151 	/// @param[in]	world1	the world
152 	/// @param[in]	calc	the SCF
Nemo(World & world1,std::shared_ptr<SCF> calc)153 	Nemo(World& world1, std::shared_ptr<SCF> calc) :
154 			world(world1), calc(calc), ttt(0.0), sss(0.0), coords_sum(-1.0), ac(world,calc) {
155 
156 	    if (do_pcm()) pcm=PCM(world,this->molecule(),calc->param.pcm_data,true);
157 
158 	}
159 
construct_nuclear_correlation_factor()160 	void construct_nuclear_correlation_factor() {
161 
162 	    // construct the nuclear correlation factor:
163 	    if (not nuclear_correlation)
164 	        nuclear_correlation=create_nuclear_correlation_factor(world,*calc);
165 
166 	    // re-project the ncf
167 	    nuclear_correlation->initialize(FunctionDefaults<3>::get_thresh());
168 	    R = nuclear_correlation->function();
169 	    R.set_thresh(FunctionDefaults<3>::get_thresh());
170 	    R_square = nuclear_correlation->square();
171 	    R_square.set_thresh(FunctionDefaults<3>::get_thresh());
172 	}
173 
value()174 	double value() {return value(calc->molecule.get_all_coords());}
175 
176 	double value(const Tensor<double>& x);
177 
178 	/// compute the nuclear gradients
179 	Tensor<double> gradient(const Tensor<double>& x);
180 
provides_gradient()181 	bool provides_gradient() const {return true;}
182 
183 	/// returns the molecular hessian matrix at structure x
184 	Tensor<double> hessian(const Tensor<double>& x);
185 
186 	/// purify and symmetrize the hessian
187 
188 	/// The hessian should be symmetric, but it is not, because
189 	/// \f[
190 	///  \langle i^{Y_B}|H^{X_A}|i\rangle \neq \langle i|H^{X_A}|i^{Y_B}\rangle
191 	/// \f]
192 	/// does holds analytically, but not numerically. If the two numbers
193 	/// differ, pick the more trustworthy, which is the one with a heavy
194 	/// atom causing the perturbed density and the light atom being the
195 	/// nuclear singularity.
196 	/// @param[in]  hessian the raw hessian
197 	/// @return     a symmetrized hessian
198 	Tensor<double> purify_hessian(const Tensor<double>& hessian) const;
199 
200 
201 	/// solve the CPHF equations for the nuclear displacements
202 
203 	/// this function computes that part of the orbital response that is
204 	/// orthogonal to the occupied space. If no NCF's are used this
205 	/// corresponds to the normal response. If NCF's are used the part
206 	/// parallel to the occupied space must be added!
207 	/// \f[
208 	///     F^X = F^\perp + F^\parallel
209 	/// \f]
210 	/// cf parallel_CPHF()
211 	/// @param[in]  iatom   the atom A to be moved
212 	/// @param[in]  iaxis   the coordinate X of iatom to be moved
213 	/// @return     \ket{i^X} or \ket{F^\perp}
214 	vecfuncT solve_cphf(const int iatom, const int iaxis, const Tensor<double> fock,
215 	        const vecfuncT& guess, const vecfuncT& rhsconst,
216 	        const Tensor<double> incomplete_hessian, const vecfuncT& parallel,
217 	        const SCFProtocol& p, const std::string& xc_data) const;
218 
219 	/// solve the CPHF equation for all displacements
220 
221 	/// this function computes the nemo response F^X
222     /// \f[
223     ///     F^X = F^\perp + F^\parallel
224     /// \f]
225 	/// To reconstruct the unregularized orbital response (not recommended):
226 	/// \f[
227 	///   i^X   = R^X F + R F^X
228 	/// \f]
229 	/// The orbital response i^X constructed in this way is automatically
230 	/// orthogonal to the occupied space because of the parallel term F^\parallel
231 	/// @return a vector of the nemo response F^X for all displacements
232 	std::vector<vecfuncT> compute_all_cphf();
233 
234     /// this function computes that part of the orbital response that is
235     /// parallel to the occupied space.
236     /// \f[
237     ///     F^X = F^\perp + F^\parallel
238     /// \f]
239     /// If no NCF's are used F^\parallel vanishes.
240     /// If NCF's are used this term does not vanish because the derivatives of
241     /// the NCF does not vanish, and it is given by
242     /// \f[
243     ///  F_i^\parallel = -\frac{1}{2}\sum_k|F_k ><F_k | (R^2)^X | F_i>
244     /// \f]
245     vecfuncT compute_cphf_parallel_term(const int iatom, const int iaxis) const;
246 
247     /// compute the IR intensities in the double harmonic approximation
248 
249     /// use the projected normal modes; units are km/mol
250     /// @param[in]  normalmodes the normal modes
251     /// @param[in]  dens_pt the perturbed densities for each nuclear displacement
252     Tensor<double> compute_IR_intensities(const Tensor<double>& normalmodes,
253             const vecfuncT& dens_pt) const;
254 
get_calc()255 	std::shared_ptr<SCF> get_calc() const {return calc;}
256 
get_pcm()257 	PCM get_pcm()const{return pcm;}
258 
259 	/// compute the Fock matrix from scratch
260 	tensorT compute_fock_matrix(const vecfuncT& nemo, const tensorT& occ) const;
261 
262 	/// return a reference to the molecule
molecule()263 	Molecule& molecule() {return calc->molecule;}
264 
265     /// return a reference to the molecule
molecule()266     Molecule& molecule() const {
267         return calc->molecule;
268     }
269 
270     /// make the density (alpha or beta)
271     real_function_3d make_density(const Tensor<double>& occ,
272             const vecfuncT& nemo) const;
273 
274     /// make the density using different bra and ket vectors
275 
276     /// e.g. for computing the perturbed density \sum_i \phi_i \phi_i^X
277     /// or when using nemos: \sum_i R2nemo_i nemo_i
278     real_function_3d make_density(const tensorT & occ,
279             const vecfuncT& bra, const vecfuncT& ket, const bool refine=false) const;
280 
281     /// make the derivative of the density
282 
283     /// \f$ \nabla\rho = 2R^X R \rho_R + R^2\nabla \rho_R \f$
284     /// @param[in]  rhonemo    the regularized density
285     /// @param[in]  axis       the component of the nabla operator
286     /// @return     the gradient of the *reconstructed* density
287     real_function_3d make_ddensity(const real_function_3d& rhonemo,
288             const int axis) const;
289 
290     /// compute the reduced densities sigma (gamma) for GGA functionals
291     real_function_3d make_sigma(const real_function_3d& rho1,
292             const real_function_3d& rho2) const;
293 
294 
295     /// the Laplacian of the density
296 
297     /// The Laplacian should currently only be used for subsequent convolution
298     /// with a Green's function (which is reasonably stable), but not on its own!
299     ///
300     /// The Laplacian of the cuspy density is numerically fairly unstable:
301     ///  - a singular term may be rewritten using the nuclear potential (see below)
302     ///  - the Laplacian of the regularized density is still very noisy
303     ///
304     /// It may be computed as
305     /// \f[
306     ///   \Delta \rho = \Delta (R^2 \rho_R)
307     ///          = \Delta (R^2) \rho_R + 2\nabla R \nabla \rho_R + R^2 \Delta \rho_R
308     ///          = 2 R^2 U1^2 \rho_R -4 R^2 ( U-V ) \rho_R + R^2 \Delta\rho_R
309     /// \f]
310     /// where we can use the identity
311     /// \f[
312     ///   U=V + R^{-1}[T,R]
313     ///   -2 R (U-V) = \Delta R + 2\nabla R\dot \nabla
314     /// \f]
315     /// first term comes from the definition of the U potential as the commutator
316     /// over the kinetic energy (aka the Laplacian)
317     /// @param[in]  rhonemo    the regularized density \rho_R
318     /// @return     the laplacian of the reconstructed density \Delta (R^2\rho_R)
319     real_function_3d make_laplacian_density(const real_function_3d& rhonemo) const;
320 
321     /// compute the kinetic energy potential using Eq. (16) of
322     /// R. A. King and N. C. Handy, “Kinetic energy functionals from the Kohn–Sham potential,”
323     /// Phys. Chem. Chem. Phys., vol. 2, no. 22, pp. 5049–5056, 2000.
324     real_function_3d kinetic_energy_potential(const vecfuncT& nemo) const;
325 
326 
327     /// smooth a function by projecting it onto k-1 and then average with k
328 
329     /// kept it here for further testing
smoothen(real_function_3d & f)330     static void smoothen(real_function_3d& f) {
331         int k=f.get_impl()->get_k();
332         real_function_3d fproj=project(f,k-1);
333         real_function_3d freproj=project(fproj,k);
334         f=0.5*(f+freproj);
335     }
336 
337 private:
338 
339 	/// the world
340 	World& world;
341 
342 	std::shared_ptr<SCF> calc;
343 
344 	mutable double ttt, sss;
START_TIMER(World & world)345 	void START_TIMER(World& world) const {
346 	    world.gop.fence(); ttt=wall_time(); sss=cpu_time();
347 	}
348 
END_TIMER(World & world,const std::string msg)349 	void END_TIMER(World& world, const std::string msg) const {
350 	    END_TIMER(world,msg.c_str());
351 	}
352 
END_TIMER(World & world,const char * msg)353 	void END_TIMER(World& world, const char* msg) const {
354 	    ttt=wall_time()-ttt; sss=cpu_time()-sss;
355 	    if (world.rank()==0) printf("timer: %20.20s %8.2fs %8.2fs\n", msg, sss, ttt);
356 	}
357 
358 	struct timer {
359         World& world;
360 	    double ttt,sss;
timertimer361 	    timer(World& world) : world(world) {
362 	        world.gop.fence();
363 	        ttt=wall_time();
364 	        sss=cpu_time();
365 	    }
366 
tagtimer367 	    void tag(const std::string msg) {
368             world.gop.fence();
369 	        double tt1=wall_time()-ttt;
370 	        double ss1=cpu_time()-sss;
371 	        if (world.rank()==0) printf("timer: %20.20s %8.2fs %8.2fs\n", msg.c_str(), ss1, tt1);
372 	        ttt=wall_time();
373 	        sss=cpu_time();
374 	    }
375 
endtimer376 	    void end(const std::string msg) {
377             world.gop.fence();
378             double tt1=wall_time()-ttt;
379             double ss1=cpu_time()-sss;
380             if (world.rank()==0) printf("timer: %20.20s %8.2fs %8.2fs\n", msg.c_str(), ss1, tt1);
381         }
382 
383 	};
384 
385 public:
386 
387 	/// the nuclear correlation factor
388 	std::shared_ptr<NuclearCorrelationFactor> nuclear_correlation;
389 
390 	/// the nuclear correlation factor
391 	real_function_3d R;
392 
393     /// the square of the nuclear correlation factor
394     real_function_3d R_square;
395 
396 private:
397 
398 	/// sum of square of coords at last solved geometry
399 	mutable double coords_sum;
400 
401 	/// a poisson solver
402 	std::shared_ptr<real_convolution_3d> poisson;
403 
404 //	/// asymptotic correction for DFT
405 //	AC<3> ac;
406 //
407 //    /// apply the AC scheme of Tozer/Handy with the multipole approximation
408 //    Function<double,3> apply_ac(const Function<double,3>& vxc)const{
409 //    	return ac.apply(vxc);
410 //    }
411 //
412 //    /// apply the AC scheme of Tozer/Handy using the hartree potential
413 //    Function<double,3> apply_ac(const Function<double,3>& vxc, const Function<double,3>& vhartree)const{
414 //    	return ac.apply(vxc,vhartree);
415 //    }
416 //
417 //    /// apply the AC scheme of Tozer/Handy
418 //    Function<double,3> apply_ac_excited(Function<double,3>& vxc, const Function<double,3>& vhartree)const{
419 //    	return ac.apply_potential(vxc,vhartree);
420 //    }
421 
422 	/// polarizable continuum model
423 	PCM pcm;
424 	AC<3> ac;
425 
426 	/// adapt the thresholds consistently to a common value
set_protocol(const double thresh)427     void set_protocol(const double thresh) {
428 
429         calc->set_protocol<3>(world,thresh);
430 
431         // (re) construct nuclear potential and correlation factors
432         timer timer1(world);
433         construct_nuclear_correlation_factor();
434         timer1.end("reproject ncf");
435 
436         // (re) construct the Poisson solver
437         poisson = std::shared_ptr<real_convolution_3d>(
438                 CoulombOperatorPtr(world, calc->param.lo, FunctionDefaults<3>::get_thresh()));
439 
440         // set thresholds for the MOs
441         set_thresh(world,calc->amo,thresh);
442         set_thresh(world,calc->bmo,thresh);
443 
444     }
445 
446 	/// solve the HF equations
447 	double solve(const SCFProtocol& proto);
448 
449 	/// given nemos, compute the HF energy
450 	double compute_energy(const vecfuncT& psi, const vecfuncT& Jpsi,
451 			const vecfuncT& Kpsi) const;
452 
453     /// given nemos, compute the HF energy using the regularized expressions for T and V
454     double compute_energy_regularized(const vecfuncT& nemo, const vecfuncT& Jnemo,
455             const vecfuncT& Knemo, const vecfuncT& Unemo) const;
456 
457 	/// compute the reconstructed orbitals, and all potentials applied on nemo
458 
459 	/// to use these potentials in the fock matrix computation they must
460 	/// be multiplied by the nuclear correlation factor
461 	/// @param[in]	nemo	the nemo orbitals
462 	/// @param[out]	psi		the reconstructed, full orbitals
463 	/// @param[out]	Jnemo	Coulomb operator applied on the nemos
464 	/// @param[out]	Knemo	exchange operator applied on the nemos
465 	/// @param[out]	pcmnemo	PCM (solvent) potential applied on the nemos
466 	/// @param[out]	Unemo	regularized nuclear potential applied on the nemos
467 	void compute_nemo_potentials(const vecfuncT& nemo, vecfuncT& psi,
468 			vecfuncT& Jnemo, vecfuncT& Knemo, vecfuncT& pcmnemo,
469 			vecfuncT& Unemo) const;
470 
471 	/// normalize the nemos
472 	void normalize(vecfuncT& nemo) const;
473 
474     /// orthonormalize the vectors
475 
476     /// @param[in,out]	nemo	the vectors to be orthonormalized
477     void orthonormalize(vecfuncT& nemo) const;
478 
479 	/// return the Coulomb potential
480 	real_function_3d get_coulomb_potential(const vecfuncT& psi) const;
481 
482 	/// compute the incomplete hessian
483 
484 	/// incomplete hessian is the nuclear-nuclear contribution, and the
485 	/// contribution from the second derivative of the nuclear potential,
486 	/// and also the derivative of the nuclear correlation factor.
487 	/// i.e. all contributions that *do not* contain the regularized perturbed
488 	/// density, but it will contain parts of the perturbed density
489 	Tensor<double> make_incomplete_hessian() const;
490 
491     /// compute the complementary incomplete hessian
492 
493 	/// @param[in]  xi the response functions including the parallel part
494     Tensor<double> make_incomplete_hessian_response_part(
495             const std::vector<vecfuncT>& xi) const;
496 
497 	/// compute the constant term for the CPHF equations
498 
499 	/// mainly all terms with the nuclear correlation factor's derivatives
500 	vecfuncT make_cphf_constant_term(const int iatom, const int iaxis,
501 	        const vecfuncT& R2nemo, const real_function_3d& rhonemo) const;
502 
503 	public:
504 
is_dft()505 	bool is_dft() const {return calc->xc.is_dft();}
506 
do_pcm()507 	bool do_pcm() const {return calc->param.pcm_data != "none";}
508 
do_ac()509 	bool do_ac() const {return calc->param.ac_data != "none";}
510 
get_ac()511 	AC<3> get_ac() const {return ac;}
512 
513 	private:
514 
515 	/// localize the nemo orbitals
516     vecfuncT localize(const vecfuncT& nemo, const double dconv, const bool randomize) const;
517 
518 	/// return the threshold for vanishing elements in orbital rotations
trantol()519     double trantol() const {
520         return calc->vtol / std::min(30.0, double(get_calc()->amo.size()));
521     }
522 
523 	template<typename solverT>
524 	void rotate_subspace(World& world, const tensorT& U, solverT& solver,
525 			int lo, int nfunc) const;
526 
Q2(const tensorT & s)527     tensorT Q2(const tensorT& s) const {
528         tensorT Q = -0.5*s;
529         for (int i=0; i<s.dim(0); ++i) Q(i,i) += 1.5;
530         return Q;
531     }
532 
533     void make_plots(const real_function_3d &f,const std::string &name="function")const{
534         double width = FunctionDefaults<3>::get_cell_min_width()/2.0 - 1.e-3;
535         plot_plane(world,f,name);
536         coord_3d start(0.0); start[0]=-width;
537         coord_3d end(0.0); end[0]=width;
538         plot_line(("line_"+name).c_str(),1000,start,end,f);
539     }
540 
541     /// save a function
542     template<typename T, size_t NDIM>
543     void save_function(const std::vector<Function<T,NDIM> >& f, const std::string name) const;
544 
545     /// load a function
546     template<typename T, size_t NDIM>
547     void load_function(std::vector<Function<T,NDIM> >& f, const std::string name) const;
548 
549 };
550 
551 /// rotate the KAIN subspace (cf. SCF.cc)
552 template<typename solverT>
rotate_subspace(World & world,const tensorT & U,solverT & solver,int lo,int nfunc)553 void Nemo::rotate_subspace(World& world, const tensorT& U, solverT& solver,
554         int lo, int nfunc) const {
555     std::vector < vecfunc<double, 3> > &ulist = solver.get_ulist();
556     std::vector < vecfunc<double, 3> > &rlist = solver.get_rlist();
557     for (unsigned int iter = 0; iter < ulist.size(); ++iter) {
558         vecfuncT& v = ulist[iter].x;
559         vecfuncT& r = rlist[iter].x;
560         vecfuncT vnew = transform(world, vecfuncT(&v[lo], &v[lo + nfunc]), U,
561                 trantol(), false);
562         vecfuncT rnew = transform(world, vecfuncT(&r[lo], &r[lo + nfunc]), U,
563                 trantol(), true);
564 
565         world.gop.fence();
566         for (int i=0; i<nfunc; i++) {
567             v[i] = vnew[i];
568             r[i] = rnew[i];
569         }
570     }
571     world.gop.fence();
572 }
573 
574 /// save a function
575 template<typename T, size_t NDIM>
save_function(const std::vector<Function<T,NDIM>> & f,const std::string name)576 void Nemo::save_function(const std::vector<Function<T,NDIM> >& f, const std::string name) const {
577     if (world.rank()==0) print("saving vector of functions",name);
578     archive::ParallelOutputArchive ar(world, name.c_str(), 1);
579     ar & f.size();
580     for (const Function<T,NDIM>& ff:f)  ar & ff;
581 }
582 
583 /// save a function
584 template<typename T, size_t NDIM>
load_function(std::vector<Function<T,NDIM>> & f,const std::string name)585 void Nemo::load_function(std::vector<Function<T,NDIM> >& f, const std::string name) const {
586     if (world.rank()==0) print("loading vector of functions",name);
587     archive::ParallelInputArchive ar(world, name.c_str(), 1);
588     std::size_t fsize=0;
589     ar & fsize;
590     f.resize(fsize);
591     for (std::size_t i=0; i<fsize; ++i) ar & f[i];
592 }
593 
594 }
595 
596 #endif /* NEMO_H_ */
597 
598