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 /* 33 * Leafop.h 34 * 35 * Created on: Apr 12, 2016 36 * Author: kottmanj 37 * 38 * Definition of Operators which operate on leaf boxes 39 * includes special operators for cuspy funtions (f12 applications) which enforce deeper refinement on the "diagonal" boxes (coordinate r1 == coordinate r2) 40 * 41 */ 42 43 #ifndef SRC_MADNESS_MRA_LEAFOP_H_ 44 #define SRC_MADNESS_MRA_LEAFOP_H_ 45 46 namespace madness { 47 48 // forward declarations 49 template<std::size_t NDIM> 50 class Key; 51 template<typename T> 52 class GenTensor; 53 template<typename T, std::size_t NDIM> 54 class SeparatedConvolution; 55 56 /// helper structure for the Leaf_op 57 /// The class has an operator which decides if a given key belongs to a special box (needs further refinement up to a special level) 58 /// this is the default class which always gives back false 59 template<typename T, std::size_t NDIM> 60 struct Specialbox_op{ 61 public: Specialbox_opSpecialbox_op62 Specialbox_op() {} ~Specialbox_opSpecialbox_op63 virtual ~Specialbox_op() {} nameSpecialbox_op64 virtual std::string name()const{return "default special box which only checks for the special points";} 65 /// the operator which deceides if the given box is special 66 /// @param[in] the key of the current box 67 /// @param[in] the function which will be constructed (if it is needed) 68 /// @param[out] bool which states if the box is special or not operatorSpecialbox_op69 virtual bool operator()(const Key<NDIM> &key,const FunctionImpl<T,NDIM>*const f=NULL )const{return false;} 70 71 // check if on of the special points is in the current box (or at low refinement level in a neighbouring box) 72 /// @param[in] the key of the box 73 /// @param[in] the function which will be constructed (if it is needed) 74 bool check_special_pointsSpecialbox_op75 check_special_points(const Key<NDIM> &key, const FunctionImpl<T,NDIM>*const f) const{ 76 const std::vector<Vector<double, NDIM> > &special_points = f->get_special_points(); 77 if(special_points.empty()) return false; 78 // level 0 and 1 has only boundary boxes 79 if(key.level()>1 and box_is_at_boundary(key)) return false; 80 // check for all special points if they are neighbours of the current box 81 BoundaryConditions<NDIM> bc=FunctionDefaults<NDIM>::get_bc(); 82 std::vector<bool> bperiodic=bc.is_periodic(); 83 for(size_t i=0; i < special_points.size(); ++i){ 84 Vector<double, NDIM> simpt; 85 user_to_sim(special_points[i],simpt); 86 Key<NDIM> specialkey=simpt2key(simpt,key.level()); 87 // use adaptive scheme: if we are at a low level refine also neighbours 88 size_t ll=get_half_of_special_level(f->get_special_level()); 89 if(ll<f->get_initial_level()) ll = f->get_initial_level(); 90 if(key.level()>ll){ 91 if(specialkey==key) return true; 92 else return false; 93 }else{ 94 if(specialkey.is_neighbor_of(key,bperiodic)) return true; 95 else return false; 96 } 97 } 98 return false; 99 } 100 101 102 template<typename Archive> 103 void serializeSpecialbox_op104 serialize(Archive& ar) { } 105 106 /// Determine if a given box is at the boundary of the simulation box 107 /// @param[in] the key of the current box 108 /// Since boxes of level 0 and 1 are always at the boundary this case should be avoided 109 /// if the boundary conditions are periodic then all boxes are boundary boxes box_is_at_boundarySpecialbox_op110 virtual bool box_is_at_boundary(const Key<NDIM> &key)const{ 111 // l runs from l=0 to l=2^n-1 for every dimension 112 // if one l is = 2^n or 0 we are at the boundary 113 // note that boxes of level 0 and 1 are always boundary boxes 114 for(size_t i=0; i < NDIM; i++){ 115 if(key.translation()[i] == 0 or key.translation()[i] == pow(2,key.level())-1){ 116 if(FunctionDefaults<NDIM>::get_bc()(i,0)!=BC_PERIODIC)return true; 117 } 118 } 119 return false; 120 } 121 122 // if you want to use milder refinement conditions for special boxes with increasing refinement level 123 // Cuspybox_op needs this 124 size_t get_half_of_special_level(const size_t& sl= FunctionDefaults<NDIM>::get_special_level())const{ 125 size_t ll = sl; 126 if(sl%2==0) ll=sl/2; 127 else ll = (sl+1)/2; 128 return ll; 129 } 130 131 }; 132 133 /// the high_dimensional key is broken appart into two lower dimensional keys, if they are neighbours of each other then refinement 134 /// up to the special_level is enforced 135 /// For general class description see the base class Specialbox_op 136 template<typename T, std::size_t NDIM> 137 struct ElectronCuspyBox_op : public Specialbox_op<T, NDIM> { 138 public: ElectronCuspyBox_opElectronCuspyBox_op139 ElectronCuspyBox_op() {} ~ElectronCuspyBox_opElectronCuspyBox_op140 ~ElectronCuspyBox_op() {} 141 142 template<typename Archive> serializeElectronCuspyBox_op143 void serialize(Archive& ar) {} 144 nameElectronCuspyBox_op145 std::string name()const{return "Cuspybox_op";} 146 147 /// Operator which decides if the key belongs to a special box 148 /// The key is broken appart in two lower dimensional keys (for electron cusps this is 6D -> 2x3D) 149 /// if the keys are neighbours then refinement up to the special level is enforced (means the 6D box is close to the cusp or contains it) 150 /// if the refinement level is already beyond half of the special_level then refinement is only enforded if the broken keys are the same (6D box contains cusp) operatorElectronCuspyBox_op151 bool operator()(const Key<NDIM> &key, const FunctionImpl<T,NDIM>*const f) const{ 152 // do not treat boundary boxes beyond level 2 as special (level 1 and 0 consist only of boundary boxes) 153 if(not (key.level() < 2)){ 154 if(this->box_is_at_boundary(key)) return false; 155 } 156 157 if(NDIM % 2 != 0) MADNESS_EXCEPTION("Cuspybox_op only valid for even dimensions",1); // if uneven dims are needed just make a new class with NDIM+1/2 and NDIM-LDIM 158 BoundaryConditions<NDIM> bc=FunctionDefaults<NDIM>::get_bc(); 159 std::vector<bool> bperiodic=bc.is_periodic(); 160 Key<NDIM / 2> key1; 161 Key<NDIM / 2> key2; 162 key.break_apart(key1,key2); 163 size_t ll=this->get_half_of_special_level(); 164 if(ll<f->get_initial_level()) ll = f->get_initial_level(); 165 if(key.level()>ll){ 166 if(key1 == key2) return true; 167 else return false; 168 }else{ 169 if(key1.is_neighbor_of(key2,bperiodic)) return true; 170 else return false; 171 } 172 MADNESS_EXCEPTION("We should not end up here (check further of cuspy box)",1); 173 return false; 174 } 175 176 }; 177 178 /// This works similar to the Cuspybox_op: The key is broken apart (2N-dimensional -> 2x N-Dimensional) 179 /// then it is checked if one of the lower dimensional keys contains a lower dimensional special points (which will be the nuclear-coordinates) 180 template<typename T, std::size_t NDIM> 181 struct NuclearCuspyBox_op : public Specialbox_op<T, NDIM> { 182 public: NuclearCuspyBox_opNuclearCuspyBox_op183 NuclearCuspyBox_op(): particle(-1) { 184 // failsafe 185 if(NDIM%2!=0) MADNESS_EXCEPTION("NuclearCuspyBox works only for even dimensions",1); 186 } NuclearCuspyBox_opNuclearCuspyBox_op187 NuclearCuspyBox_op(const size_t p): particle(p){ 188 // failsafe 189 if(NDIM%2!=0) MADNESS_EXCEPTION("NuclearCuspyBox works only for even dimensions",1); 190 MADNESS_ASSERT(particle==1 or particle==2 or particle==0); 191 } ~NuclearCuspyBox_opNuclearCuspyBox_op192 ~NuclearCuspyBox_op() {} 193 194 // particle==0: check both particles 195 // particle==1 or particle==2: check only particle1 or 2 196 int particle; 197 198 template<typename Archive> serializeNuclearCuspyBox_op199 void serialize(Archive& ar) { ar & particle;} 200 nameNuclearCuspyBox_op201 std::string name()const{return "Cuspybox_op for nuclear cusps";} 202 203 /// Operator which decides if the key belongs to a special box 204 /// The key is broken appart in two lower dimensional keys (6D -> 2x3D) 205 /// The special points should be given in a format which can also be broken appart 206 /// so: 6D special_points = (3D-special-points, 3D-special-points) 207 /// if the refinement level is already beyond half of the special_level then refinement is only enforded if the broken keys are the same (6D box contains cusp) operatorNuclearCuspyBox_op208 bool operator()(const Key<NDIM> &key, const FunctionImpl<T,NDIM>*const f) const{ 209 if(not (key.level() < 2)){ 210 if(this->box_is_at_boundary(key)) return false; 211 } 212 MADNESS_ASSERT(particle==1 or particle==2 or particle==0); 213 if(f==NULL) MADNESS_EXCEPTION("NuclearCuspyBox: Pointer to function is NULL",1); 214 const std::vector<Vector<double, NDIM> > &special_points = f->get_special_points(); 215 if(special_points.empty()) MADNESS_EXCEPTION("Demanded NuclearCuspyBox but the special points of the function are empty",1); 216 217 // break the special points into 3D points 218 std::vector<Vector<double,NDIM/2> > lowdim_sp; 219 for(size_t i=0;i<special_points.size();i++){ 220 Vector<double,NDIM/2> lowdim_tmp; 221 for(size_t j=0;j<NDIM/2;j++){ 222 lowdim_tmp[j]=special_points[i][j]; 223 // check if the formatting is correct 224 if(special_points[i][j]!=special_points[i][NDIM/2+j]) 225 MADNESS_EXCEPTION("NuclearCuspyBox: Wrong format of special_point: ",1); 226 } 227 lowdim_sp.push_back(lowdim_tmp); 228 } 229 230 // now break the key appart and check if one if the results is in the neighbourhood of a special point 231 BoundaryConditions<NDIM/2> bc=FunctionDefaults<NDIM/2>::get_bc(); 232 std::vector<bool> bperiodic=bc.is_periodic(); 233 Key<NDIM / 2> key1; 234 Key<NDIM / 2> key2; 235 236 key.break_apart(key1,key2); 237 for(size_t i=0; i < lowdim_sp.size(); ++i){ 238 Vector<double, NDIM/2> simpt; 239 user_to_sim(lowdim_sp[i],simpt); 240 Key<NDIM/2> specialkey=simpt2key(simpt,key1.level()); 241 // use adaptive scheme: if we are at a low level refine also neighbours 242 size_t ll=this->get_half_of_special_level(f->get_special_level()); 243 if(ll<f->get_initial_level()) ll = f->get_initial_level(); 244 if(key.level()>ll){ 245 if(particle==1 and specialkey==key1) return true; 246 else if(particle==2 and specialkey==key2) return true; 247 else if(particle==0 and (specialkey==key1 or specialkey==key2)) return true; 248 else return false; 249 }else{ 250 if(particle==1 and specialkey.is_neighbor_of(key1,bperiodic)) return true; 251 else if(particle==2 and specialkey.is_neighbor_of(key2,bperiodic)) return true; 252 else if(particle==0 and (specialkey.is_neighbor_of(key1,bperiodic) or specialkey.is_neighbor_of(key2,bperiodic))) return true; 253 else return false; 254 } 255 } 256 return false; 257 } 258 259 260 261 262 263 }; 264 265 template<typename T, std::size_t NDIM,typename opT, typename specialboxT> 266 class Leaf_op{ 267 public: 268 /// the function which the operators use (in most cases this function is also the function that will be constructed) 269 const FunctionImpl<T, NDIM>* f; 270 /// the operator which is used for screening (null pointer means no screening) 271 const opT * op; 272 /// special box structure: has an operator which decides if a given key belongs to a special box 273 /// the base class Specialbox_op<T,NDIM> just gives back false for every key 274 /// the derived class Cuspybox_op<T,NDIM> is used for potentials containing cusps at coalescence points of electrons 275 specialboxT specialbox; 276 /// pre or post screening (see if this is the general_leaf_op or the leaf_op_other) 277 virtual bool do_pre_screening()278 do_pre_screening() const { 279 return false; 280 } Leaf_op()281 Leaf_op() 282 : f(NULL),op(NULL), specialbox(specialboxT()) { 283 } Leaf_op(const FunctionImpl<T,NDIM> * const tmp)284 Leaf_op(const FunctionImpl<T, NDIM> *const tmp) 285 : f(tmp),op(NULL), specialbox(specialboxT()) { 286 } Leaf_op(const FunctionImpl<T,NDIM> * const tmp,specialboxT & sb)287 Leaf_op(const FunctionImpl<T, NDIM> *const tmp,specialboxT &sb) 288 : f(tmp),op(NULL), specialbox(sb) { 289 } Leaf_op(const FunctionImpl<T,NDIM> * const tmp,const opT * const ope,specialboxT & sb)290 Leaf_op(const FunctionImpl<T, NDIM> *const tmp,const opT*const ope,specialboxT &sb) 291 : f(tmp),op(ope), specialbox(sb) { 292 } Leaf_op(const Leaf_op & other)293 Leaf_op(const Leaf_op &other) : f(other.f), op(other.op), specialbox(other.specialbox) {} 294 virtual ~Leaf_op()295 ~Leaf_op() { 296 } 297 298 virtual std::string name()299 name() const { 300 return "general_leaf_op"; 301 } 302 303 /// make sanity check 304 virtual void sanity()305 sanity() const { 306 if(f == NULL) MADNESS_EXCEPTION(("Error in " + name() + " pointer to function is NULL").c_str(),1); 307 } 308 309 public: 310 /// pre-determination 311 /// the decision if the current box will be a leaf box is made from information of another function 312 /// this is needed for on demand function 313 /// not that the on-demand function that is beeing constructed is not the function in this class 314 virtual bool pre_screening(const Key<NDIM> & key)315 pre_screening(const Key<NDIM> &key) const { 316 MADNESS_EXCEPTION("pre-screening was called for leaf_op != leaf_op_other",1); 317 return false; 318 } 319 320 /// post-determination 321 /// determination if the box will be a leaf box after the coefficients are made 322 /// the function that is beeing constructed is the function in this class 323 /// the function will use the opartor op in order to screen, if op is a NULL pointer the result is always: false 324 /// @param[in] key: the key to the current box 325 /// @param[in] coeff: Coefficients of the current box 326 virtual bool post_screening(const Key<NDIM> & key,const GenTensor<T> & coeff)327 post_screening(const Key<NDIM> &key,const GenTensor<T>& coeff) const{ 328 if(op==NULL) return false; 329 if(key.level() < this->f->get_initial_level()) return false; 330 sanity(); 331 const double cnorm=coeff.normf(); 332 BoundaryConditions<NDIM> bc=FunctionDefaults<NDIM>::get_bc(); 333 std::vector<bool> bperiodic=bc.is_periodic(); 334 335 typedef Key<opT::opdim> opkeyT; 336 const opkeyT source=op->get_source_key(key); 337 338 const double thresh=(this->f->truncate_tol(this->f->get_thresh(),key)); 339 const std::vector<opkeyT>& disp=op->get_disp(key.level()); 340 const opkeyT& d=*disp.begin(); // use the zero-displacement for screening 341 const double opnorm=op->norm(key.level(),d,source); 342 const double norm=opnorm * cnorm; 343 344 return norm < thresh; 345 } 346 347 /// determines if a node is well represented compared to the parents 348 /// @param[in] key the FunctionNode which we want to determine if it's a leaf node 349 /// @param[in] coeff the coeffs of key 350 /// @param[in] parent the coeffs of key's parent node 351 /// @return is the FunctionNode of key a leaf node? 352 bool compare_to_parent(const Key<NDIM> & key,const GenTensor<T> & coeff,const GenTensor<T> & parent)353 compare_to_parent(const Key<NDIM>& key,const GenTensor<T>& coeff,const GenTensor<T>& parent) const{ 354 if(key.level() < this->f->get_initial_level()) return false; 355 //this->sanity(); 356 if(parent.has_no_data()) return false; 357 if(key.level() < this->f->get_initial_level()) return false; 358 GenTensor<T> upsampled=this->f->upsample(key,parent); 359 upsampled.scale(-1.0); 360 upsampled+=coeff; 361 const double dnorm=upsampled.normf(); 362 const bool is_leaf=(dnorm < this->f->truncate_tol(this->f->get_thresh(),key.level())); 363 return is_leaf; 364 } 365 366 /// check if the box is a special box 367 /// first check: Is one of the special points defined in the function f in the current box or a neighbour box 368 /// second check: Is the box special according to the specialbox operator (see class description of Specialbox_op) 369 /// @param[in] the key of the box 370 bool special_refinement_needed(const Key<NDIM> & key)371 special_refinement_needed(const Key<NDIM>& key) const { 372 if(key.level() > f->get_special_level()) return false; 373 else if(specialbox.check_special_points(key,f)) return true; 374 else if(specialbox(key,f)) return true; 375 else return false; 376 } 377 378 template<typename Archive> 379 void serialize(Archive & ar)380 serialize(Archive& ar) { 381 ar & this->f & this->specialbox; 382 } 383 384 }; 385 386 /// leaf_op for construction of an on-demand function 387 /// the function in the class is the function which defines the structure of the on-demand function 388 /// means: The tree of the function which is to construct will mirror the tree-structure of the function in this class (function variable defined in base-class) 389 template<typename T, std::size_t NDIM> 390 class Leaf_op_other : public Leaf_op<T, NDIM, SeparatedConvolution<double,NDIM> ,Specialbox_op<T,NDIM> > { 391 std::string name()392 name() const { 393 return "leaf_op_other"; 394 } 395 /// we only need the pre-determination based on the already constructed function f 396 /// if f at box(key) is a leaf then return true 397 public: 398 bool do_pre_screening()399 do_pre_screening() const { 400 return true; 401 } Leaf_op_other()402 Leaf_op_other(): Leaf_op<T, NDIM, SeparatedConvolution<double,NDIM> ,Specialbox_op<T,NDIM> >() { 403 } Leaf_op_other(const FunctionImpl<T,NDIM> * const f_)404 Leaf_op_other(const FunctionImpl<T, NDIM> *const f_) { 405 this->f=f_; 406 } Leaf_op_other(const Leaf_op_other & other)407 Leaf_op_other(const Leaf_op_other &other) : Leaf_op<T, NDIM, SeparatedConvolution<double,NDIM> ,Specialbox_op<T,NDIM> >(other.f) {} 408 409 bool pre_screening(const Key<NDIM> & key)410 pre_screening(const Key<NDIM> &key) const { 411 MADNESS_ASSERT(this->f->get_coeffs().is_local(key)); 412 return (not this->f->get_coeffs().find(key).get()->second.has_children()); 413 } sanity()414 void sanity()const{ 415 if(this->f==0) MADNESS_EXCEPTION("Leaf_op_other: f is NULL pointer",1); 416 if(this->op!=0) MADNESS_EXCEPTION("Leaf_op_other: Screening operator was set",1); 417 } 418 /// this should never be called for this leaf_op since the function in this class as already constructed 419 bool post_screening(const Key<NDIM> & key,const GenTensor<T> & G)420 post_screening(const Key<NDIM> &key,const GenTensor<T>& G) const { 421 MADNESS_EXCEPTION("post-determination was called for leaf_op_other",1); 422 return false; 423 } 424 /// this should never be called for this leaf_op since the function in this class as already constructed 425 bool compare_to_parent(const Key<NDIM> & key,const GenTensor<T> & a,const GenTensor<T> & b)426 compare_to_parent(const Key<NDIM> &key,const GenTensor<T>& a,const GenTensor<T>& b) const { 427 MADNESS_EXCEPTION("compare-to-parent was called for leaf_op_other",1); 428 return false; 429 } 430 /// this should never be called for this leaf_op since the function in this class as already constructed 431 bool special_refinement_needed(const Key<NDIM> & key)432 special_refinement_needed(const Key<NDIM>&key)const{ 433 MADNESS_EXCEPTION("special_refinement_needed was called for leaf_op_other",1); 434 return false; 435 } 436 437 template<typename Archive> 438 void serialize(Archive & ar)439 serialize(Archive& ar) { 440 ar & this->f; 441 } 442 443 }; 444 445 } /* namespace madness */ 446 447 #endif /* SRC_MADNESS_MRA_LEAFOP_H_ */ 448