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 $Id$ 32 */ 33 34 /// \file function_factory.h 35 /// Holds machinery to set up Functions/FuncImpls using various Factories and Interfaces 36 37 /// We provide an abstract base class FunctionFunctorInterface, of which we derive 38 /// (as of now) the following classes: 39 /// - ElementaryInterface (formerly FunctorInterfaceWrapper) to wrap elementary functions 40 /// - ElectronRepulsionInterface to provide 1/r12, which is not elementarily accessible 41 /// - CompositeFunctionInterface to provide on-demand coefficients of pair functions 42 /// 43 /// Each of these Interfaces can be used in a FunctionFactory to set up a Function 44 45 46 #ifndef MADNESS_MRA_FUNCTION_FACTORY_H__INCLUDED 47 #define MADNESS_MRA_FUNCTION_FACTORY_H__INCLUDED 48 49 #include <madness/tensor/tensor.h> 50 #include <madness/tensor/gentensor.h> 51 #include <madness/mra/key.h> 52 #include <madness/mra/function_interface.h> 53 54 55 namespace madness { 56 57 // needed for the CompositeFactory 58 template<typename T, std::size_t NDIM> 59 class FunctionImpl; 60 61 template<typename T, std::size_t NDIM> 62 class Function; 63 64 template<typename T, std::size_t NDIM> 65 Tensor<T> fcube(const Key<NDIM>&, T (*f)(const Vector<double,NDIM>&), const Tensor<double>&); 66 67 template <typename T, std::size_t NDIM> 68 Tensor<T> fcube(const Key<NDIM>& key, const FunctionFunctorInterface<T,NDIM>& f, const Tensor<double>& qx); 69 70 /// FunctionFactory implements the named-parameter idiom for Function 71 72 /// C++ does not provide named arguments (as does, e.g., Python). 73 /// This class provides something very close. Create functions as follows 74 /// \code 75 /// double myfunc(const double x[]); 76 /// Function<double,3> f = FunctionFactory<double,3>(world).f(myfunc).k(11).thresh(1e-9).debug() 77 /// \endcode 78 /// where the methods of function factory, which specify the non-default 79 /// arguments eventually passed to the \c Function constructor, can be 80 /// used in any order. 81 /// 82 /// Need to add a general functor for initial projection with a standard interface. 83 template<typename T, std::size_t NDIM> 84 class FunctionFactory { 85 friend class FunctionImpl<T, NDIM> ; 86 typedef Vector<double, NDIM> coordT; ///< Type of vector holding coordinates 87 protected: 88 World& _world; 89 int _k; 90 double _thresh; 91 int _initial_level=FunctionDefaults<NDIM>::get_initial_level(); 92 int _special_level=FunctionDefaults<NDIM>::get_special_level(); 93 std::vector<Vector<double,NDIM> > _special_points; 94 int _max_refine_level; 95 int _truncate_mode; 96 bool _refine; 97 bool _empty; 98 bool _autorefine; 99 bool _truncate_on_project; 100 bool _fence; 101 bool _is_on_demand; 102 bool _compressed; 103 //Tensor<int> _bc; 104 std::shared_ptr<WorldDCPmapInterface<Key<NDIM> > > _pmap; 105 106 private: 107 // need to keep this private, access only via get_functor(); 108 // reason is that the functor must only be constructed when the actual 109 // FuncImpl is constructed, otherwise we might depend on the ordering 110 // of the chaining (specifically, if the functor is constructed before 111 // of after the threshold is changed) 112 std::shared_ptr<FunctionFunctorInterface<T, NDIM> > _functor; 113 114 public: 115 FunctionFactory(World & world)116 FunctionFactory(World& world) : 117 _world(world), 118 _k(FunctionDefaults<NDIM>::get_k()), 119 _thresh(FunctionDefaults<NDIM>::get_thresh()), 120 _initial_level(FunctionDefaults<NDIM>::get_initial_level()), 121 _special_level(FunctionDefaults<NDIM>::get_special_level()), 122 _special_points(std::vector<Vector<double,NDIM> >()), 123 _max_refine_level(FunctionDefaults<NDIM>::get_max_refine_level()), 124 _truncate_mode(FunctionDefaults<NDIM>::get_truncate_mode()), 125 _refine(FunctionDefaults<NDIM>::get_refine()), 126 _empty(false), 127 _autorefine(FunctionDefaults<NDIM>::get_autorefine()), 128 _truncate_on_project(FunctionDefaults<NDIM>::get_truncate_on_project()), 129 _fence(true), // _bc(FunctionDefaults<NDIM>::get_bc()), 130 _is_on_demand(false), 131 _compressed(false), 132 _pmap(FunctionDefaults<NDIM>::get_pmap()), _functor() { 133 } 134 ~FunctionFactory()135 virtual ~FunctionFactory() {}; 136 137 FunctionFactory& functor(const std::shared_ptr<FunctionFunctorInterface<T,NDIM>> & f)138 functor(const std::shared_ptr<FunctionFunctorInterface<T, NDIM> >& f) { 139 _functor = f; 140 return self(); 141 } 142 143 /// pass in a functor that is derived from FunctionFunctorInterface 144 145 /// similar to the first version of functor, but easy-to-use 146 /// FunctionFunctorInterface must be a public base of opT 147 template<typename opT> 148 typename std::enable_if<std::is_base_of<FunctionFunctorInterface<T, NDIM>,opT>::value, functor(const opT & op)149 FunctionFactory& >::type functor(const opT& op) { 150 _functor=std::shared_ptr<FunctionFunctorInterface<T,NDIM> >(new opT(op)); 151 return self(); 152 } 153 154 /// pass in a functor that is *not* derived from FunctionFunctorInterface 155 156 /// similar to the first version of functor, but easy-to-use 157 template<typename opT> 158 typename std::enable_if<not std::is_base_of<FunctionFunctorInterface<T, NDIM>,opT>::value, functor(const opT & op)159 FunctionFactory& >::type functor(const opT& op) { 160 _functor=std::shared_ptr<FunctionInterface<T,NDIM,opT> > 161 (new FunctionInterface<T,NDIM,opT>(op)); 162 return self(); 163 } 164 165 FunctionFactory& compressed(bool value=true) { 166 _compressed = value; 167 return self(); 168 } 169 170 FunctionFactory& no_functor()171 no_functor() { 172 _functor.reset(); 173 return self(); 174 } 175 FunctionFactory& f(T (* f)(const coordT &))176 f(T (*f)(const coordT&)) { 177 functor(std::shared_ptr<FunctionFunctorInterface<T, NDIM> > ( 178 new ElementaryInterface<T,NDIM>(f))); 179 return self(); 180 } 181 k(int k)182 virtual FunctionFactory& k(int k) { 183 _k = k; 184 return self(); 185 } 186 thresh(double thresh)187 virtual FunctionFactory& thresh(double thresh) { 188 _thresh = thresh; 189 return self(); 190 } 191 192 FunctionFactory& initial_level(int initial_level)193 initial_level(int initial_level) { 194 _initial_level = initial_level; 195 return self(); 196 } 197 198 FunctionFactory& special_level(int special_level)199 special_level(int special_level) { 200 _special_level=special_level; 201 return self(); 202 } 203 204 FunctionFactory& special_points(std::vector<Vector<double,NDIM>> & special_points)205 special_points(std::vector<Vector<double, NDIM> > & special_points) { 206 _special_points=special_points; 207 return self(); 208 } 209 210 FunctionFactory& max_refine_level(int max_refine_level)211 max_refine_level(int max_refine_level) { 212 _max_refine_level = max_refine_level; 213 return self(); 214 } 215 FunctionFactory& truncate_mode(int truncate_mode)216 truncate_mode(int truncate_mode) { 217 _truncate_mode = truncate_mode; 218 return self(); 219 } 220 FunctionFactory& 221 refine(bool refine = true) { 222 _refine = refine; 223 return self(); 224 } 225 FunctionFactory& 226 norefine(bool norefine = true) { 227 _refine = !norefine; 228 return self(); 229 } 230 FunctionFactory& empty()231 empty() { 232 _empty = true; 233 return self(); 234 } 235 FunctionFactory& autorefine()236 autorefine() { 237 _autorefine = true; 238 return self(); 239 } 240 FunctionFactory& noautorefine()241 noautorefine() { 242 _autorefine = false; 243 return self(); 244 } 245 FunctionFactory& truncate_on_project()246 truncate_on_project() { 247 _truncate_on_project = true; 248 return self(); 249 } 250 FunctionFactory& notruncate_on_project()251 notruncate_on_project() { 252 _truncate_on_project = false; 253 return self(); 254 } 255 FunctionFactory& 256 fence(bool fence = true) { 257 _fence = fence; 258 return self(); 259 } 260 FunctionFactory& nofence()261 nofence() { 262 _fence = false; 263 return self(); 264 } 265 virtual FunctionFactory& is_on_demand()266 is_on_demand() { 267 _is_on_demand = true; 268 return self(); 269 } 270 FunctionFactory& pmap(const std::shared_ptr<WorldDCPmapInterface<Key<NDIM>>> & pmap)271 pmap(const std::shared_ptr<WorldDCPmapInterface<Key<NDIM> > >& pmap) { 272 _pmap = pmap; 273 return self(); 274 } 275 get_k()276 int get_k() const {return _k;}; get_thresh()277 double get_thresh() const {return _thresh;}; get_world()278 World& get_world() const {return _world;}; 279 280 /// return the functor; override this if the functor needs deferred construction get_functor()281 virtual std::shared_ptr<FunctionFunctorInterface<T, NDIM> > get_functor() const { 282 return _functor; 283 } 284 285 /// implement this in all derived classes for correct chaining self()286 FunctionFactory& self() {return *this;} 287 288 }; 289 290 291 /// Factory for facile setup of a CompositeFunctorInterface and its FuncImpl 292 293 /// for the concept of a Factory see base class FunctionFactory 294 /// here we need to provide two different dimensions, since the main purpose 295 /// of this is to set up a pair function (6D), consisting of orbitals (3D), 296 /// and also one- and two-electron potentials 297 /// 298 /// This Factory constructs a FuncImpl, and also the functor to it. 299 /// 300 /// NOTE: pass in only copies of functions, since use in here will corrupt the 301 /// tree structure and functions will not pass the VERIFY test after this. 302 template<typename T, std::size_t NDIM, std::size_t MDIM> 303 class CompositeFactory : public FunctionFactory<T, NDIM> { 304 public: 305 std::shared_ptr<FunctionImpl<T,NDIM> > _ket; ///< supposedly a 6D pair function ket 306 std::shared_ptr<FunctionImpl<T,NDIM> > _g12; ///< supposedly a interaction potential 307 std::shared_ptr<FunctionImpl<T,MDIM> > _v1; ///< supposedly a potential for particle 1 308 std::shared_ptr<FunctionImpl<T,MDIM> > _v2; ///< supposedly a potential for particle 2 309 std::shared_ptr<FunctionImpl<T,MDIM> > _particle1; ///< supposedly particle 1 310 std::shared_ptr<FunctionImpl<T,MDIM> > _particle2; ///< supposedly particle 2 311 312 private: 313 std::shared_ptr<CompositeFunctorInterface<T, NDIM, MDIM> > _func; 314 315 friend class CompositeFunctorInterface<T, NDIM, MDIM>; 316 317 public: 318 CompositeFactory(World & world)319 CompositeFactory(World& world) 320 : FunctionFactory<T,NDIM>(world) 321 , _ket() 322 , _g12() 323 , _v1() 324 , _v2() 325 , _particle1() 326 , _particle2() 327 , _func() { 328 329 // there are certain defaults that make only sense here 330 this->_is_on_demand=true; 331 } 332 333 /// provide directly the NDIM (6D) pair function ket 334 CompositeFactory& 335 // ket(const std::shared_ptr<FunctionImpl<T, NDIM> >& f) { ket(const Function<T,NDIM> & f)336 ket(const Function<T, NDIM>& f) { 337 _ket = f.get_impl(); 338 return self(); 339 } 340 341 /// g12 is the interaction potential (6D) 342 CompositeFactory& g12(const Function<T,NDIM> & f)343 g12(const Function<T, NDIM>& f) { 344 _g12 = f.get_impl(); 345 return self(); 346 } 347 348 /// a one-particle potential, acting on particle 1 349 CompositeFactory& V_for_particle1(const Function<T,MDIM> & f)350 V_for_particle1(const Function<T, MDIM>& f) { 351 _v1 = f.get_impl(); 352 return self(); 353 } 354 355 /// a one-particle potential, acting on particle 2 356 CompositeFactory& V_for_particle2(const Function<T,MDIM> & f)357 V_for_particle2(const Function<T, MDIM>& f) { 358 _v2 = f.get_impl(); 359 return self(); 360 } 361 362 /// provide particle 1, used with particle 2 to set up a pair function by 363 /// direct product 364 CompositeFactory& particle1(const Function<T,MDIM> & f)365 particle1(const Function<T, MDIM>& f) { 366 _particle1 = f.get_impl(); 367 return self(); 368 } 369 370 /// provide particle 2, used with particle 1 to set up a pair function by 371 /// direct product 372 CompositeFactory& particle2(const Function<T,MDIM> & f)373 particle2(const Function<T, MDIM>& f) { 374 _particle2 = f.get_impl(); 375 return self(); 376 } 377 378 // access to the functor *only* via this get_functor()379 std::shared_ptr<FunctionFunctorInterface<T, NDIM> > get_functor() const { 380 381 // return if we already have a valid functor 382 if (this->_func) return this->_func; 383 384 // construction of the functor is const in spirit, but non-const in sad reality.. 385 // this Factory not only constructs the Function, but also the functor, so 386 // pass *this to the interface 387 const_cast< std::shared_ptr<CompositeFunctorInterface<T, NDIM, MDIM> >& >(this->_func)= 388 std::shared_ptr<CompositeFunctorInterface<T, NDIM, MDIM> >( 389 new CompositeFunctorInterface<double, NDIM, MDIM>( 390 this->_world,_ket,_g12,_v1,_v2,_particle1,_particle2 391 )); 392 393 return this->_func; 394 } 395 self()396 CompositeFactory& self() {return *this;} 397 }; 398 399 /// factory for generating TwoElectronInterfaces 400 class TwoElectronFactory : public FunctionFactory<double,6> { 401 402 protected: 403 typedef std::shared_ptr<FunctionFunctorInterface<double, 6> > InterfacePtr; 404 405 public: TwoElectronFactory(World & world)406 TwoElectronFactory(World& world) 407 : FunctionFactory<double,6>(world) 408 , type_(coulomb_) 409 , interface_() 410 , dcut_(FunctionDefaults<3>::get_thresh()) 411 , gamma_(-1.0) 412 , bc_(FunctionDefaults<6>::get_bc()) { 413 _is_on_demand=true; 414 this->_thresh=(FunctionDefaults<3>::get_thresh()); 415 this->_k=(FunctionDefaults<3>::get_k()); 416 417 } 418 419 /// the smallest length scale to be represented (aka lo) dcut(double dcut)420 TwoElectronFactory& dcut(double dcut) { 421 dcut_=dcut; 422 return self(); 423 } 424 425 /// the requested precision thresh(double thresh)426 TwoElectronFactory& thresh(double thresh) { 427 _thresh=thresh; 428 return self(); 429 } 430 431 /// the exponent of a slater function gamma(double g)432 TwoElectronFactory& gamma(double g) { 433 gamma_=g; 434 return self(); 435 } 436 437 /// return the operator (1 - exp(-gamma x) / (2 gamma) f12()438 TwoElectronFactory& f12() { 439 type_=f12_; 440 return self(); 441 } 442 443 /// return the operator (1 - exp(-gamma x) / (2 gamma) slater()444 TwoElectronFactory& slater() { 445 type_=slater_; 446 return self(); 447 } 448 449 /// return the BSH operator BSH()450 TwoElectronFactory& BSH() { 451 type_=bsh_; 452 return self(); 453 } 454 455 // access to the functor *only* via this get_functor()456 InterfacePtr get_functor() const { 457 458 // return if we already have a valid interface 459 if (this->interface_) return this->interface_; 460 461 // construction of the functor is const in spirit, but non-const in sad reality.. 462 if (type_==coulomb_) { 463 const_cast<InterfacePtr& >(this->interface_)= 464 InterfacePtr(new ElectronRepulsionInterface( 465 dcut_,_thresh,bc_,_k)); 466 } else if (type_==f12_) { 467 // make sure gamma is set 468 MADNESS_ASSERT(gamma_>0); 469 const_cast<InterfacePtr& >(this->interface_)= 470 InterfacePtr(new SlaterF12Interface( 471 gamma_,dcut_,_thresh,bc_,_k)); 472 } else if (type_==slater_) { 473 // make sure gamma is set 474 MADNESS_ASSERT(gamma_>0); 475 const_cast<InterfacePtr& >(this->interface_)= 476 InterfacePtr(new SlaterFunctionInterface( 477 gamma_,dcut_,_thresh,bc_,_k)); 478 } else if (type_==bsh_){ 479 const_cast<InterfacePtr& >(this->interface_)= 480 InterfacePtr(new BSHFunctionInterface( 481 gamma_,dcut_,_thresh,bc_,_k)); 482 483 } else { 484 MADNESS_EXCEPTION("unimplemented integral kernel",1); 485 } 486 return this->interface_; 487 } 488 self()489 TwoElectronFactory& self() {return *this;} 490 491 protected: 492 493 enum operatortype {coulomb_, slater_, f12_, bsh_}; 494 495 operatortype type_; 496 497 /// the interface providing the actual coefficients 498 InterfacePtr interface_; 499 500 double dcut_; ///< cutoff radius for 1/r12, aka regularization 501 502 double gamma_; 503 504 BoundaryConditions<6> bc_; 505 506 }; 507 508 #if 0 509 class ERIFactory : public TwoElectronFactory<ERIFactory> { 510 public: 511 ERIFactory(World& world) : TwoElectronFactory<ERIFactory>(world) {} 512 513 // access to the functor *only* via this 514 InterfacePtr get_functor() const { 515 516 // return if we already have a valid interface 517 if (this->interface_) return this->interface_; 518 519 // construction of the functor is const in spirit, but non-const in sad reality.. 520 const_cast<InterfacePtr& >(this->interface_)= 521 InterfacePtr(new ElectronRepulsionInterface( 522 dcut_,thresh_,bc_,k_)); 523 return this->interface_; 524 } 525 526 ERIFactory& self() {return *this;} 527 528 }; 529 530 /// a function like f(x) = 1 - exp(-mu x) 531 class SlaterFunctionFactory : public TwoElectronFactory<SlaterFunctionFacto> { 532 public: 533 SlaterFunctionFactory(World& world) 534 : TwoElectronFactory(world), gamma_(-1.0), f12_(false) {} 535 536 /// set the exponent of the Slater function 537 SlaterFunctionFactory& gamma(double gamma) { 538 this->gamma_ = gamma; 539 return self(); 540 } 541 542 /// do special f12 function 543 SlaterFunctionFactory& f12() { 544 this->f12_=true; 545 return self(); 546 } 547 548 // access to the functor *only* via this 549 InterfacePtr get_functor() const { 550 551 // return if we already have a valid interface 552 if (this->interface_) return this->interface_; 553 554 // make sure gamma is set 555 MADNESS_ASSERT(gamma_>0); 556 557 // construction of the functor is const in spirit, but non-const in sad reality.. 558 if (f12_) { 559 const_cast<InterfacePtr& >(this->interface_)= 560 InterfacePtr(new SlaterF12Interface( 561 gamma_,dcut_,this->_thresh,bc_,this->_k)); 562 } else { 563 const_cast<InterfacePtr& >(this->interface_)= 564 InterfacePtr(new SlaterFunctionInterface( 565 gamma_,dcut_,this->_thresh,bc_,this->_k)); 566 } 567 return this->interface_; 568 } 569 570 SlaterFunctionFactory& self() {return *this;} 571 572 private: 573 574 double gamma_; ///< the exponent of the Slater function f(x)=exp(-gamma x) 575 bool f12_; ///< use 1-exp(-gamma x) instead of exp(-gamma x) 576 }; 577 578 /// Factory to set up an ElectronRepulsion Function 579 template<typename T, std::size_t NDIM> 580 class ERIFactory : public FunctionFactory<T, NDIM> { 581 582 private: 583 std::shared_ptr<ElectronRepulsionInterface> _eri; 584 585 public: 586 587 /// cutoff radius for 1/r12, aka regularization 588 double _dcut; 589 BoundaryConditions<NDIM> _bc; 590 591 public: 592 ERIFactory(World& world) 593 : FunctionFactory<T,NDIM>(world) 594 , _eri() 595 , _dcut(FunctionDefaults<NDIM>::get_thresh()) 596 , _bc(FunctionDefaults<NDIM>::get_bc()) 597 { 598 this->_is_on_demand=true; 599 MADNESS_ASSERT(NDIM==6); 600 } 601 602 ERIFactory& 603 thresh(double thresh) { 604 this->_thresh = thresh; 605 return *this; 606 } 607 608 ERIFactory& 609 dcut(double dcut) { 610 this->_dcut = dcut; 611 return *this; 612 } 613 614 // access to the functor *only* via this 615 std::shared_ptr<FunctionFunctorInterface<T, NDIM> > get_functor() const { 616 617 // return if we already have a valid eri 618 if (this->_eri) return this->_eri; 619 620 // if (this->_world.rank()==0) print("set dcut in ERIFactory to ", _dcut); 621 622 // construction of the functor is const in spirit, but non-const in sad reality.. 623 const_cast< std::shared_ptr<ElectronRepulsionInterface>& >(this->_eri)= 624 std::shared_ptr<ElectronRepulsionInterface>( 625 new ElectronRepulsionInterface(_dcut,this->_thresh, 626 _bc,this->_k)); 627 628 return this->_eri; 629 } 630 631 }; 632 #endif 633 634 635 /// Does not work 636 // /// Factory to set up an ElectronRepulsion Function 637 // template<typename T, std::size_t NDIM> 638 // class FGFactory : public FunctionFactory<T, NDIM> { 639 // 640 // private: 641 // std::shared_ptr<FGInterface> _fg; 642 // 643 // public: 644 // 645 // /// cutoff radius for 1/r12, aka regularization 646 // double _dcut; 647 // double _gamma; 648 // BoundaryConditions<NDIM> _bc; 649 // 650 // public: 651 // FGFactory(World& world, double gamma) 652 // : FunctionFactory<T,NDIM>(world) 653 // , _fg() 654 // , _dcut(FunctionDefaults<NDIM>::get_thresh()) 655 // , _gamma(gamma) 656 // , _bc(FunctionDefaults<NDIM>::get_bc()) 657 // { 658 // this->_is_on_demand=true; 659 // MADNESS_ASSERT(NDIM==6); 660 // } 661 // 662 // FGFactory& 663 // thresh(double thresh) { 664 // this->_thresh = thresh; 665 // return *this; 666 // } 667 // 668 // FGFactory& 669 // dcut(double dcut) { 670 // this->_dcut = dcut; 671 // return *this; 672 // } 673 // 674 // // access to the functor *only* via this 675 // std::shared_ptr<FunctionFunctorInterface<T, NDIM> > get_functor() const { 676 // 677 // // return if we already have a valid eri 678 // if (this->_fg) return this->_fg; 679 // 680 // // if (this->_world.rank()==0) print("set dcut in ERIFactory to ", _dcut); 681 // 682 // // construction of the functor is const in spirit, but non-const in sad reality.. 683 // const_cast< std::shared_ptr<FGInterface>& >(this->_fg)= 684 // std::shared_ptr<FGInterface>( 685 // new FGInterface(this->_world,_dcut,this->_thresh, 686 // _gamma,_bc,this->_k)); 687 // 688 // return this->_fg; 689 // } 690 // 691 // }; 692 693 } 694 695 #endif // MADNESS_MRA_FUNCTION_FACTORY_H__INCLUDED 696