MADNESS 0.10.1
gfit.h
Go to the documentation of this file.
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 $Id: key.h 2907 2012-06-14 10:15:05Z 3ru6ruWu $
33 */
34
35#ifndef MADNESS_MRA_GFIT_H__INCLUDED
36#define MADNESS_MRA_GFIT_H__INCLUDED
37
38/// \file gfit.h
39/// \brief fit isotropic functions to a set of Gaussians with controlled precision
40
41//#include <iostream>
42
43#include <cmath>
44#include "../constants.h"
45#include "../tensor/basetensor.h"
46#include "../tensor/slice.h"
47#include "../tensor/tensor.h"
48#include "../tensor/tensor_lapack.h"
49#include "../world/madness_exception.h"
50#include "../world/print.h"
52#include <algorithm>
54
55
56namespace madness {
57
58template<typename T, std::size_t NDIM>
59class GFit {
60
61public:
62
63 /// default ctor does nothing
64 GFit() = default;
65
67 double mu = info.mu;
68 double lo = info.lo;
69 double hi = info.hi;
70 MADNESS_CHECK_THROW(hi>0,"hi must be positive in gfit: U need to set it manually in operator.h");
71 double eps = info.thresh;
72 bool debug=info.debug;
73 OpType type = info.type;
74
75
76 if (type==OT_G12) {*this=CoulombFit(lo,hi,eps,debug);
77 } else if (type==OT_SLATER) {*this=SlaterFit(mu,lo,hi,eps,debug);
78 } else if (type==OT_GAUSS) {*this=GaussFit(mu,lo,hi,eps,debug);
79 } else if (type==OT_F12) {*this=F12Fit(mu,lo,hi,eps,debug);
80 } else if (type==OT_FG12) {*this=FGFit(mu,lo,hi,eps,debug);
81 } else if (type==OT_F212) {*this=F12sqFit(mu,lo,hi,eps,debug);
82 } else if (type==OT_F2G12) {*this=F2GFit(mu,lo,hi,eps,debug);
83 } else if (type==OT_BSH) {*this=BSHFit(mu,lo,hi,eps,debug);
84 } else {
85 print("Operator type not implemented: ",type);
86 MADNESS_EXCEPTION("Operator type not implemented: ",1);
87 }
88
89 }
90
91 /// copy constructor
92 GFit(const GFit& other) = default;
93
94 /// assignment operator
95 GFit& operator=(const GFit& other) {
96 coeffs_ = other.coeffs_;
97 exponents_ = other.exponents_;
98 return *this;
99 }
100
101 /// return a fit for the Coulomb function
102
103 /// f(r) = 1/r
104 static GFit CoulombFit(double lo, double hi, double eps, bool prnt=false) {
105 GFit fit=BSHFit(0.0,lo,hi,eps/(4.0*constants::pi),prnt);
106 fit.coeffs_.scale(4.0*constants::pi); // BSHFit scales by 1/(4 pi), undo here
107 return fit;
108 }
109
110 /// return a fit for the bound-state Helmholtz function
111
112 /// the BSH function is defined by
113 /// f(r) = 1/ (4 pi) exp(-\mu r)/r
114 /// @param[in] mu the exponent of the BSH
115 /// @param[in] lo the smallest length scale that needs to be precisely represented
116 /// @param[in] hi the largest length scale that needs to be precisely represented
117 /// @param[in] eps the precision threshold
118 /// @parma[in] prnt print level
119 static GFit BSHFit(double mu, double lo, double hi, double eps, bool prnt=false) {
120 GFit fit;
121 bool fix_interval=false;
122 if (NDIM==3) bsh_fit(mu,lo,hi,eps,fit.coeffs_,fit.exponents_,prnt,fix_interval);
123 else bsh_fit_ndim(NDIM,mu,lo,hi,eps,fit.coeffs_,fit.exponents_,prnt);
124
125 if (prnt) {
126 print("bsh fit");
127 auto exact = [&mu](const double r) -> double { return 1.0/(4.0 * constants::pi) * exp(-mu * r)/r; };
128 fit.print_accuracy(exact, lo, hi);
129 }
130 return fit;
131 }
132
133 /// return a fit for the Slater function
134
135 /// the Slater function is defined by
136 /// f(r) = exp(-\gamma r)
137 /// @param[in] gamma the exponent of the Slater function
138 /// @param[in] lo the smallest length scale that needs to be precisely represented
139 /// @param[in] hi the largest length scale that needs to be precisely represented
140 /// @param[in] eps the precision threshold
141 /// @parma[in] prnt print level
142 static GFit SlaterFit(double gamma, double lo, double hi, double eps, bool prnt=false) {
143 GFit fit;
144 slater_fit(gamma,lo,hi,eps,fit.coeffs_,fit.exponents_,false);
145 if (prnt) {
146 print("Slater fit");
147 auto exact = [&gamma](const double r) -> double { return exp(-gamma * r); };
148 fit.print_accuracy(exact, lo, hi);
149 }
150 return fit;
151 }
152
153 /// return a (trivial) fit for a single Gauss function
154
155 /// the Gauss function is defined by
156 /// f(r) = exp(-\gamma r^2)
157 /// @param[in] gamma the exponent of the Gauss function
158 /// @param[in] lo the smallest length scale that needs to be precisely represented
159 /// @param[in] hi the largest length scale that needs to be precisely represented
160 /// @param[in] eps the precision threshold
161 /// @parma[in] prnt print level
162 static GFit GaussFit(double gamma, double lo, double hi, double eps, bool prnt=false) {
163 GFit fit;
165 fit.exponents_=Tensor<double>(1);
166 fit.coeffs_=1.0;
167 fit.exponents_=gamma;
168 if (prnt) {
169 print("Gauss fit");
170 auto exact = [&gamma](const double r) -> double { return exp(-gamma * r*r); };
171 fit.print_accuracy(exact, lo, hi);
172 }
173 return fit;
174 }
175
176 /// return a fit for the F12 correlation factor
177
178 /// the Slater function is defined by
179 /// f(r) = 1/(2 gamma) * (1 - exp(-\gamma r))
180 /// @param[in] gamma the exponent of the Slater function
181 /// @param[in] lo the smallest length scale that needs to be precisely represented
182 /// @param[in] hi the largest length scale that needs to be precisely represented
183 /// @param[in] eps the precision threshold
184 /// @parma[in] prnt print level
185 static GFit F12Fit(double gamma, double lo, double hi, double eps, bool prnt=false) {
186 GFit fit;
187 f12_fit(gamma,lo*0.1,hi,eps*0.01,fit.coeffs_,fit.exponents_,false);
188 fit.coeffs_*=(0.5/gamma);
189 if (prnt) {
190 print("f12 fit");
191 auto exact=[&gamma](const double r) -> double {return 0.5/gamma*(1.0-exp(-gamma*r));};
192 fit.print_accuracy(exact,lo,hi);
193 }
194 return fit;
195 }
196
197 /// return a fit for the F12^2 correlation factor
198
199 /// the Slater function square is defined by
200 /// f(r) = [ 1/(2 gamma) * (1 - exp(-\gamma r)) ] ^2
201 /// @param[in] gamma the exponent of the Slater function
202 /// @param[in] lo the smallest length scale that needs to be precisely represented
203 /// @param[in] hi the largest length scale that needs to be precisely represented
204 /// @param[in] eps the precision threshold
205 /// @parma[in] prnt print level
206 static GFit F12sqFit(double gamma, double lo, double hi, double eps, bool prnt=false) {
207 GFit fit;
208 f12sq_fit(gamma,lo*0.1,hi,eps*0.01,fit.coeffs_,fit.exponents_,false);
209 fit.coeffs_*=(0.25/(gamma*gamma));
210 if (prnt) {
211 print("f12sq fit");
212 auto exact=[&gamma](const double r) -> double {return std::pow(0.5/gamma*(1.0-exp(-gamma*r)),2.0);};
213 fit.print_accuracy(exact,lo,hi);
214 }
215 return fit;
216 }
217
218
219 /// return a fit for the FG function
220
221 /// fg = 1/(2 mu) * (1 - exp(-gamma r12)) / r12
222 /// = 1/(2 mu) *( 1/r12 - exp(-gamma r12)/r12)
223 /// = 1/(2 mu) * (coulomb - bsh)
224 /// @param[in] gamma the exponent of the Slater function
225 /// @param[in] lo the smallest length scale that needs to be precisely represented
226 /// @param[in] hi the largest length scale that needs to be precisely represented
227 /// @param[in] eps the precision threshold
228 /// @parma[in] prnt print level
229 static GFit FGFit(double gamma, double lo, double hi, double eps, bool prnt=false) {
230 GFit bshfit,coulombfit;
231 eps*=0.1;
232 lo*=0.1;
233// bool restrict_interval=false;
234 bool fix_interval=true;
235 bsh_fit(gamma,lo,hi,eps,bshfit.coeffs_,bshfit.exponents_,false,fix_interval);
236 bsh_fit(0.0,lo,hi,eps,coulombfit.coeffs_,coulombfit.exponents_,false,fix_interval);
237 // check the exponents are identical
238 auto diffexponents=(coulombfit.exponents() - bshfit.exponents());
239 MADNESS_CHECK(diffexponents.normf()/coulombfit.exponents().size()<1.e-12);
240 auto diffcoefficients=(coulombfit.coeffs() - bshfit.coeffs());
241 GFit fgfit;
242 fgfit.exponents_=bshfit.exponents_;
243 fgfit.coeffs_=4.0*constants::pi*0.5/gamma*diffcoefficients;
245
246 if (prnt) {
247 print("fg fit");
248 auto exact=[&gamma](const double r) -> double {return 0.5/gamma*(1.0-exp(-gamma*r))/r;};
249 fgfit.print_accuracy(exact,lo,hi);
250 }
251 return fgfit;
252 }
253
254 /// return a fit for the F2G function
255
256 /// f2g = (1/(2 mu) * (1 - exp(-gamma r12)))^2 / r12
257 /// = 1/(4 mu^2) * [ 1/r12 - 2 exp(-gamma r12)/r12) + exp(-2 gamma r12)/r12 ]
258 /// @param[in] gamma the exponent of the Slater function
259 /// @param[in] lo the smallest length scale that needs to be precisely represented
260 /// @param[in] hi the largest length scale that needs to be precisely represented
261 /// @param[in] eps the precision threshold
262 /// @parma[in] prnt print level
263 static GFit F2GFit(double gamma, double lo, double hi, double eps, bool prnt=false) {
264 GFit bshfit,coulombfit,bsh2fit;
265 eps*=0.1;
266 lo*=0.1;
267// bool restrict_interval=false;
268 bool fix_interval=true;
269 bsh_fit(gamma,lo,hi,eps,bshfit.coeffs_,bshfit.exponents_,false,fix_interval);
270 bsh_fit(2.0*gamma,lo,hi,eps,bsh2fit.coeffs_,bsh2fit.exponents_,false,fix_interval);
271 bsh_fit(0.0,lo,hi,eps,coulombfit.coeffs_,coulombfit.exponents_,false,fix_interval);
272
273 // check the exponents are identical
274 auto diffexponents=(coulombfit.exponents() - bshfit.exponents());
275 MADNESS_CHECK(diffexponents.normf()/coulombfit.exponents().size()<1.e-12);
276 auto diffexponents1=(coulombfit.exponents() - bsh2fit.exponents());
277 MADNESS_CHECK(diffexponents1.normf()/coulombfit.exponents().size()<1.e-12);
278
279 auto coefficients=(coulombfit.coeffs() - 2.0* bshfit.coeffs() + bsh2fit.coeffs());
280 GFit f2gfit;
281 f2gfit.exponents_=bshfit.exponents_;
282 // additional factor 4 pi due to implementation of bsh_fit
283 double fourpi=4.0*constants::pi;
284 double fourmu2=4.0*gamma*gamma;
285 f2gfit.coeffs_=fourpi/fourmu2*coefficients;
287
288 if (prnt) {
289 print("fg fit");
290 auto exact=[&gamma](const double r) -> double {
291 return 0.25/(gamma*gamma)*(1.0-2.0*exp(-gamma*r)+exp(-2.0*gamma*r))/r;
292 };
293 f2gfit.print_accuracy(exact,lo,hi);
294 }
295 return f2gfit;
296 }
297
298
299 /// return a fit for a general isotropic function
300
301 /// note that the error is controlled over a uniform grid, the boundaries
302 /// will be poorly represented in general. Following Beylkin 2005
303 static GFit GeneralFit() {
304 MADNESS_EXCEPTION("General GFit still to be implemented",1);
305 return GFit();
306 }
307
308 /// return the coefficients of the fit
309 Tensor<T> coeffs() const {return coeffs_;}
310
311 /// return the exponents of the fit
313
314 /// Fold the most diffuse Gaussians of a fit into their neighbours while the fit stays accurate
315 /// to eps on [lo, hi]. Works from the tail inwards and stops at the first term that cannot be
316 /// merged; terms before `first_prunable` are never merged into (so `first_prunable - 1` and
317 /// everything before it keep their coefficients).
318 void static prune_small_coefficients(const double eps, const double lo, const double hi,
319 Tensor<double>& coeff, Tensor<double>& expnt,
320 const long first_prunable = 1) {
321 double mid = lo + (hi-lo)*0.5;
322 long npt=coeff.size();
323 long i;
324 for (i=npt-1; i>=std::max(first_prunable, 1L); --i) {
325 double cnew = coeff[i]*exp(-(expnt[i]-expnt[i-1])*mid*mid);
326 double errlo = coeff[i]*exp(-expnt[i]*lo*lo) -
327 cnew*exp(-expnt[i-1]*lo*lo);
328 double errhi = coeff[i]*exp(-expnt[i]*hi*hi) -
329 cnew*exp(-expnt[i-1]*hi*hi);
330 if (std::max(std::abs(errlo),std::abs(errhi)) > 0.03*eps) break;
331 npt--;
332 coeff[i-1] = coeff[i-1] + cnew;
333 }
334 coeff = coeff(Slice(0,npt-1));
335 expnt = expnt(Slice(0,npt-1));
336 }
337
338 /// Truncate the fit of a kernel that is lattice-summed along some axes
339
340 /// Along an axis with an infinite lattice sum, a Gaussian with exponent below
341 /// tcut = 0.25/L^2 (L the largest such cell width) has a lattice sum that is flat to
342 /// exp(-4 pi^2) over the cell: a gauge constant. If every axis is summed, those terms
343 /// are dropped (all but the first, as before). If some axes are not summed, the same
344 /// terms are not flat along them and carry real potential there -- so they
345 /// are kept as far as the finite axes need them: the tail is folded into its neighbours
346 /// while the fit stays accurate to eps on [lo, hi_fin], where hi_fin is the largest
347 /// distance the finite axes can reach. The first term below tcut is never merged into a
348 /// term above it, whose lattice sum is not flat.
349 /// @param[in,out] c coefficients of the fit; truncated (and possibly rescaled) on return
350 /// @param[in,out] e exponents of the fit, in decreasing order
351 /// @param[in] lattice_ranges lattice range of each axis
352 /// @param[in] cell_width width of each axis of the cell
353 /// @param[in] lo smallest distance the fit must represent accurately
354 /// @param[in] hi_fin largest distance the finite (non-infinite) axes must represent accurately
355 /// @param[in] eps accuracy of the fit on [lo, hi_fin]
357 const std::array<LatticeRange, NDIM>& lattice_ranges,
358 const Tensor<double>& cell_width,
359 double lo, double hi_fin, double eps) {
360 const bool infinite_any = std::any_of(lattice_ranges.begin(), lattice_ranges.end(), [](const auto& b) { return b.infinite(); });
361 const bool infinite_all = std::all_of(lattice_ranges.begin(), lattice_ranges.end(), [](const auto& b) { return b.infinite(); });
362 if (!infinite_any) return; // no lattice sum is a constant: nothing can be dropped
363
364 // The widest infinitely summed axis determines how "diffuse" a Gaussian sum needs to be for
365 // all infinite lattice sums to be constant.
366 double max_infinite_width = 0;
367 for (std::size_t d = 0; d != NDIM; ++d)
368 if (lattice_ranges[d].infinite()) max_infinite_width = std::max(max_infinite_width, cell_width(long(d)));
369 const double tcut = 0.25 / (max_infinite_width * max_infinite_width);
370
371 // the first term whose lattice sums are flat (exponents decrease with the index)
372 long icut = -1;
373 for (long i = 0; i < e.dim(0); ++i) {
374 if (e(i) < tcut) { icut = i; break; }
375 }
376 if (icut < 0) return; // no diffuse terms
377
378 if (infinite_all) { // every axis is summed: the diffuse tail is a constant, keep its first term only
379 c = c(Slice(0, icut));
380 e = e(Slice(0, icut));
381 return;
382 }
383 // mixed: fold the tail into its neighbours as far as the finite axes allow. The first
384 // prunable term is icut + 1, so icut may absorb its neighbours but the term before it,
385 // whose lattice sum is not flat, is never touched.
386 prune_small_coefficients(eps, lo, hi_fin, c, e, /* first_prunable = */ icut + 1);
387 }
388
389private:
390
391 /// ctor taking an isotropic function
392
393 /// the function will be represented with a uniform error on a uniform grid
394 /// @param[in] f a 1d-function that implements T operator()
395 template<typename funcT>
396 GFit(const funcT& f) {
397
398 }
399
400 /// print coefficients and exponents, and values and errors
401
402 /// @param[in] op the exact function, e.g. 1/r, exp(-mu r), etc
403 /// @param[in] lo lower bound for the range r
404 /// @param[in] hi higher bound for the range r
405 template<typename opT>
406 void print_accuracy(opT op, const double lo, const double hi) const {
407
408 std::cout << "weights and roots" << std::endl;
409 for (int i=0; i<coeffs_.size(); ++i)
410 std::cout << i << " " << coeffs_[i] << " " << exponents_[i] << std::endl;
411
412 long npt = 300;
413 std::cout << " x value abserr relerr" << std::endl;
414 std::cout << " ------------ ------- -------- -------- " << std::endl;
415 double step = exp(log(hi/lo)/(npt+1));
416 for (int i=0; i<=npt; ++i) {
417 double r = lo*(pow(step,i+0.5));
418// double exact = exp(-mu*r)/r/4.0/constants::pi;
419 double exact = op(r);
420 double test = 0.0;
421 for (int j=0; j<coeffs_.dim(0); ++j)
422 test += coeffs_[j]*exp(-r*r*exponents_[j]);
423 double err = 0.0;
424 if (exact) err = (exact-test)/exact;
425 printf(" %.6e %8.1e %8.1e %8.1e\n",r, exact, exact-test, err);
426 }
427 }
428
429 /// the coefficients of the expansion f(x) = \sum_m coeffs[m] exp(-exponents[m] * x^2)
431
432 /// the exponents of the expansion f(x) = \sum_m coeffs[m] exp(-exponents[m] * x^2)
434
435 /// fit the function exp(-mu r)/r
436
437 /// formulas taken from
438 /// G. Beylkin and L. Monzon, On approximation of functions by exponential sums,
439 /// Appl Comput Harmon A, vol. 19, no. 1, pp. 17-48, Jul. 2005.
440 /// and
441 /// R. J. Harrison, G. I. Fann, T. Yanai, and G. Beylkin,
442 /// Multiresolution Quantum Chemistry in Multiwavelet Bases,
443 /// Lecture Notes in Computer Science, vol. 2660, p. 103, 2003.
444 static void bsh_fit(double mu, double lo, double hi, double eps,
445 Tensor<double>& pcoeff, Tensor<double>& pexpnt, bool prnt, bool fix_interval) {
446
447 if (mu < 0.0) throw "cannot handle negative mu in bsh_fit";
448// bool restrict_interval=(mu>0) and use_mu_for_restricting_interval;
449
450
451 if ((mu > 0) and (not fix_interval)) {
452// if (restrict_interval) {
453 // Restrict hi according to the exponential decay
454 double r = -log(4*constants::pi*0.01*eps);
455 r = -log(r * 4*constants::pi*0.01*eps);
456 if (hi > r) hi = r;
457 }
458
459 double TT;
460 double slo, shi;
461
462 if (eps >= 1e-2) TT = 5;
463 else if (eps >= 1e-4) TT = 10;
464 else if (eps >= 1e-6) TT = 14;
465 else if (eps >= 1e-8) TT = 18;
466 else if (eps >= 1e-10) TT = 22;
467 else if (eps >= 1e-12) TT = 26;
468 else TT = 30;
469
470 if ((mu > 0) and (not fix_interval)) {
471// if (restrict_interval) {
472 slo = -0.5*log(4.0*TT/(mu*mu));
473 }
474 else {
475 slo = log(eps/hi) - 1.0;
476 }
477 shi = 0.5*log(TT/(lo*lo));
478 if (shi <= slo) throw "bsh_fit: logic error in slo,shi";
479
480 // Resolution required for quadrature over s
481 double h = 1.0/(0.2-.50*log10(eps)); // was 0.5 was 0.47
482
483 // Truncate the number of binary digits in h's mantissa
484 // so that rounding does not occur when performing
485 // manipulations to determine the quadrature points and
486 // to limit the number of distinct values in case of
487 // multiple precisions being used at the same time.
488 h = floor(64.0*h)/64.0;
489
490
491 // Round shi/lo up/down to an integral multiple of quadrature points
492 shi = ceil(shi/h)*h;
493 slo = floor(slo/h)*h;
494
495 long npt = long((shi-slo)/h+0.5);
496
497 //if (prnt)
498 //std::cout << "mu " << mu << " slo " << slo << " shi " << shi << " npt " << npt << " h " << h << " " << eps << std::endl;
499
500 Tensor<double> coeff(npt), expnt(npt);
501
502 for (int i=0; i<npt; ++i) {
503 double s = slo + h*(npt-i); // i+1
504 coeff[i] = h*2.0/sqrt(constants::pi)*exp(-mu*mu*exp(-2.0*s)/4.0)*exp(s);
505 coeff[i] = coeff[i]/(4.0*constants::pi);
506 expnt[i] = exp(2.0*s);
507 }
508
509#if ONE_TERM
510 npt=1;
511 double s=1.0;
512 coeff[0]=1.0;
513 expnt[0] = exp(2.0*s);
514 coeff=coeff(Slice(0,0));
515 expnt=expnt(Slice(0,0));
516 print("only one term in gfit",s,coeff[0],expnt[0]);
517
518
519#endif
520
521 // Prune large exponents from the fit ... never necessary due to construction
522
523 // Prune small exponents from Coulomb fit. Evaluate a gaussian at
524 // the range midpoint, and replace it there with the next most
525 // diffuse gaussian. Then examine the resulting error at the two
526 // end points ... if this error is less than the desired
527 // precision, can discard the diffuse gaussian.
528
529 if ((mu == 0.0) and (not fix_interval)) {
530// if (restrict_interval) {
532 }
533
534 // Modify the coeffs of the largest exponents to satisfy the moment conditions
535 //
536 // SETTING NMOM>1 TURNS OUT TO BE A BAD IDEA (AS CURRENTLY IMPLEMENTED)
537 // [It is accurate and efficient for a one-shot application but it seems to
538 // introduce fine-scale noise that amplifies during iterative solution of
539 // the SCF and DFT equations ... the symptom is negative coeffs in the fit]
540 //
541 // SET NMOM=0 or 1 (1 recommended) unless you are doing a one-shot application
542 //
543 // Determine the effective range of the four largest exponents and compute
544 // moments of the exact and remainder of the fit. Then adjust the coeffs
545 // to reproduce the exact moments in that volume.
546 //
547 // If nmom!=4 we assume that we will eliminate n=-1 which is stored first
548 // in the moment list
549 //
550 // <r^i|gj> cj = <r^i|exact-remainder>
551 const long nmom = 0;
552 if (nmom > 0) {
553 Tensor<double> q(4), qg(4);
554 double range = sqrt(-log(1e-6)/expnt[nmom-1]);
555 if (prnt) print("exponent(nmom-1)",expnt[nmom-1],"has range", range);
556
557 bsh_spherical_moments(mu, range, q);
558 Tensor<double> M(nmom,nmom);
559 for (int i=nmom; i<npt; ++i) {
560 Tensor<double> qt(4);
561 gaussian_spherical_moments(expnt[i], range, qt);
562 qg += qt*coeff[i];
563 }
564 if (nmom != 4) {
565 q = q(Slice(1,nmom));
566 qg = qg(Slice(1,nmom));
567 }
568 if (prnt) {
569 print("moments", q);
570 print("moments", qg);
571 }
572 q = q - qg;
573 for (int j=0; j<nmom; ++j) {
574 Tensor<double> qt(4);
575 gaussian_spherical_moments(expnt[j], range, qt);
576 if (nmom != 4) qt = qt(Slice(1,nmom));
577 for (int i=0; i<nmom; ++i) {
578 M(i,j) = qt[i];
579 }
580 }
581 Tensor<double> ncoeff;
582 gesv(M, q, ncoeff);
583 if (prnt) {
584 print("M\n",M);
585 print("old coeffs", coeff(Slice(0,nmom-1)));
586 print("new coeffs", ncoeff);
587 }
588
589 coeff(Slice(0,nmom-1)) = ncoeff;
590 }
591
592 pcoeff = coeff;
593 pexpnt = expnt;
594 }
595
596 /// fit a Slater function using a sum of Gaussians
597
598 /// formula inspired by the BSH fit, with the roles of r and mu exchanged
599 /// see also Eq. (A3) in
600 /// S. Ten-no, Initiation of explicitly correlated Slater-type geminal theory,
601 /// Chem. Phys. Lett., vol. 398, no. 1, pp. 56-61, 2004.
602 static void slater_fit(double gamma, double lo, double hi, double eps,
603 Tensor<double>& pcoeff, Tensor<double>& pexpnt, bool prnt) {
604
605 MADNESS_CHECK(gamma >0.0);
606 // empirical number TT for the upper integration limit
607 double TT;
608 if (eps >= 1e-2) TT = 5;
609 else if (eps >= 1e-4) TT = 10;
610 else if (eps >= 1e-6) TT = 14;
611 else if (eps >= 1e-8) TT = 18;
612 else if (eps >= 1e-10) TT = 22;
613 else if (eps >= 1e-12) TT = 26;
614 else TT = 30;
615
616 // integration limits for quadrature over s: slo and shi
617 // slo and shi must not depend on gamma!!!
618 double slo=0.5 * log(eps) - 1.0;
619 double shi=log(TT/(lo*lo))*0.5;
620
621 // Resolution required for quadrature over s
622 double h = 1.0/(0.2-.5*log10(eps)); // was 0.5 was 0.47
623
624 // Truncate the number of binary digits in h's mantissa
625 // so that rounding does not occur when performing
626 // manipulations to determine the quadrature points and
627 // to limit the number of distinct values in case of
628 // multiple precisions being used at the same time.
629 h = floor(64.0*h)/64.0;
630
631 // Round shi/lo up/down to an integral multiple of quadrature points
632 shi = ceil(shi/h)*h;
633 slo = floor(slo/h)*h;
634
635 long npt = long((shi-slo)/h+0.5);
636
637 Tensor<double> coeff(npt), expnt(npt);
638
639 for (int i=0; i<npt; ++i) {
640 const double s = slo + h*(npt-i); // i+1
641 coeff[i] = h*exp(-gamma*gamma*exp(2.0*s) + s);
642 coeff[i]*=2.0*gamma/sqrt(constants::pi);
643 expnt[i] = 0.25*exp(-2.0*s);
644 }
645
646 if (prnt) {
647 std::cout << "weights and roots for a Slater function with gamma=" << gamma << std::endl;
648 for (int i=0; i<npt; ++i)
649 std::cout << i << " " << coeff[i] << " " << expnt[i] << std::endl;
650
651 long npt = 300;
652 //double hi = 1.0;
653 //if (mu) hi = min(1.0,30.0/mu);
654 std::cout << " x value abserr relerr" << std::endl;
655 std::cout << " ------------ ------- -------- -------- " << std::endl;
656 double step = exp(log(hi/lo)/(npt+1));
657 for (int i=0; i<=npt; ++i) {
658 double r = lo*(pow(step,i+0.5));
659 double exact = exp(-gamma*r);
660 double test = 0.0;
661 for (int j=0; j<coeff.dim(0); ++j)
662 test += coeff[j]*exp(-r*r*expnt[j]);
663 double err = 0.0;
664 if (exact) err = (exact-test)/exact;
665 printf(" %.6e %8.1e %8.1e %8.1e\n",r, exact, exact-test, err);
666 }
667 }
668 pcoeff = coeff;
669 pexpnt = expnt;
670 }
671
672
673 /// fit a correlation factor (1- exp(-mu r))
674
675 /// use the Slater fit with an additional term: 1*exp(-0 r^2)
676 static void f12_fit(double gamma, double lo, double hi, double eps,
677 Tensor<double>& pcoeff, Tensor<double>& pexpnt, bool prnt) {
678 Tensor<double> coeff,expnt;
679 slater_fit(gamma, lo, hi, eps, coeff, expnt, prnt);
680
681 pcoeff=Tensor<double>(coeff.size()+1);
682 pcoeff(Slice(1,-1,1))=-coeff(_);
683 pexpnt=Tensor<double>(expnt.size()+1);
684 pexpnt(Slice(1,-1,1))=expnt(_);
685
686 pcoeff(0l)=1.0;
687 pexpnt(0l)=1.e-10;
688 }
689
690
691 /// fit a correlation factor f12^2 = (1- exp(-mu r))^2 = 1 - 2 exp(-mu r) + exp(-2 mu r)
692
693 /// no factor 1/(2 mu) or square of it included!
694 /// use the Slater fit with an additional term: 1*exp(-0 r^2)
695 static void f12sq_fit(double gamma, double lo, double hi, double eps,
696 Tensor<double>& pcoeff, Tensor<double>& pexpnt, bool prnt) {
697 Tensor<double> coeff1,expnt1, coeff2, expnt2;
698 slater_fit(gamma, lo, hi, eps, coeff1, expnt1, prnt);
699 slater_fit(2.0*gamma, lo, hi, eps, coeff2, expnt2, prnt);
700
701 // check exponents are the same
702 MADNESS_CHECK((expnt1-expnt2).normf()/expnt1.size()<1.e-12);
703
704 // add exponential terms
705 auto coeff=coeff2-2.0*coeff1;
706 pcoeff=Tensor<double>(coeff1.size()+1);
707 pcoeff(Slice(1,-1,1))=coeff(_);
708 pexpnt=Tensor<double>(expnt1.size()+1);
709 pexpnt(Slice(1,-1,1))=expnt1(_);
710
711 // add constant term
712 pcoeff(0l)=1.0;
713 pexpnt(0l)=1.e-10;
714 }
715
716
717 void static bsh_fit_ndim(int ndim, double mu, double lo, double hi, double eps,
718 Tensor<double>& pcoeff, Tensor<double>& pexpnt, bool prnt) {
719
720 if (mu > 0) {
721 // Restrict hi according to the exponential decay
722 double r = -log(4*constants::pi*0.01*eps);
723 r = -log(r * 4*constants::pi*0.01*eps);
724 if (hi > r) hi = r;
725 }
726
727
728 // Determine range of quadrature by estimating when
729 // kernel drops to exp(-100)
730
731 double slo, shi;
732 if (mu > 0) {
733 slo = -0.5*log(4.0*100.0/(mu*mu));
734 slo = -0.5*log(4.0*(slo*ndim - 2.0*slo + 100.0)/(mu*mu));
735 }
736 else {
737 slo = log(eps/hi) - 1.0;
738 }
739 shi = 0.5*log(100.0/(lo*lo));
740
741 // Resolution required for quadrature over s
742 double h = 1.0/(0.2-.50*log10(eps)); // was 0.5 was 0.47
743
744 // Truncate the number of binary digits in h's mantissa
745 // so that rounding does not occur when performing
746 // manipulations to determine the quadrature points and
747 // to limit the number of distinct values in case of
748 // multiple precisions being used at the same time.
749 h = floor(64.0*h)/64.0;
750
751
752 // Round shi/lo up/down to an integral multiple of quadrature points
753 shi = ceil(shi/h)*h;
754 slo = floor(slo/h)*h;
755
756 long npt = long((shi-slo)/h+0.5);
757
758 if (prnt)
759 std::cout << "bsh: mu " << mu << " lo " << lo << " hi " << hi
760 << " eps " << eps << " slo " << slo << " shi " << shi
761 << " npt " << npt << " h " << h << std::endl;
762
763
764 // Compute expansion pruning small coeffs and large exponents
765 Tensor<double> coeff(npt), expnt(npt);
766 int nnpt=0;
767 for (int i=0; i<npt; ++i) {
768 double s = slo + h*(npt-i); // i+1
769 double c = exp(-0.25*mu*mu*exp(-2.0*s)+(ndim-2)*s)*0.5/pow(constants::pi,0.5*ndim);
770 double p = exp(2.0*s);
771 c = c*h;
772 if (c*exp(-p*lo*lo) > eps) {
773 coeff(nnpt) = c;
774 expnt(nnpt) = p;
775 ++nnpt;
776 }
777 }
778 npt = nnpt;
779#if ONE_TERM
780 npt=1;
781 double s=1.0;
782 coeff[0]=1.0;
783 expnt[0] = exp(2.0*s);
784 coeff=coeff(Slice(0,0));
785 expnt=expnt(Slice(0,0));
786 print("only one term in gfit",s,coeff[0],expnt[0]);
787
788#endif
789
790
791 // Prune small exponents from Coulomb fit. Evaluate a gaussian at
792 // the range midpoint, and replace it there with the next most
793 // diffuse gaussian. Then examine the resulting error at the two
794 // end points ... if this error is less than the desired
795 // precision, can discard the diffuse gaussian.
796
797 if (mu == 0.0) {
798 double mid = lo + (hi-lo)*0.5;
799 long i;
800 for (i=npt-1; i>0; --i) {
801 double cnew = coeff[i]*exp(-(expnt[i]-expnt[i-1])*mid*mid);
802 double errlo = coeff[i]*exp(-expnt[i]*lo*lo) -
803 cnew*exp(-expnt[i-1]*lo*lo);
804 double errhi = coeff[i]*exp(-expnt[i]*hi*hi) -
805 cnew*exp(-expnt[i-1]*hi*hi);
806 if (std::max(std::abs(errlo),std::abs(errhi)) > 0.03*eps) break;
807 npt--;
808 coeff[i-1] = coeff[i-1] + cnew;
809 }
810 }
811
812 // Shrink array to correct size
813 coeff = coeff(Slice(0,npt-1));
814 expnt = expnt(Slice(0,npt-1));
815
816
817 if (prnt) {
818 for (int i=0; i<npt; ++i)
819 std::cout << i << " " << coeff[i] << " " << expnt[i] << std::endl;
820
821 long npt = 300;
822 std::cout << " x value" << std::endl;
823 std::cout << " ------------ ---------------------" << std::endl;
824 double step = exp(log(hi/lo)/(npt+1));
825 for (int i=0; i<=npt; ++i) {
826 double r = lo*(pow(step,i+0.5));
827 double test = 0.0;
828 for (int j=0; j<coeff.dim(0); ++j)
829 test += coeff[j]*exp(-r*r*expnt[j]);
830 printf(" %.6e %20.10e\n",r, test);
831 }
832 }
833
834 pcoeff = coeff;
835 pexpnt = expnt;
836 }
837
838 // Returns in q[0..4] int(r^2(n+1)*exp(-alpha*r^2),r=0..R) n=-1,0,1,2
839 static void gaussian_spherical_moments(double alpha, double R, Tensor<double>& q) {
840 q[0] = -(-0.1e1 + exp(-alpha * R*R)) / alpha / 0.2e1;
841 q[1] = (-0.2e1 * R * pow(alpha, 0.3e1 / 0.2e1) + sqrt(constants::pi)
842 * erf(R * sqrt(alpha)) * alpha * exp(alpha * R*R))
843 * pow(alpha, -0.5e1 / 0.2e1) * exp(-alpha * R*R) / 0.4e1;
844 q[2] = -(-0.1e1 + exp(-alpha * R*R) + exp(-alpha * R*R) * alpha * R*R)
845 * pow(alpha, -0.2e1) / 0.2e1;
846 q[3] = -(-0.3e1 * sqrt(constants::pi) * erf(R * sqrt(alpha)) * pow(alpha, 0.2e1)
847 * exp(alpha * R*R) + 0.6e1 * R * pow(alpha, 0.5e1 / 0.2e1)
848 + 0.4e1 * pow(R, 0.3e1) * pow(alpha, 0.7e1 / 0.2e1))
849 * pow(alpha, -0.9e1 / 0.2e1) * exp(-alpha * R*R) / 0.8e1;
850 }
851
852 // Returns in q[0..4] int(r^2(n+1)*exp(-mu*r)/(4*constants::pi*r),r=0..R) n=-1,0,1,2
853 static void bsh_spherical_moments(double mu, double R, Tensor<double>& q) {
854 if (mu == 0.0) {
855 q[0] = R / constants::pi / 0.4e1;
856 q[1] = pow(R, 0.2e1) / constants::pi / 0.8e1;
857 q[2] = pow(R, 0.3e1) / constants::pi / 0.12e2;
858 q[3] = pow(R, 0.4e1) / constants::pi / 0.16e2;
859 }
860 else {
861 q[0] = (exp(mu * R) - 0.1e1) / mu * exp(-mu * R) / constants::pi / 0.4e1;
862 q[1] = -(-exp(mu * R) + 0.1e1 + mu * R) * pow(mu, -0.2e1) / constants::pi
863 * exp(-mu * R) / 0.4e1;
864 q[2] = -(-0.2e1 * exp(mu * R) + 0.2e1 + 0.2e1 * mu * R + R*R *
865 pow(mu, 0.2e1))*pow(mu, -0.3e1) / constants::pi * exp(-mu * R) / 0.4e1;
866 q[3] = -(-0.6e1 * exp(mu * R) + 0.6e1 + 0.6e1 * mu * R + 0.3e1 * R*R
867 * pow(mu, 0.2e1) + pow(R, 0.3e1) * pow(mu, 0.3e1))
868 * pow(mu, -0.4e1) / constants::pi * exp(-mu * R) / 0.4e1;
869 }
870 }
871
872};
873
874
875}
876
877#endif // MADNESS_MRA_GFIT_H__INCLUDED
878
double q(double t)
Definition DKops.h:18
long size() const
Returns the number of elements in the tensor.
Definition basetensor.h:138
Definition gfit.h:59
Tensor< T > coeffs() const
return the coefficients of the fit
Definition gfit.h:309
void print_accuracy(opT op, const double lo, const double hi) const
print coefficients and exponents, and values and errors
Definition gfit.h:406
Tensor< T > exponents() const
return the exponents of the fit
Definition gfit.h:312
static void prune_small_coefficients(const double eps, const double lo, const double hi, Tensor< double > &coeff, Tensor< double > &expnt, const long first_prunable=1)
Definition gfit.h:318
static GFit GaussFit(double gamma, double lo, double hi, double eps, bool prnt=false)
return a (trivial) fit for a single Gauss function
Definition gfit.h:162
GFit()=default
default ctor does nothing
static GFit F2GFit(double gamma, double lo, double hi, double eps, bool prnt=false)
return a fit for the F2G function
Definition gfit.h:263
GFit(const funcT &f)
ctor taking an isotropic function
Definition gfit.h:396
static void f12_fit(double gamma, double lo, double hi, double eps, Tensor< double > &pcoeff, Tensor< double > &pexpnt, bool prnt)
fit a correlation factor (1- exp(-mu r))
Definition gfit.h:676
static void slater_fit(double gamma, double lo, double hi, double eps, Tensor< double > &pcoeff, Tensor< double > &pexpnt, bool prnt)
fit a Slater function using a sum of Gaussians
Definition gfit.h:602
static void bsh_spherical_moments(double mu, double R, Tensor< double > &q)
Definition gfit.h:853
static GFit BSHFit(double mu, double lo, double hi, double eps, bool prnt=false)
return a fit for the bound-state Helmholtz function
Definition gfit.h:119
static GFit GeneralFit()
return a fit for a general isotropic function
Definition gfit.h:303
Tensor< T > exponents_
the exponents of the expansion f(x) = \sum_m coeffs[m] exp(-exponents[m] * x^2)
Definition gfit.h:433
static GFit FGFit(double gamma, double lo, double hi, double eps, bool prnt=false)
return a fit for the FG function
Definition gfit.h:229
static void f12sq_fit(double gamma, double lo, double hi, double eps, Tensor< double > &pcoeff, Tensor< double > &pexpnt, bool prnt)
fit a correlation factor f12^2 = (1- exp(-mu r))^2 = 1 - 2 exp(-mu r) + exp(-2 mu r)
Definition gfit.h:695
static GFit F12sqFit(double gamma, double lo, double hi, double eps, bool prnt=false)
return a fit for the F12^2 correlation factor
Definition gfit.h:206
static void bsh_fit(double mu, double lo, double hi, double eps, Tensor< double > &pcoeff, Tensor< double > &pexpnt, bool prnt, bool fix_interval)
fit the function exp(-mu r)/r
Definition gfit.h:444
static void gaussian_spherical_moments(double alpha, double R, Tensor< double > &q)
Definition gfit.h:839
static GFit F12Fit(double gamma, double lo, double hi, double eps, bool prnt=false)
return a fit for the F12 correlation factor
Definition gfit.h:185
GFit & operator=(const GFit &other)
assignment operator
Definition gfit.h:95
static GFit SlaterFit(double gamma, double lo, double hi, double eps, bool prnt=false)
return a fit for the Slater function
Definition gfit.h:142
static GFit CoulombFit(double lo, double hi, double eps, bool prnt=false)
return a fit for the Coulomb function
Definition gfit.h:104
static void truncate_mixed_expansion(Tensor< double > &c, Tensor< double > &e, const std::array< LatticeRange, NDIM > &lattice_ranges, const Tensor< double > &cell_width, double lo, double hi_fin, double eps)
Truncate the fit of a kernel that is lattice-summed along some axes.
Definition gfit.h:356
static void bsh_fit_ndim(int ndim, double mu, double lo, double hi, double eps, Tensor< double > &pcoeff, Tensor< double > &pexpnt, bool prnt)
Definition gfit.h:717
GFit(const GFit &other)=default
copy constructor
GFit(OperatorInfo info)
Definition gfit.h:66
Tensor< T > coeffs_
the coefficients of the expansion f(x) = \sum_m coeffs[m] exp(-exponents[m] * x^2)
Definition gfit.h:430
A slice defines a sub-range or patch of a dimension.
Definition slice.h:103
A tensor is a multidimensional array.
Definition tensor.h:318
static const double R
Definition csqrt.cc:46
char * p(char *buf, const char *name, int k, int initial_level, double thresh, int order)
Definition derivatives.cc:72
static double lo
Definition dirac-hatom.cc:23
static bool debug
Definition dirac-hatom.cc:16
Tensor< double > op(const Tensor< double > &x)
Definition kain.cc:508
static double pow(const double *a, const double *b)
Definition lda.h:74
#define MADNESS_CHECK(condition)
Check a condition — even in a release build the condition is always evaluated so it can have side eff...
Definition madness_exception.h:182
#define MADNESS_EXCEPTION(msg, value)
Macro for throwing a MADNESS exception.
Definition madness_exception.h:119
#define MADNESS_CHECK_THROW(condition, msg)
Check a condition — even in a release build the condition is always evaluated so it can have side eff...
Definition madness_exception.h:207
constexpr double pi
Mathematical constant .
Definition constants.h:48
Namespace for all elements and tools of MADNESS.
Definition DFConvergence.h:9
static const Slice _(0,-1, 1)
OpType
operator types
Definition operatorinfo.h:11
@ OT_FG12
1-exp(-r)
Definition operatorinfo.h:18
@ OT_SLATER
1/r
Definition operatorinfo.h:15
@ OT_GAUSS
exp(-r)
Definition operatorinfo.h:16
@ OT_BSH
(1-exp(-r))^2/r = 1/r + exp(-2r)/r - 2 exp(-r)/r
Definition operatorinfo.h:21
@ OT_F12
exp(-r2)
Definition operatorinfo.h:17
@ OT_F212
(1-exp(-r))/r
Definition operatorinfo.h:19
@ OT_G12
indicates the identity
Definition operatorinfo.h:14
@ OT_F2G12
(1-exp(-r))^2
Definition operatorinfo.h:20
void gesv(const Tensor< T > &a, const Tensor< T > &b, Tensor< T > &x)
Solve Ax = b for general A using the LAPACK *gesv routines.
Definition lapack.cc:804
void print(const T &t, const Ts &... ts)
Print items to std::cout (items separated by spaces) and terminate with a new line.
Definition print.h:227
NDIM & f
Definition mra.h:2668
std::string type(const PairType &n)
Definition PNOParameters.h:18
static long abs(long a)
Definition tensor.h:219
const double mu
Definition navstokes_cosines.cc:95
static const double b
Definition nonlinschro.cc:119
static const double d
Definition nonlinschro.cc:121
static const double c
Definition relops.cc:10
Definition operatorinfo.h:58
double hi
Definition operatorinfo.h:67
double thresh
Definition operatorinfo.h:65
bool debug
Definition operatorinfo.h:69
OpType type
introspection
Definition operatorinfo.h:66
double mu
some introspection
Definition operatorinfo.h:63
double lo
Definition operatorinfo.h:64
void e()
Definition test_sig.cc:75
static const double alpha
Definition testcosine.cc:10
double(* exact)(double, double, double)
Definition testfuns.cc:6
std::vector< double > fit(size_t m, size_t n, const std::vector< double > N, const std::vector< double > &f)
Definition testfuns.cc:36
constexpr std::size_t NDIM
Definition testgconv.cc:54
double h(const coord_1d &r)
Definition testgconv.cc:175
void test()
Definition y.cc:696