MADNESS 0.10.1
electronic_correlation_factor.h
Go to the documentation of this file.
1/*
2 * electronic_correlation_factor.h
3 *
4 * Created on: Jul 9, 2015
5 * Author: fbischoff
6 */
7
8#ifndef SRC_APPS_CHEM_ELECTRONIC_CORRELATION_FACTOR_H_
9#define SRC_APPS_CHEM_ELECTRONIC_CORRELATION_FACTOR_H_
10
11
12#include <madness/mra/mra.h>
13#include <madness/mra/lbdeux.h>
18#include <iomanip>
19
20#include "CCStructures.h"
21
22namespace madness {
23 /// a class holding the electronic correlation factor for R12 theory
24 class CorrelationFactor {
25 World& world;
26 double _gamma; ///< the correlation factor exp(-gamma r12)
27 double dcut; ///< the cutoff for the 1/r potential
28 double lo; ///< smallest length scale to be resolved
29 int truncate_mode=1;
30 double tight_thresh_6d=-1.0; ///< thresh for truncations, keep deliberately tight
31
32 public:
33 /// ctor, use negative gamma for linear correlation factor r12
34 CorrelationFactor(World& world) : world(world), _gamma(-1.0), dcut(1.e-10),
35 lo(1.e-10), tight_thresh_6d(FunctionDefaults<6>::get_thresh()*0.1) {}
36
37 /// ctor, use negative gamma for linear correlation factor r12
38 CorrelationFactor(World& world, const double& gamma, const double dcut,
39 const Molecule& molecule) : world(world), _gamma(gamma), dcut(dcut), tight_thresh_6d(FunctionDefaults<6>::get_thresh()*0.1) {
40 lo = 1.e-6; //lo = molecule.smallest_length_scale();
41 // if (world.rank()==0) {
42 // if (gamma>0.0) print("constructed correlation factor with gamma=",gamma);
43 // else if (gamma==0.0) print("constructed linear correlation factor");
44 // }
45 }
46
47 /// ctor, use negative gamma for linear correlation factor r12
48 CorrelationFactor(World& world, const double& gamma, const double dcut,
49 const double lo, const double tt6=FunctionDefaults<6>::get_thresh()*0.1)
50 : world(world), _gamma(gamma), dcut(dcut), lo(lo), tight_thresh_6d(tt6) {
51 // if (world.rank()==0) {
52 // if (gamma>0.0) print("constructed correlation factor with gamma=",gamma);
53 // else if (gamma==0.0) print("constructed linear correlation factor");
54 // }
55 }
56
57 /// copy ctor
58 CorrelationFactor(const CorrelationFactor& other) : world(other.world) {
59 _gamma = other._gamma;
60 dcut = other.dcut;
61 lo = other.lo;
62 tight_thresh_6d = other.tight_thresh_6d;
63 }
64
65 /// assignment; assume other's world is this world
66 CorrelationFactor& operator=(const CorrelationFactor& other) {
67 _gamma = other._gamma;
68 dcut = other.dcut;
69 lo = other.lo;
70 tight_thresh_6d = other.tight_thresh_6d;
71 return *this;
72 }
73
74 /// return the exponent of this correlation factor
75 double gamma() const { return _gamma; }
76
77 void set_truncate_mode(int mode) {
78 truncate_mode=mode;
79 }
80
81 /// return the value of the correlation factor
82 double operator()(const coord_6d& r) const {
83 const double rr = r12(r);
84 if (_gamma > 0.0) return (1.0 - exp(-_gamma * rr)) / (2.0 * _gamma);
85 return 0.5 * rr;
86 }
87
88 /// compute the local part of the Ue potential
89 real_function_6d apply_U_local(const real_function_3d& phi_i, const real_function_3d& phi_j,
90 const real_convolution_6d& op_mod, double thresh) const {
91 if (world.rank()==0) print("applying local part of Ue term");
92 double fac=1.0;
93 if (truncate_mode<0) fac = 0.1;
94 print("truncate mode",truncate_mode);
95 print("factor for thresh",fac);
96 fg_ func(_gamma, dcut);
98 real_function_6d mul = CompositeFactory<double, 6, 3>(world)
99 .g12(fg3).particle1(copy(phi_i)).particle2(copy(phi_j)).thresh(thresh*fac)
100 .truncate_mode(truncate_mode);
101 mul.fill_cuspy_tree(op_mod).truncate(tight_thresh_6d);
102 mul.print_size("local Ue|ij>");
103 return mul;
104 }
105
106 /// compute the semi-local part of the Ue potential containing derivatives
107 real_function_6d apply_U_semilocal(const real_function_3d& phi_i, const real_function_3d& phi_j,
108 const real_convolution_6d& op_mod, double thresh, const bool symmetric = false) const {
109 if (world.rank()==0) print("applying semi-local part of Ue term");
110 double fac=1.0;
111 if (truncate_mode<0) fac = 0.1;
112 print("truncate mode",truncate_mode);
113 print("factor for thresh",fac);
114
115 auto dphi_i=grad(phi_i);
116 auto dphi_j=grad(phi_j);
117
119 for (int axis = 0; axis < 3; ++axis) {
120 real_function_6d u = U1(axis);
121
122 std::vector<real_function_3d> p1({copy(dphi_i[axis]),copy(phi_i)});
123 std::vector<real_function_3d> p2({copy(phi_j),-1.0*copy(dphi_j[axis])});
124
125 real_function_6d tmp = CompositeFactory<double, 6, 3>(world)
126 .g12(u).particle1(p1).particle2(p2).thresh(thresh*fac)
127 .truncate_mode(truncate_mode);
128 tmp.fill_cuspy_tree(op_mod).truncate(tight_thresh_6d);
129 result = result + tmp;
130 world.gop.fence();
131 }
132 result.truncate(tight_thresh_6d).reduce_rank();
133 result.print_size("semi-local Ue|ij>");
134 return result;
135 }
136
137 /// compute the double commutator electronic/nuclear correlation factor: R^{-1}[[T,f],R]
138
139 /// @param[in] U1nuc the U1 potential of the nuclear correlation factor
140 real_function_6d apply_U_mixed_commutator(const real_function_3d& phi_i, const real_function_3d& phi_j,
141 const real_convolution_6d& op_mod, double thresh,
142 const std::vector<real_function_3d> U1nuc)const {
143 if (world.rank()==0) print("applying mixed double commutator of the Ue term");
144 double fac=1.0;
145 if (truncate_mode<0) fac = 0.1;
146 print("truncate mode",truncate_mode);
147 print("factor for thresh",fac);
148
149 auto dphi_i=U1nuc*phi_i;
150 auto dphi_j=U1nuc*phi_j;
151
153 for (int axis = 0; axis < 3; ++axis) {
154 real_function_6d u = U1(axis);
155
156 std::vector<real_function_3d> p1({copy(dphi_i[axis]),copy(phi_i)});
157 std::vector<real_function_3d> p2({copy(phi_j),-1.0*copy(dphi_j[axis])});
158
159 real_function_6d tmp = CompositeFactory<double, 6, 3>(world)
160 .g12(u).particle1(p1).particle2(p2).thresh(thresh*fac)
161 .truncate_mode(truncate_mode);
162 tmp.fill_cuspy_tree(op_mod).truncate(tight_thresh_6d);
163 result = result + tmp;
164 world.gop.fence();
165 }
166 result.truncate(tight_thresh_6d).reduce_rank();
167 result.print_size("mixed commutator of Ue: R^{-1} [[T,f12],R] |ij>");
168 return result;
169 }
170
171
172 /// apply Kutzelnigg's regularized potential to an orbital product
173
174 /// If an observer basis `ao` is given, run the Ue diagnostics (local,
175 /// semilocal, mixed). If additionally an occupied ket space `occ_ket` is
176 /// given, run the GQUe diagnostics: apply Q12 and G = BSH(mu) to each Ue
177 /// piece and compare against the 3D-only Schwinger reference; the pair
178 /// energy E = -mu^2/2 is taken from op_mod.
179 ///
180 /// Bra and ket spaces may differ (nemo-style |k> <-> <k|R²): pass occ_bra
181 /// (e.g. info.mo_bra); Q12 is then built as O = Σ|k_ket><k_bra|. If the
182 /// bra counterparts phi_i_bra/phi_j_bra of the pair orbitals are given,
183 /// an additional diagnostic in the raw pair-bra basis {phi_i_bra, phi_j_bra}
184 /// is run, whose (0,1) element <i_bra j_bra|G Q12 Ue|ij> is the contribution
185 /// of each Ue piece to the MP2 energy of pair (ij).
186 real_function_6d apply_U(const real_function_3d& phi_i, const real_function_3d& phi_j,
187 const real_convolution_6d& op_mod, const std::vector<real_function_3d>& U1nuc,
188 const std::vector<real_function_3d> ao=std::vector<real_function_3d>(),
189 const std::vector<real_function_3d> occ_ket=std::vector<real_function_3d>(),
190 const std::vector<real_function_3d> occ_bra=std::vector<real_function_3d>(),
191 const real_function_3d phi_i_bra=real_function_3d(),
192 const real_function_3d phi_j_bra=real_function_3d(),
193 const double tight_thresh_3d=-1.0) const {
194 MADNESS_CHECK_THROW(op_mod.modified(), "ElectronicCorrelationFactor::apply_U, op_mod must be in modified_NS form");
195 const double thresh = FunctionDefaults<6>::get_thresh();
196
197 real_function_6d local=apply_U_local(phi_i, phi_j,op_mod,thresh);
198 real_function_6d semilocal=apply_U_semilocal(phi_i, phi_j,op_mod,thresh);
199 real_function_6d mixed=apply_U_mixed_commutator(phi_i, phi_j,op_mod,thresh,U1nuc);
200 real_function_6d result=local+semilocal-mixed;
201 result.truncate(tight_thresh_6d).reduce_rank();
202
203 // do some diagnostics (defined in electronic_correlation_factor.cc)
204 if (ao.size()>0)
205 run_apply_U_diagnostics(local, semilocal, mixed, phi_i, phi_j, op_mod,
206 U1nuc, ao, occ_ket, occ_bra, phi_i_bra, phi_j_bra,
207 tight_thresh_3d);
208
209 result.print_size("Ue|ij>");
210 return result;
211 }
212
213 /// return the U1 term of the correlation function
214 real_function_6d U1(const int axis) const {
215 U func(_gamma, axis, dcut);
216 const real_function_6d u1 = real_factory_6d(world)
218 return u1;
219 }
220
221 /// return the U1 term of the correlation function
222 real_function_6d U2() const {
223 if (world.rank() == 0) print("U2 for the electronic correlation factor");
224 if (world.rank() == 0) print("is expensive -- do you really need it??");
225 MADNESS_EXCEPTION("U2() not implemented, since it might be expensive", 1);
226 return real_factory_6d(world);
227 }
228
229 /// return the correlation factor as on-demand function
230 real_function_6d f() const {
231 // real_function_6d tmp=real_factory_6d(world).functor2(*this).is_on_demand();
232 double thresh = FunctionDefaults<3>::get_thresh();
233 real_function_6d tmp = TwoElectronFactory(world)
234 .dcut(dcut).gamma(_gamma).f12().thresh(thresh);
235 return tmp;
236 }
237
238 /// return f^2 as on-demand function
239 real_function_6d f2() const {
240 f2_ func(_gamma);
242 return tmp;
243 }
244
245 /// return fg+sth as on-demand function
246 real_function_6d fg() const {
247 fg_ func(_gamma, dcut);
249 return tmp;
250 }
251
252 /// return f/r as on-demand function
253 real_function_6d f_over_r() const {
254 f_over_r_ func(_gamma, dcut);
256 return tmp;
257 }
258
259 /// return (\nabla f)^2 as on-demand functions
260 real_function_6d nablaf2() const {
261 nablaf2_ func(_gamma);
263 return tmp;
264 }
265
266 /// compute the error in Uphi by comparing to reference values: project onto a (small) aobasis
267
268 /// NOTE: diagnose_Ue, diagnose_GUe and diagnose_GQUe share the same signature;
269 /// parameters not needed by a particular diagnostic are ignored (here:
270 /// occ_ket, occ_bra, energy — pass {} and 0.0).
271 /// @param[in] Uphi_mixed U_mixed|ij> (optional; pass default-constructed to skip)
272 /// @param[in] U1nuc nuclear U1 potentials (required when Uphi_mixed is provided)
273 /// @return DiagnosticMatrix with entries "local", "semilocal" (and "mixed" if applicable).
274 /// ref=3D analytic formula, result=6D projection <ab|U|ij>, error=||ref-result||.
275 DiagnosticMatrix<> diagnose_Ue(const real_function_6d& Uphi_local,
276 const real_function_6d& Uphi_semilocal,
277 const real_function_3d& phi_i, const real_function_3d& phi_j,
278 const std::vector<real_function_3d>& aobasis,
279 const std::vector<real_function_3d>& /*occ_ket*/,
280 const std::vector<real_function_3d>& /*occ_bra*/,
281 const double /*energy*/,
282 const real_function_6d& Uphi_mixed = real_function_6d(),
283 const std::vector<real_function_3d>& U1nuc = {},
284 const bool orthonormalize_basis = true,
285 const double tight_thresh_3d = -1.0) const {
286 DiagnosticMatrix<> dm(world, aobasis, orthonormalize_basis);
287 const double tt = (tight_thresh_3d > 0.0) ? tight_thresh_3d : FunctionDefaults<3>::get_thresh()*0.1;
288 auto bra = dm.build_simple_bra();
289 return diagnose_Ue_impl(std::move(dm), bra,
290 Uphi_local, Uphi_semilocal, phi_i, phi_j,
291 "local", "semilocal",
292 Uphi_mixed, U1nuc, "mixed", tt);
293 }
294
295 /// compute <ab|(T-E)^{-1} Ue|ij> via 3D Gaussian quadrature of the Schwinger representation
296 /// and compare against the 6D reference obtained by projecting G U_local/U_semilocal |ij>.
297 ///
298 /// The 6D Green's function G = (T-E)^{-1} is written via the Schwinger proper-time expansion
299 /// G(r1,r2;r1',r2') = int_0^inf e^{tE} h_t(r1-r1') h_t(r2-r2') dt
300 /// where h_t is the 3D heat kernel. Discretizing via the BSH Gaussian fit with
301 /// mu = sqrt(-2E) gives the separable approximation
302 /// G ≈ sum_n w_n^{6d} g_{alpha_n}(r1-r1') g_{alpha_n}(r2-r2')
303 /// with 6D weights w_n^{6d} = c_n^{bsh} * (alpha_n/pi)^{3/2}.
304 ///
305 /// The matrix elements then reduce to
306 /// M(a,b) ≈ sum_n w_n^{6d} <ã_n b̃_n | Ue | ij>
307 /// where ã_n = g_{alpha_n} * a (3D Gaussian convolution).
308 ///
309 /// NOTE: diagnose_Ue, diagnose_GUe and diagnose_GQUe share the same signature;
310 /// parameters not needed by a particular diagnostic are ignored (here:
311 /// occ_ket, occ_bra — pass {}).
312 /// @param[in] GUphi_local G U_local |ij> as a 6D MRA function (reference)
313 /// @param[in] GUphi_semilocal G U_semilocal|ij> as a 6D MRA function (reference)
314 /// @param[in] phi_i orbital i (particle 1)
315 /// @param[in] phi_j orbital j (particle 2)
316 /// @param[in] aobasis projector basis {a}
317 /// @param[in] energy two-particle energy E = eps_i + eps_j (must be < 0)
318 /// @param[in] GUphi_mixed G U_mixed|ij> (optional; pass default-constructed to skip)
319 /// @param[in] U1nuc nuclear U1 potentials (required when GUphi_mixed is provided)
320 /// @return DiagnosticMatrix with entries "Glocal", "Gsemilocal" (and "Gmixed" if applicable).
321 /// ref=3D Schwinger quadrature, result=6D projection <ab|G Ue|ij>, error=||ref-result||.
322 DiagnosticMatrix<> diagnose_GUe(const real_function_6d& GUphi_local,
323 const real_function_6d& GUphi_semilocal,
324 const real_function_3d& phi_i,
325 const real_function_3d& phi_j,
326 const std::vector<real_function_3d>& aobasis,
327 const std::vector<real_function_3d>& /*occ_ket*/,
328 const std::vector<real_function_3d>& /*occ_bra*/,
329 const double energy,
330 const real_function_6d& GUphi_mixed = real_function_6d(),
331 const std::vector<real_function_3d>& U1nuc = {},
332 const bool orthonormalize_basis = true,
333 const double tight_thresh_3d = -1.0) const {
334 MADNESS_CHECK_THROW(energy < 0.0, "diagnose_GUe: energy must be negative");
335 DiagnosticMatrix<> dm(world, aobasis, orthonormalize_basis);
336 // tight fit thresh & lo for a threshold-decoupled 3D reference
337 const double tt = (tight_thresh_3d > 0.0) ? tight_thresh_3d : FunctionDefaults<3>::get_thresh()*0.1;
338 auto bra = dm.build_Gab_bra(energy, tt, tt);
339 return diagnose_Ue_impl(std::move(dm), bra,
340 GUphi_local, GUphi_semilocal, phi_i, phi_j,
341 "Glocal", "Gsemilocal",
342 GUphi_mixed, U1nuc, "Gmixed", tt);
343 }
344
345 /// Diagnose <ab|G Q₁₂ Ue|ij> where Q₁₂ = (1-O₁)(1-O₂).
346 ///
347 /// Caller supplies G·Q₁₂ Ue|ij> (the 6D result, e.g. reused from the
348 /// production pair update — neither G nor Q is applied internally).
349 /// The reference is computed via 3D functions only by
350 /// DiagnosticMatrix::ref_GQab: G is moved to the bra via the Schwinger/BSH
351 /// fit and Q₁₂ = 1 − O₁ − O₂ + O₁O₂, O = Σ_k |k_ket><k_bra|, is expanded
352 /// on the ket side as scalar contractions of the analytic Ue matrix
353 /// elements (no catastrophic cancellation) — see operator_diagnostics.md.
354 ///
355 /// @param GQ12Uphi_local G Q₁₂ Ue_local |ij> (G, Q applied by the caller)
356 /// @param GQ12Uphi_semilocal G Q₁₂ Ue_semilocal|ij> (idem)
357 /// @param occ_ket occupied ket orbitals |k⟩ of O = Σₖ|k_ket⟩⟨k_bra|
358 /// @param occ_bra occupied bra orbitals ⟨k| (= R²·k_ket for nemo;
359 /// pass occ_ket again when bra and ket coincide)
360 /// @param GQ12Uphi_mixed G Q₁₂ Ue_mixed|ij> (optional; pass default-constructed to skip)
361 /// @param U1nuc nuclear U1 potentials (required when GQ12Uphi_mixed is provided)
362 /// @param orthonormalize_basis Löwdin-orthonormalize aobasis (pass false to
363 /// keep raw, e.g. when elements carry physical meaning)
364 /// @return DiagnosticMatrix with entries "GQlocal", "GQsemilocal" (and "GQmixed" if applicable).
365 /// ref=3D Schwinger quadrature with Q12 expanded on the ket,
366 /// result=6D projection <ab|G Q₁₂ Ue|ij>.
367 DiagnosticMatrix<> diagnose_GQUe(
368 const real_function_6d& GQ12Uphi_local,
369 const real_function_6d& GQ12Uphi_semilocal,
370 const real_function_3d& phi_i,
371 const real_function_3d& phi_j,
372 const std::vector<real_function_3d>& aobasis,
373 const std::vector<real_function_3d>& occ_ket,
374 const std::vector<real_function_3d>& occ_bra,
375 const double energy,
376 const real_function_6d& GQ12Uphi_mixed = real_function_6d(),
377 const std::vector<real_function_3d>& U1nuc = {},
378 const bool orthonormalize_basis = true,
379 const double tight_thresh_3d = -1.0) const;
380
381 /// Quick 3D-only references for Ue, GUe and GQUe (no 6D work).
382 ///
383 /// Reuses the Ue element providers and the Schwinger/Q12 reference
384 /// builders to evaluate the <i_bra j_bra|X|ij> 3D reference for X = Ue,
385 /// G Ue and G Q12 Ue in the raw pair-bra basis, and prints the combined
386 /// (local + semilocal - mixed) value of each. Intended to be called
387 /// before the (expensive) constant-part macrotask so the references can
388 /// be compared cheaply. energy = eps_i + eps_j (< 0); pass
389 /// tight_thresh_3d to match the reference fit accuracy used in the
390 /// constant-part diagnostics.
391 void compute_3d_references(const real_function_3d& phi_i, const real_function_3d& phi_j,
392 const real_function_3d& phi_i_bra, const real_function_3d& phi_j_bra,
393 const std::vector<real_function_3d>& occ_ket,
394 const std::vector<real_function_3d>& occ_bra,
395 const double energy, const std::vector<real_function_3d>& U1nuc,
396 const double tight_thresh_3d = -1.0) const;
397
398 private:
399 /// the diagnostics block of apply_U: Ue, GQUe, and the MP2 pair-energy
400 /// report in the raw pair-bra basis (defined in electronic_correlation_factor.cc)
401 void run_apply_U_diagnostics(
402 const real_function_6d& local,
403 const real_function_6d& semilocal,
404 const real_function_6d& mixed,
405 const real_function_3d& phi_i, const real_function_3d& phi_j,
406 const real_convolution_6d& op_mod,
407 const std::vector<real_function_3d>& U1nuc,
408 const std::vector<real_function_3d>& ao,
409 const std::vector<real_function_3d>& occ_ket,
410 const std::vector<real_function_3d>& occ_bra,
411 const real_function_3d& phi_i_bra,
412 const real_function_3d& phi_j_bra,
413 const double tight_thresh_3d=-1.0) const;
414 /// Unified core for diagnose_Ue and diagnose_GUe.
415 ///
416 /// @param dm DiagnosticMatrix holding the observer basis (already
417 /// orthonormalized by its constructor if requested); the
418 /// bra must have been built from this same dm. Filled
419 /// with entries and returned.
420 /// @param bra Observer bra: simple (build_simple_bra) or Schwinger (build_Gab_bra).
421 /// Controls the 3D ref computation only.
422 /// bra[k].get_a()[a] = particle-1 function for basis index a (weight absorbed)
423 /// bra[k].get_b()[b] = particle-2 function for basis index b
424 /// @param Uphi_local B·U_local|ij> (6D MRA, B already applied)
425 /// @param Uphi_semilocal B·U_semilocal|ij> (6D MRA, B already applied)
426 /// @param name_local / name_semilocal / name_mixed entry keys in the returned DiagnosticMatrix
427 DiagnosticMatrix<> diagnose_Ue_impl(
428 DiagnosticMatrix<> dm,
429 const std::vector<CCPairFunction<double,6>>& bra,
430 const real_function_6d& Uphi_local,
431 const real_function_6d& Uphi_semilocal,
432 const real_function_3d& phi_i, const real_function_3d& phi_j,
433 const std::string& name_local, const std::string& name_semilocal,
434 const real_function_6d& Uphi_mixed = real_function_6d(),
435 const std::vector<real_function_3d>& U1nuc = {},
436 const std::string& name_mixed = "", double eps = -1.0) const;
437
438 public:
439 // Ue element-provider factories — public so external diagnostics (e.g.
440 // the RI MP2 test in CCPotentials::make_constant_part_macrotask) can
441 // build the 3D reference, mirroring ExchangeCommutator::{kf,fk}_provider.
442
443 /// element provider: M(x,y) = <p1[x](1) p2[y](2) | Ue_local | ij>
444
445 /// OT_FG12(γ) kernel = (1-exp(-γr))/(2γr); U_local = 2γ·FG12 + (γ/2)·SLATER.
446 /// The convolution operators are built once and captured in the closure.
447 DiagnosticMatrix<>::ElementProvider ue_local_provider(
448 const real_function_3d& phi_i, const real_function_3d& phi_j,
449 double eps = -1.0) const;
450
451 /// element provider: M(x,y) = <p1[x](1) p2[y](2) | Ue_semilocal | ij>
452
453 /// r12·∇12|ij> decomposes into the four convolution terms +A -B -C +D.
454 /// The BSH operator and the orbital intermediates (r, ∇φ, rφ, ĩ, j̃) are
455 /// built once and captured in the closure.
456 DiagnosticMatrix<>::ElementProvider ue_semilocal_provider(
457 const real_function_3d& phi_i, const real_function_3d& phi_j,
458 double eps = -1.0) const;
459
460 /// element provider: M(x,y) = <p1[x](1) p2[y](2) | Ue_mixed | ij>
461
462 /// mixed commutator: same structure as semilocal with ∇φ → U1nuc·φ.
463 /// Operator and intermediates are built once and captured in the closure.
464 DiagnosticMatrix<>::ElementProvider ue_mixed_provider(
465 const real_function_3d& phi_i, const real_function_3d& phi_j,
466 const std::vector<real_function_3d>& U1nuc, double eps = -1.0) const;
467
468 private:
469 /// functor for the local potential (1-f12)/r12 + sth (doubly connected term of the commutator)
470
471 /// TODO: turn this into coeffs directly
472 struct fg_ : FunctionFunctorInterface<double, 6> {
473 double gamma;
474 double dcut;
475
476 fg_(double gamma, double dcut) : gamma(gamma), dcut(dcut) {
477 MADNESS_ASSERT(gamma>0.0);
478 }
479
480 using FunctionFunctorInterface<double,6>::operator();
481
482 double operator()(const coord_6d& r) const override {
483 const double rr = r12(r);
484 const double e = exp(-gamma * rr);
485 if (rr < 5.e-2) {
486 double value = 1.5 * gamma - 1. * std::pow(gamma, 2) * rr + 0.41666666666666663 * std::pow(gamma, 3)
487 * std::pow(rr, 2) -
488 0.125 * std::pow(gamma, 4) * std::pow(rr, 3) + 0.029166666666666667 * std::pow(gamma, 5) *
489 std::pow(rr, 4)
490 + 0.005555555555555556 * std::pow(gamma, 6) * std::pow(rr, 5) +
491 0.0008928571428571429 * std::pow(gamma, 7) * std::pow(rr, 6);
492 return value;
493 }
494 else {
495 // return (1.0-e)*u(rr,dcut) + 0.5*gamma*e;
496 return (1.0 - e) / rr + 0.5 * gamma * e;
497 }
498 }
499 };
500
501 /// functor for the local potential (1-f12)/r12
502 struct f_over_r_ : FunctionFunctorInterface<double, 6> {
503 double gamma;
504 double dcut;
505
506 f_over_r_(double gamma, double dcut) : gamma(gamma), dcut(dcut) {
507 MADNESS_ASSERT(gamma>0.0);
508 }
509
510 using FunctionFunctorInterface<double,6>::operator();
511
512 double operator()(const coord_6d& r) const override {
513 const double rr = r12(r);
514 const double e = exp(-gamma * rr);
515 return (1.0 - e) * u(rr, dcut) / (2.0 * gamma);
516 }
517 };
518
519 /// functor for the local part of the regularized potential: f12/r12*(r1-r2)(D1-D2)
520 struct U : FunctionFunctorInterface<double, 6> {
521 double gamma;
522 int axis;
523 double dcut;
524
525 U(double gamma, int axis, double dcut) : gamma(gamma), axis(axis),
526 dcut(dcut) {
527 MADNESS_ASSERT(axis>=0 and axis<3);
528 }
529 using FunctionFunctorInterface<double,6>::operator();
530 double operator()(const coord_6d& r) const override {
531 const double rr = r12(r);
532 const coord_3d vr12{r[0] - r[3], r[1] - r[4], r[2] - r[5]};
533 const coord_3d N = unitvec(vr12);
534 if (gamma > 0.0) return -0.5 * exp(-gamma * rr) * N[axis];
535 MADNESS_EXCEPTION("no gamma in electronic corrfac::U1", 1);
536 // const double rr=r12(r);
537 // const double g12=u(rr,dcut);
538 // double a=0.5;
539 // if (gamma>0.0) a=0.5*exp(-gamma*rr);
540 // return -a*x12(r,axis) * g12;
541 }
542 };
543
544 /// functor for the local potential (1-f12)^2
545 struct f2_ : FunctionFunctorInterface<double, 6> {
546 double gamma;
547 f2_(double gamma) : gamma(gamma) { MADNESS_ASSERT(gamma>0.0); }
548 using FunctionFunctorInterface<double,6>::operator();
549 double operator()(const coord_6d& r) const override {
550 const double rr = r12(r);
551 const double e = exp(-gamma * rr);
552 const double f = (1.0 - e) / (2.0 * gamma);
553 return f * f;
554 }
555 };
556
557 /// functor for the local potential (\nabla f)^2
558 struct nablaf2_ : FunctionFunctorInterface<double, 6> {
559 double gamma;
560
561 nablaf2_(double gamma) : gamma(gamma) {
562 MADNESS_ASSERT(gamma>0.0);
563 MADNESS_ASSERT(gamma==1.0);
564 }
565 using FunctionFunctorInterface<double,6>::operator();
566 double operator()(const coord_6d& r) const override {
567 const double rr = r12(r);
568 const double f = exp(-2.0 * gamma * rr) / (4.0 * gamma * gamma);
569 return f;
570 }
571 };
572
573 /// Smoothed 1/r potential (c is the smoothing distance)
574 static double u(double r, double c) {
575 r = r / c;
576 double r2 = r * r, pot;
577 if (r > 6.5) {
578 pot = 1.0 / r;
579 }
580 else if (r > 1e-2) {
581 pot = erf(r) / r + exp(-r2) * 0.56418958354775630;
582 }
583 else {
584 pot = 1.6925687506432689 - r2 * (0.94031597257959381 - r2 * (0.39493270848342941 - 0.12089776790309064 *
585 r2));
586 }
587 return pot / c;
588 }
589
590 static double r12(const coord_6d& r) {
591 const double x12 = r[0] - r[3];
592 const double y12 = r[1] - r[4];
593 const double z12 = r[2] - r[5];
594 const double r12 = sqrt(x12 * x12 + y12 * y12 + z12 * z12);
595 return r12;
596 }
597
598 static double x12(const coord_6d& r, const int axis) {
599 return r[axis] - r[axis + 3];
600 }
601
602 static coord_3d smoothed_unitvec(const coord_3d& xyz, double smoothing) {
603 // if (smoothing==0.0) smoothing=molecule.get_eprec();
604 // TODO:need to test this
605 // reduce the smoothing for the unitvector
606 //if (not (this->type()==None or this->type()==Two)) smoothing=sqrt(smoothing);
607 const double r = xyz.normf();
608 const double cutoff = smoothing;
609 if (r > cutoff) {
610 return 1.0 / r * xyz;
611 }
612 else {
613 const double xi = r / cutoff;
614 const double xi2 = xi * xi;
615 const double xi3 = xi * xi * xi;
616 // const double nu21=0.5+1./32.*(45.*xi - 50.*xi3 + 21.*xi*xi*xi*xi*xi);
617 const double nu22 = 0.5 + 1. / 64. * (105 * xi - 175 * xi3 + 147 * xi2 * xi3 - 45 * xi3 * xi3 * xi);
618 // const double nu40=0.5 + 1./128.*(225 *xi - 350 *xi3 + 189*xi2*xi3);
619 const double kk = 2. * nu22 - 1.0;
620 return kk / (r + 1.e-15) * xyz;
621 }
622 }
623 };
624
625 /// a class holding the electronic correlation factor for R12 theory
626 /// CorrelationFactor2 = (1-0.5*exp(-gamma*r12), gamma=0.5
627 /// (CorrelationFactor + 1)*2.0 = CorrelationFactor2 (currently CorrelationFactor2 is only implemented for gamma=0.5 so use this gamma also on CorrelationFactor
628 class CorrelationFactor2 {
629 World& world;
630 double _gamma; ///< the correlation factor exp(-gamma r12)
631 typedef std::shared_ptr<FunctionFunctorInterface<double, 6>> functorT;
632
633 public:
634 double dcut; ///< the cutoff for the 1/r potential
635 double lo; ///< smallest length scale to be resolved
636 double vtol; ///< initial projection threshold
637
638
639 /// ctor, use negative gamma for linear correlation factor r12
640 CorrelationFactor2(World& world) : world(world), _gamma(0.5), dcut(1.e-10),
641 lo(1.e-10), vtol(FunctionDefaults<3>::get_thresh() * 0.1) {
642 MADNESS_ASSERT(_gamma==0.5);
643 }
644
645 /// return the exponent of this correlation factor
646 double gamma() const { return _gamma; }
647
648 real_function_6d function() const {
649 functorT R = functorT(new R_functor(_gamma, 1));
650 return real_factory_6d(world).functor(R).is_on_demand();
651 }
652
653 real_function_6d square() const {
654 functorT R2 = functorT(new R_functor(_gamma, 2));
655 return real_factory_6d(world).functor(R2).is_on_demand();
656 }
657
658 real_function_6d inverse() const {
659 functorT R = functorT(new R_functor(_gamma, -1));
660 return real_factory_6d(world).functor(R).is_on_demand();
661 }
662
663 /// return the U1 term of the correlation function
664 real_function_6d U1(const int axis) const {
665 functorT U1f = functorT(new U1_functor(_gamma, axis));
666 return real_factory_6d(world).functor(U1f).is_on_demand();
667 }
668
669 /// return the U2 term of the correlation function
670 real_function_6d U2() const {
671 functorT U2f = functorT(new U2_functor(_gamma));
672 return real_factory_6d(world).functor(U2f).is_on_demand();
673 }
674
675 /// apply Kutzelnigg's regularized potential to an orbital product
676 real_function_6d apply_U(const real_function_6d& psi, const double eps) const {
677 const double bsh_thresh = 1.e-7;
678
679 real_function_6d result = real_factory_6d(world);
680
681 real_convolution_6d op_mod = BSHOperator<6>(world, sqrt(-2 * eps), lo, bsh_thresh);
682 op_mod.modified() = true;
683
684 for (int axis = 0; axis < 3; ++axis) {
685 //if (world.rank()==0) print("working on axis",axis);
686 real_derivative_6d D1 = free_space_derivative<double, 6>(world, axis);
687 real_derivative_6d D2 = free_space_derivative<double, 6>(world, axis + 3);
688 const real_function_6d Drhs1 = D1(psi).truncate();
689 const real_function_6d Drhs2 = D2(psi).truncate();
690
691 const real_function_6d u1 = U1(axis);
692
693 real_function_6d tmp1 = CompositeFactory<double, 6, 3>(world)
694 .g12(u1).ket(copy(Drhs1));
695 tmp1.fill_cuspy_tree(op_mod).truncate();
696
697 real_function_6d tmp2 = CompositeFactory<double, 6, 3>(world)
698 .g12(u1).ket(copy(Drhs2));
699 tmp2.fill_cuspy_tree(op_mod).truncate();
700 // if (world.rank()==0) print("done with fill_tree");
701
702 result = result + (tmp1 - tmp2).truncate();
703 tmp1.clear();
704 tmp2.clear();
705 world.gop.fence();
706 result.truncate().reduce_rank();
707
708 // if (world.rank()==0) printf("done with multiplication with U at ime %.1f\n",wall_time());
709 // result.print_size("result");
710 }
711
712 real_function_6d u2 = U2();
713 real_function_6d r2 = CompositeFactory<double, 6, 3>(world).ket(copy(psi))
714 .g12(u2);
715 r2.fill_tree(op_mod);
716 result = (result + r2).truncate();
717 return result;
718 }
719
720 private:
721 /// functor for the correlation factor R
722 class R_functor : public FunctionFunctorInterface<double, 6> {
723 double gamma;
724 int exponent;
725
726 public:
727 R_functor(double gamma, int e = 1) : gamma(gamma), exponent(e) {
728 MADNESS_ASSERT(gamma==0.5);
729 }
730
731 using FunctionFunctorInterface<double,6>::operator();
732
733 // only valid for gamma=1
734 double operator()(const coord_6d& r) const override {
735 const double rr = r12(r);
736 double val = (1.0 - 0.5 * exp(-gamma * rr));
737 if (exponent == 1) return val;
738 else if (exponent == 2) return val * val;
739 else if (exponent == -1) return 1.0 / val;
740 else {
741 MADNESS_EXCEPTION("fancy exponent in correlationfactor2", 1);
742 }
743 }
744 };
745
746 /// functor for the U2 local potential
747 class U2_functor : public FunctionFunctorInterface<double, 6> {
748 double gamma;
749
750 public:
751 U2_functor(double gamma) : gamma(gamma) {
752 MADNESS_ASSERT(gamma==0.5);
753 }
754
755 using FunctionFunctorInterface<double,6>::operator();
756
757 // only valid for gamma=1
758 double operator()(const coord_6d& r) const override {
759 const double rr = r12(r);
760 // Taylor expansion for small r
761 if (rr < 1.e-4) { // valid for gamma==0.5, otherwise singular
762 return (5. / 4.0 - rr + (35.0 * rr * rr) / 48.0 - (101.0 * rr * rr * rr) / 192.0);
763 }
764 const double egr = exp(-gamma * rr);
765 return -(-8. * egr + 8.0 + rr * egr) / (4.0 * rr * egr - 8 * rr);
766 }
767 };
768
769 /// functor for the U1 = -\frac{\vec\nabla_1 f_{12}}{f_{12}} potential
770
771 /// the potential is given by
772 /// U1 = -\frac{\vec\nabla_1 f_{12}}{f_{12}}
773 /// = \frac{e^{-r12/2}{4-2e^{-r12/2}} \vec unitvec
774 /// the derivative operators are not included
775 class U1_functor : public FunctionFunctorInterface<double, 6> {
776 double gamma;
777 int axis;
778
779 public:
780 U1_functor(double gamma, int axis) : gamma(gamma), axis(axis) {
781 MADNESS_ASSERT(gamma==0.5);
783 }
784
785 using FunctionFunctorInterface<double,6>::operator();
786
787 double operator()(const coord_6d& r) const override {
788 const double rr = r12(r);
789 const coord_3d vr12{r[0] - r[3], r[1] - r[4], r[2] - r[5]};
790 const coord_3d N = unitvec(vr12);
791 // Taylor expansion for small r
792 double val;
793 if (rr < 1.e-4) { // valid for gamma==0.5, otherwise singular
794 val = 0.5 - 0.5 * rr + 0.125 * (3. * rr * rr) - (13. * rr * rr * rr) / 48.0;
795 }
796 else {
797 const double egr = exp(-gamma * rr);
798 val = egr / (4.0 - 2.0 * egr);
799 }
800 // NOTE the sign
801 return -val * N[axis];
802 }
803 };
804
805 /// helper function
806 static double r12(const coord_6d& r) {
807 const double x12 = r[0] - r[3];
808 const double y12 = r[1] - r[4];
809 const double z12 = r[2] - r[5];
810 const double r12 = sqrt(x12 * x12 + y12 * y12 + z12 * z12);
811 return r12;
812 }
813 };
814}
815
816
817#endif /* SRC_APPS_CHEM_ELECTRONIC_CORRELATION_FACTOR_H_ */
virtual FunctionFactory & thresh(double thresh)
Definition function_factory.h:198
FunctionFactory & functor(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > &f)
Definition function_factory.h:141
virtual FunctionFactory & is_on_demand()
Definition function_factory.h:288
FunctionFactory & truncate_mode(int truncate_mode)
Definition function_factory.h:228
Function< T, NDIM > & fill_tree(const Function< R, NDIM > &g, bool fence=true)
With this being an on-demand function, fill the MRA tree according to different criteria.
Definition mra.h:1347
Function< T, NDIM > & fill_cuspy_tree(const opT &op, const bool fence=true)
Definition mra.h:1388
double thresh() const
Returns value of truncation threshold. No communication.
Definition mra.h:674
Function< T, NDIM > & truncate(double tol=0.0, bool fence=true)
Truncate the function with optional fence. Compresses with fence if not compressed.
Definition mra.h:718
Function< T, NDIM > & reduce_rank(const double thresh=0.0, const bool fence=true)
reduce the rank of the coefficient tensors
Definition mra.h:1909
constexpr T normf() const
Calculate the 2-norm of the vector elements.
Definition vector.h:421
static const double R
Definition csqrt.cc:46
double(* f)(const coord_3d &)
Definition derivatives.cc:54
double(* energy)()
Definition derivatives.cc:58
double(* f2)(const coord_3d &)
Definition derivatives.cc:56
static double lo
Definition dirac-hatom.cc:23
std::vector< Spinor > truncate(std::vector< Spinor > arg)
Definition dirac-hatom.cc:503
Fcwf copy(Fcwf psi)
Definition fcwf.cc:374
static double function(const coord_3d &r)
Normalized gaussian.
Definition functionio.cc:100
double square(double x)
Definition gfit.cc:59
double psi(const Vector< double, 3 > &r)
Definition hatom_energy.cc:78
static double u(double r, double c)
Definition he.cc:20
static const double dcut
Definition he.cc:13
double func(double x)
A simple program for testing the CubicInterpolationTable class.
Definition interp3.cc:43
Implements (2nd generation) static load/data balancing for functions.
#define MADNESS_EXCEPTION(msg, value)
Macro for throwing a MADNESS exception.
Definition madness_exception.h:119
#define MADNESS_ASSERT(condition)
Assert a condition that should be free of side-effects since in release builds this might be a no-op.
Definition madness_exception.h:134
#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
std::shared_ptr< FunctionFunctorInterface< double, 3 > > functorT
Definition mcpfit.cc:49
void print(const tensorT &t)
Definition mcpfit.cc:140
Main include file for MADNESS and defines Function interface.
Namespace for all elements and tools of MADNESS.
Definition DFConvergence.h:9
Function< TENSOR_RESULT_TYPE(Q, T), NDIM > mul(const Q alpha, const Function< T, NDIM > &f, bool fence=true)
Returns new function equal to alpha*f(x) with optional fence.
Definition mra.h:1938
@ ao
read the AO projections (restartaodata) and iterate
Definition RestartPlan.h:57
Tensor< T > inverse(const Tensor< T > &a_in)
invert general square matrix A
Definition lapack.cc:832
constexpr Vector< T, N > unitvec(const Vector< T, N > &r, const double eps=1.e-6)
Construct a unit-Vector that has the same direction as r.
Definition vector.h:768
Function< double, 6 > real_function_6d
Definition functypedefs.h:83
Function< double, 3 > real_function_3d
Definition functypedefs.h:80
std::vector< Function< T, NDIM > > grad(const Function< T, NDIM > &f, bool refine=false, bool fence=true)
shorthand gradient operator
Definition vmra.h:2370
SeparatedConvolution< double, 6 > real_convolution_6d
Definition functypedefs.h:139
Vector< double, 3 > coord_3d
Definition funcplot.h:1074
Derivative< double, 6 > real_derivative_6d
Definition functypedefs.h:188
FunctionFactory< double, 6 > real_factory_6d
Definition functypedefs.h:111
double operator()(const std::array< double, 3 > &x) const
Evaluate the Gaussian primitive at the specified point.
static const double c
Definition relops.cc:10
static const double thresh
Definition rk.cc:45
const double xi
Exponent for delta function approx.
Definition siam_example.cc:60
void e()
Definition test_sig.cc:75
#define N
Definition testconv.cc:37
static const int truncate_mode
Definition testcosine.cc:14
std::size_t axis
Definition testpdiff.cc:59
static Molecule molecule
Definition testperiodicdft.cc:39
static AtomicBasisSet aobasis
Definition testperiodicdft.cc:40
const double R2
Definition vnucso.cc:84