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