MADNESS 0.10.1
mraimpl.h
Go to the documentation of this file.
1/*
2 This file is part of MADNESS.
3
4 Copyright (C) 2007,2010 Oak Ridge National Laboratory
5
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
10
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19
20 For more information please contact:
21
22 Robert J. Harrison
23 Oak Ridge National Laboratory
24 One Bethel Valley Road
25 P.O. Box 2008, MS-6367
26
27 email: harrisonrj@ornl.gov
28 tel: 865-241-3937
29 fax: 865-572-0680
30*/
31
32#ifndef MADNESS_MRA_MRAIMPL_H__INCLUDED
33#define MADNESS_MRA_MRAIMPL_H__INCLUDED
34
35#ifndef MPRAIMPLX
36#error "mraimpl.h should ONLY be included in one of the mraX.cc files (x=1..6)"
37#endif
38
40#include <memory>
41#include <math.h>
42#include <cmath>
47
50
51namespace std {
52 template <typename T>
53 bool isnan(const std::complex<T>& v) {
54 MADNESS_PRAGMA_CLANG(diagnostic push)
55 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wtautological-constant-compare")
56 return ::std::isnan(v.real()) || ::std::isnan(v.imag());
57 MADNESS_PRAGMA_CLANG(diagnostic pop)
58 }
59}
60
61/// \file mra/mraimpl.h
62/// \brief Declaration and initialization of static data, some implementation, some instantiation
63
64namespace madness {
65 // Definition and initialization of FunctionDefaults static members
66 // It cannot be an instance of FunctionFactory since we want to
67 // set the defaults independent of the data type.
68
69 template <typename T, std::size_t NDIM>
71 if (! two_scale_hg(k, &hg)) throw "failed to get twoscale coefficients";
72 hgT = copy(transpose(hg));
73
74 Slice sk(0,k-1), sk2(k,-1);
75 hgsonly = copy(hg(Slice(0,k-1),_));
76
77 h0 = copy(hg(sk,sk));
78 h1 = copy(hg(sk,sk2));
79 g0 = copy(hg(sk2,sk));
80 g1 = copy(hg(sk2,sk2));
81
82 h0T = copy(transpose(hg(sk,sk)));
83 h1T = copy(transpose(hg(sk,sk2)));
84 g0T = copy(transpose(hg(sk2,sk)));
85 g1T = copy(transpose(hg(sk2,sk2)));
86
87 }
88
89 template <typename T, std::size_t NDIM>
91 (int k, int npt, Tensor<double>& quad_x, Tensor<double>& quad_w,
92 Tensor<double>& quad_phi, Tensor<double>& quad_phiw, Tensor<double>& quad_phit) {
93 quad_x = Tensor<double>(npt); // point
94 quad_w = Tensor<double>(npt); // wheight
95 quad_phi = Tensor<double>(npt,k);
96 quad_phiw = Tensor<double>(npt,k);
97
98 gauss_legendre(npt,0.0,1.0,quad_x.ptr(),quad_w.ptr());
99 for (int mu=0; mu<npt; ++mu) {
100 double phi[200];
101 legendre_scaling_functions(quad_x(mu),k,phi);
102 for (int j=0; j<k; ++j) {
103 quad_phi(mu,j) = phi[j];
104 quad_phiw(mu,j) = quad_w(mu)*phi[j];
105 }
106 }
107 quad_phit = transpose(quad_phi);
108 }
109
110 template <typename T, std::size_t NDIM>
113 world.gop.fence(); // Make sure nothing is going on
114 MADNESS_CHECK_THROW(verify_tree_state_local(),"inconsistent coefficients in tree node");
115 MADNESS_CHECK_THROW(verify_parents_and_children(),"missing parents or children");
116 }
117
118 template <typename T, std::size_t NDIM>
120
121 // Ensure that parents and children exist appropriately
122 for (const auto& [key, node] : coeffs) {
123
124 if (key.level() > 0) {
125 const keyT parent = key.parent();
126 typename dcT::const_iterator pit = coeffs.find(parent).get();
127 if (pit == coeffs.end()) {
128 print(world.rank(), "FunctionImpl: verify: MISSING PARENT",key,parent);
129 std::cout.flush();
130 return false;
131 MADNESS_EXCEPTION("FunctionImpl: verify: MISSING PARENT", 0);
132 }
133 const nodeT& pnode = pit->second;
134 if (!pnode.has_children()) {
135 print(world.rank(), "FunctionImpl: verify: PARENT THINKS IT HAS NO CHILDREN",key,parent);
136 return false;
137 std::cout.flush();
138 MADNESS_EXCEPTION("FunctionImpl: verify: PARENT THINKS IT HAS NO CHILDREN", 0);
139 }
140 }
141
142 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
143 typename dcT::const_iterator cit = coeffs.find(kit.key()).get();
144 if (cit == coeffs.end()) {
145 if (node.has_children()) {
146 print(world.rank(), "FunctionImpl: verify: MISSING CHILD",key,kit.key());
147 return false;
148 std::cout.flush();
149 MADNESS_EXCEPTION("FunctionImpl: verify: MISSING CHILD", 0);
150 }
151 }
152 else {
153 if (! node.has_children()) {
154 print(world.rank(), "FunctionImpl: verify: UNEXPECTED CHILD",key,kit.key());
155 return false;
156 std::cout.flush();
157 MADNESS_EXCEPTION("FunctionImpl: verify: UNEXPECTED CHILD", 0);
158 }
159 }
160 }
161 }
162 world.gop.fence();
163 return true;
164 }
165
166
167
168 template<typename T, std::size_t NDIM>
170
171 const TreeState state=get_tree_state();
172 const int k=get_k();
173
174 auto check_internal_coeff_size = [&state, &k](const coeffT& c) {
175 if (state==compressed or state==nonstandard or state==nonstandard_with_leaves)
176 return c.dim(0)==2*k;
177 if (state==redundant) return c.dim(0)==k;
178 if (state==reconstructed) return (not c.is_assigned()); // must not be assigned at all
179 if (state==redundant_after_merge or state==nonstandard_after_apply) return true;
180 MADNESS_EXCEPTION("unknown state",1);
181 };
182 auto check_leaf_coeff_size = [&state, &k](const coeffT& c) {
183 // citation from compress_spawn
184 // if (not keepleaves) node.clear_coeff();
186 return c.dim(0)==k;
187 if (state==compressed or state==nonstandard) return (not c.is_assigned()); // must not be assigned at all
188 if (state==nonstandard_after_apply) return true;
189 MADNESS_EXCEPTION("unknown state",1);
190 };
191
192 bool good=true;
193 for (const auto& [key, node] : coeffs) {
194 const auto& c=node.coeff();
195 const bool is_internal=node.has_children();
196 const bool is_leaf=not node.has_children();
198 if (is_leaf) good=good and check_leaf_coeff_size(c);
199 if (not good) {
200 print("incorrect size of coefficients for key",key,"state",state,c.dim(0));;
201 }
202 }
203 return good;
204 }
205
206 template <typename T, std::size_t NDIM>
207 const std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > >& FunctionImpl<T,NDIM>::get_pmap() const {
208 return coeffs.get_pmap();
209 }
210
211
212 /// perform: this= alpha*f + beta*g, invoked by result
213
214 /// f and g are reconstructed, so we can save on the compress operation,
215 /// walk down the joint tree, and add leaf coefficients; effectively refines
216 /// to common finest level.
217 /// @param[in] alpha prefactor for f
218 /// @param[in] f first addend
219 /// @param[in] beta prefactor for g
220 /// @param[in] g second addend
221 /// @return nothing, but leaves this's tree reconstructed and as sum of f and g
222 template <typename T, std::size_t NDIM>
224 const double beta, const implT& g, const bool fence) {
225
226 MADNESS_CHECK_THROW(f.is_reconstructed(), "gaxpy_oop_reconstructed: f is not reconstructed");
227 MADNESS_CHECK_THROW(g.is_reconstructed(), "gaxpy_oop_reconstructed: g is not reconstructed");
228
229 ProcessID owner = coeffs.owner(cdata.key0);
230 if (world.rank() == owner) {
231
234
235 typedef add_op coeff_opT;
238 apply_opT apply_op(this);
239
240 woT::task(world.rank(), &implT:: template forward_traverse<coeff_opT,apply_opT>,
241 coeff_op, apply_op, cdata.key0);
242
243 }
244 set_tree_state(reconstructed);
245 if (fence) world.gop.fence();
246 }
247
248 /// Returns true if the function is compressed.
249 template <typename T, std::size_t NDIM>
251 return (tree_state==compressed);
252 }
253
254 /// Returns true if the function is reconstructed.
255 template <typename T, std::size_t NDIM>
257 return (tree_state==reconstructed);
258 }
259
260 /// Returns true if the function is redundant.
261 template <typename T, std::size_t NDIM>
263 return (tree_state==redundant);
264 }
265
266 /// Returns true if the function is redundant_after_merge.
267 template <typename T, std::size_t NDIM>
269 return (tree_state==redundant_after_merge);
270 }
271
272 template <typename T, std::size_t NDIM>
274 return (tree_state==nonstandard);
275 }
276
277 template <typename T, std::size_t NDIM>
279 return (tree_state==nonstandard_with_leaves);
280 }
281
282 template <typename T, std::size_t NDIM>
284 return tree_state==on_demand;
285 }
286
287 template <typename T, std::size_t NDIM>
289 return (tree_state==nonstandard_with_leaves);
290 }
291
292 template <typename T, std::size_t NDIM>
294 set_tree_state(on_demand);
295// this->on_demand=true;
296 functor=functor1;
297 }
298
299 template <typename T, std::size_t NDIM>
300 std::shared_ptr<FunctionFunctorInterface<T,NDIM> > FunctionImpl<T,NDIM>::get_functor() {
301 MADNESS_ASSERT(this->functor);
302 return functor;
303 }
304
305 template <typename T, std::size_t NDIM>
306 std::shared_ptr<FunctionFunctorInterface<T,NDIM> > FunctionImpl<T,NDIM>::get_functor() const {
307 MADNESS_ASSERT(this->functor);
308 return functor;
309 }
310
311 template <typename T, std::size_t NDIM>
313// this->on_demand=false;
314 set_tree_state(unknown);
315 functor.reset();
316 }
317
318 template <typename T, std::size_t NDIM>
320
321 template <typename T, std::size_t NDIM>
323
324 template <typename T, std::size_t NDIM>
326
327 template <typename T, std::size_t NDIM>
329
330 template <typename T, std::size_t NDIM>
331 void FunctionImpl<T,NDIM>::set_thresh(double value) {thresh = value;}
332
333 template <typename T, std::size_t NDIM>
334 bool FunctionImpl<T,NDIM>::get_autorefine() const {return autorefine;}
335
336 template <typename T, std::size_t NDIM>
337 void FunctionImpl<T,NDIM>::set_autorefine(bool value) {autorefine = value;}
338
339 template <typename T, std::size_t NDIM>
340 int FunctionImpl<T,NDIM>::get_k() const {return k;}
341
342 template <typename T, std::size_t NDIM>
343 const typename FunctionImpl<T,NDIM>::dcT& FunctionImpl<T,NDIM>::get_coeffs() const {return coeffs;}
344
345 template <typename T, std::size_t NDIM>
347
348 template <typename T, std::size_t NDIM>
350
351 template <typename T, std::size_t NDIM>
353 timer_accumulate.accumulate(time);
354 }
355
356 template <typename T, std::size_t NDIM>
358 if (world.rank()==0) {
359 timer_accumulate.print("accumulate");
360 timer_target_driven.print("target_driven");
361 timer_lr_result.print("result2low_rank");
362 }
363 }
364
365 template <typename T, std::size_t NDIM>
367 if (world.rank()==0) {
368 timer_accumulate.reset();
369 timer_target_driven.reset();
370 timer_lr_result.reset();
371 }
372 }
373
374 /// Truncate according to the threshold with optional global fence
375
376 /// If thresh<=0 the default value of this->thresh is used
377 template <typename T, std::size_t NDIM>
378 void FunctionImpl<T,NDIM>::truncate(double tol, bool fence) {
379 // Cannot put tol into object since it would make a race condition
380 if (tol <= 0.0)
381 tol = thresh;
382 if (world.rank() == coeffs.owner(cdata.key0)) {
383 if (is_compressed()) {
384 truncate_spawn(cdata.key0,tol);
385 } else {
386 truncate_reconstructed_spawn(cdata.key0,tol);
387 }
388 }
389 if (fence)
390 world.gop.fence();
391 }
392
393 template <typename T, std::size_t NDIM>
395 return cdata.key0;
396 }
397
398 /// Print a plane ("xy", "xz", or "yz") containing the point x to file
399
400 /// works for all dimensions; we walk through the tree, and if a leaf node
401 /// inside the sub-cell touches the plane we print it in pstricks format
402 template <typename T, std::size_t NDIM>
403 void FunctionImpl<T,NDIM>::print_plane(const std::string filename, const int xaxis, const int yaxis, const coordT& el2) {
404
405 // get the local information
406 Tensor<double> localinfo=print_plane_local(xaxis,yaxis,el2);
407
408 // lump all the local information together, and gather on node0
409 std::vector<Tensor<double> > localinfo_vec(1,localinfo);
410 std::vector<Tensor<double> > printinfo=world.gop.concat0(localinfo_vec);
411 world.gop.fence();
412
413 // do the actual print
414 if (world.rank()==0) do_print_plane(filename,printinfo,xaxis,yaxis,el2);
415 }
416
417 /// collect the data for a plot of the MRA structure locally on each node
418
419 /// @param[in] xaxis the x-axis in the plot (can be any axis of the MRA box)
420 /// @param[in] yaxis the y-axis in the plot (can be any axis of the MRA box)
421 /// @param[in] el2
422 template <typename T, std::size_t NDIM>
423 Tensor<double> FunctionImpl<T,NDIM>::print_plane_local(const int xaxis, const int yaxis, const coordT& el2) {
426 x_sim[0]+=1.e-10;
427
428 // dimensions are: (# boxes)(hue, x lo left, y lo left, x hi right, y hi right)
429 Tensor<double> plotinfo(coeffs.size(),5);
430 long counter=0;
431
432 // loop over local boxes, if the fit, add the info to the output tensor
433 typename dcT::const_iterator end = coeffs.end();
434 for (typename dcT::const_iterator it=coeffs.begin(); it!=end; ++it) {
435 const keyT& key = it->first;
436 const nodeT& node = it->second;
437
438 // thisKeyContains ignores dim0 and dim1
439 if (key.thisKeyContains(x_sim,xaxis,yaxis) and node.is_leaf() and (node.has_coeff())) {
440
441 Level n=key.level();
443 // get the diametral edges of the node in the plotting plane
444 double scale=std::pow(0.5,double(n));
445 double xloleft = scale*l[xaxis];
446 double yloleft = scale*l[yaxis];
447 double xhiright = scale*(l[xaxis]+1);
448 double yhiright = scale*(l[yaxis]+1);
449
450 // convert back to user coordinates
456
457
458 // if ((xloleft<-5.0) or (yloleft<-5.0) or (xhiright>5.0) or (yhiright>5.0)) continue;
459 if ((user[0]<-5.0) or (user[1]<-5.0) or (user[2]>5.0) or (user[3]>5.0)) continue;
460
461 // do rank or do error
462 double color=0.0;
463 if (1) {
464
465 const double maxrank=40;
466 do_convert_to_color hue(maxrank,false);
467 color=hue(node.coeff().rank());
468 } else {
469
470 // Make quadrature rule of higher order
471 const int npt = cdata.npt + 1;
472 Tensor<double> qx, qw, quad_phi, quad_phiw, quad_phit;
473 FunctionCommonData<T,NDIM>::_init_quadrature(k+1, npt, qx, qw, quad_phi, quad_phiw, quad_phit);
474 do_err_box< FunctionFunctorInterface<T,NDIM> > op(this, this->get_functor().get(), npt, qx, quad_phit, quad_phiw);
475
476 do_convert_to_color hue(1000.0,true);
477 double error=op(it);
478 error=sqrt(error);//*pow(2,key.level()*6);
479 color=hue(error);
480 }
481
483 plotinfo(counter,1)=user[0];
484 plotinfo(counter,2)=user[1];
485 plotinfo(counter,3)=user[2];
486 plotinfo(counter,4)=user[3];
487 ++counter;
488 }
489 }
490
491 // shrink the info
493 else plotinfo=plotinfo(Slice(0,counter-1),Slice(_));
494 return plotinfo;
495 }
496
497 /// print the MRA structure
498 template <typename T, std::size_t NDIM>
500 const int xaxis, const int yaxis, const coordT el2) {
501
502 // invoke only on master node
503 MADNESS_ASSERT(world.rank()==0);
504
505 // prepare file
506 FILE * pFile;
507 pFile = fopen(filename.c_str(), "w");
509
510
511 fprintf(pFile,"\\psset{unit=1cm}\n");
512 fprintf(pFile,"\\begin{pspicture}(%4.2f,%4.2f)(%4.2f,%4.2f)\n",
513 // cell(xaxis,0),cell(xaxis,1),cell(yaxis,0),cell(yaxis,1));
514 -5.0,-5.0,5.0,5.0);
515 fprintf(pFile,"\\pslinewidth=0.1pt\n");
516
517 for (std::vector<Tensor<double> >::const_iterator it=plotinfo.begin(); it!=plotinfo.end(); ++it) {
518
520 if (localinfo.has_data()) {
521
522 for (long i=0; i<localinfo.dim(0); ++i) {
523
524 fprintf(pFile,"\\newhsbcolor{mycolor}{%8.4f 1.0 0.7}\n",localinfo(i,0));
525 fprintf(pFile,"\\psframe["//linewidth=0.5pt,"
526 "fillstyle=solid,"
527 "fillcolor=mycolor]"
528 "(%12.8f,%12.8f)(%12.8f,%12.8f)\n",
529 localinfo(i,1),localinfo(i,2),localinfo(i,3),localinfo(i,4));
530 }
531 }
532 }
533
534
535 fprintf(pFile,"\\end{pspicture}\n");
536 fclose(pFile);
537 }
538
539 /// print the grid (the roots of the quadrature of each leaf box)
540 /// of this function in user xyz coordinates
541 template <typename T, std::size_t NDIM>
542 void FunctionImpl<T,NDIM>::print_grid(const std::string filename) const {
543
544 // get the local information
545 std::vector<keyT> local_keys=local_leaf_keys();
546
547 // lump all the local information together, and gather on node0
548 std::vector<keyT> all_keys=world.gop.concat0(local_keys);
549 world.gop.fence();
550
551 // do the actual print
552 if (world.rank()==0) do_print_grid(filename,all_keys);
553
554 }
555
556 /// return the keys of the local leaf boxes
557 template <typename T, std::size_t NDIM>
558 std::vector<typename FunctionImpl<T,NDIM>::keyT> FunctionImpl<T,NDIM>::local_leaf_keys() const {
559
560 // coeffs.size is maximum number of keys (includes internal keys)
561 std::vector<keyT> keys(coeffs.size());
562
563 // loop over local boxes, if they are leaf boxes add their quadrature roots
564 // to the output tensor
565 int i=0;
566 typename dcT::const_iterator end = coeffs.end();
567 for (typename dcT::const_iterator it=coeffs.begin(); it!=end; ++it) {
568 const keyT& key = it->first;
569 const nodeT& node = it->second;
570 if (node.is_leaf()) keys[i++]=key;
571 }
572
573 // shrink the vector to number of leaf keys
574 keys.resize(i);
575 return keys;
576 }
577
578 /// print the grid in xyz format
579
580 /// the quadrature points and the key information will be written to file,
581 /// @param[in] filename where the quadrature points will be written to
582 /// @param[in] keys all leaf keys
583 template <typename T, std::size_t NDIM>
584 void FunctionImpl<T,NDIM>::do_print_grid(const std::string filename, const std::vector<keyT>& keys) const {
585 // invoke only on master node
586 MADNESS_ASSERT(world.rank()==0);
587
588 // the quadrature points in simulation coordinates of the root node
589 const Tensor<double> qx=cdata.quad_x;
590 const size_t npt = qx.dim(0);
591
592 // the number of coordinates (grid point tuples) per box ({x1},{x2},{x3},..,{xNDIM})
593 long npoints=power<NDIM>(npt);
594 // the number of boxes
595 long nboxes=keys.size();
596
597 // prepare file
598 FILE * pFile;
599 pFile = fopen(filename.c_str(), "w");
600
601 fprintf(pFile,"%ld\n",npoints*nboxes);
602 fprintf(pFile,"%ld points per box and %ld boxes \n",npoints,nboxes);
603
604 // loop over all leaf boxes
605 typename std::vector<keyT>::const_iterator key_it=keys.begin();
606 for (key_it=keys.begin(); key_it!=keys.end(); ++key_it) {
607
608 const keyT& key=*key_it;
609 fprintf(pFile,"# key: %8d",key.level());
610 for (size_t d=0; d<NDIM; d++) fprintf(pFile,"%8d",int(key.translation()[d]));
611 fprintf(pFile,"\n");
612
613 // this is borrowed from fcube
614 const Vector<Translation,NDIM>& l = key.translation();
615 const Level n = key.level();
616 const double h = std::pow(0.5,double(n));
617 coordT c; // will hold the point in user coordinates
618
621
622 if (NDIM == 3) {
623 for (size_t i=0; i<npt; ++i) {
624 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
625 for (size_t j=0; j<npt; ++j) {
626 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
627 for (size_t k=0; k<npt; ++k) {
628 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
629 // grid weights
630 // double scale = pow(0.5,0.5*NDIM*key.level())*
631 // sqrt(FunctionDefaults<NDIM>::get_cell_volume());
632 // double w=cdata.quad_phiw[i]*cdata.quad_phiw[j]*cdata.quad_phiw[k];
633
634 fprintf(pFile,"%18.12f %18.12f %18.12f\n",c[0],c[1],c[2]);
635 // fprintf(pFile,"%18.12e %18.12e %18.12e %18.12e\n",c[0],c[1],c[2],w*scale);
636 }
637 }
638 }
639 } else {
640 MADNESS_EXCEPTION("only NDIM=3 in print_grid",0);
641 }
642 }
643 fclose(pFile);
644 }
645
646
647 /// Returns the truncation threshold according to truncate_method
648 template <typename T, std::size_t NDIM>
649 double FunctionImpl<T,NDIM>::truncate_tol(double tol, const keyT& key) const {
650
651 // RJH ... introduced max level here to avoid runaway
652 // refinement due to truncation threshold going down to
653 // intrinsic numerical error
654 const int MAXLEVEL1 = 20; // 0.5**20 ~= 1e-6
655 const int MAXLEVEL2 = 10; // 0.25**10 ~= 1e-6
656
657 if (truncate_mode == 0) {
658 return tol;
659 }
660 else if (truncate_mode == 1) {
662 return tol*std::min(1.0,pow(0.5,double(std::min(key.level(),MAXLEVEL1)))*L);
663 }
664 else if (truncate_mode == 2) {
666 return tol*std::min(1.0,pow(0.25,double(std::min(key.level(),MAXLEVEL2)))*L*L);
667 }
668 else if (truncate_mode == 3) {
669 // similar to truncate mode 1, but with an additional factor to
670 // account for an increased number of boxes in higher dimensions
671
672 // here is our handwaving argument: this threshold will give each
673 // FunctionNode an error of less than tol. The total error can
674 // then be as high as sqrt(#nodes) * tol. Therefore in order to
675 // account for higher dimensions: divide tol by about the root of
676 // number of siblings (2^NDIM) that have a large error when we
677 // refine along a deep branch of the tree. FAB
678 //
679 // Nope ... it can easily be as high as #nodes * tol. The real
680 // fix for this is an end-to-end error analysis of the larger
681 // application and if desired to include this factor into the
682 // threshold selected by the application. RJH
683 const static double fac=1.0/std::pow(2,NDIM*0.5);
684 tol*=fac;
685
687 return tol*std::min(1.0,pow(0.5,double(std::min(key.level(),MAXLEVEL1)))*L);
688
689 } else {
690 MADNESS_EXCEPTION("truncate_mode invalid",truncate_mode);
691 }
692 }
693
694 /// Returns patch referring to coeffs of child in parent box
695 template <typename T, std::size_t NDIM>
696 std::vector<Slice> FunctionImpl<T,NDIM>::child_patch(const keyT& child) const {
697 std::vector<Slice> s(NDIM);
698 const Vector<Translation,NDIM>& l = child.translation();
699 for (std::size_t i=0; i<NDIM; ++i)
700 s[i] = cdata.s[l[i]&1]; // Lowest bit of translation
701 return s;
702 }
703
704 /// Directly project parent NS coeffs to child NS coeffs
705
706 template <typename T, std::size_t NDIM>
708 const keyT& child, const keyT& parent, const coeffT& coeff) const {
709
710 const implT* f=this;
711 // MADNESS_ASSERT(coeff.tensor_type()==TT_FULL);
712 coeffT result(f->cdata.v2k,coeff.tensor_type());
713
714 // if the node for child is existent in f, and it is an internal node, we
715 // automatically have the NS form; if it is a leaf node, we only have the
716 // sum coeffs, so we take zero difference coeffs
717 if (child==parent) {
718 if (coeff.dim(0)==2*f->get_k()) result=coeff; // internal node
719 else if (coeff.dim(0)==f->get_k()) { // leaf node
720 result(f->cdata.s0)+=coeff;
721 } else {
722 MADNESS_EXCEPTION("confused k in parent_to_child_NS",1);
723 }
724 } else if (child.level()>parent.level()) {
725
726 // parent and coeff should refer to a leaf node with sum coeffs only
727 // b/c tree should be compressed with leaves kept.
728 MADNESS_ASSERT(coeff.dim(0)==f->get_k());
729 const coeffT scoeff=f->parent_to_child(coeff,parent,child);
730 result(f->cdata.s0)+=scoeff;
731 } else {
732 MADNESS_EXCEPTION("confused keys in parent_to_child_NS",1);
733 }
734 return result;
735 }
736
737 /// truncate tree at a certain level
738 template <typename T, std::size_t NDIM>
740 this->make_redundant(true);
741
742 typename dcT::iterator end = coeffs.end();
743 for (typename dcT::iterator it= coeffs.begin(); it!=end; ++it) {
744 keyT key=it->first;
745 nodeT& node=it->second;
746 if (key.level()>max_level) coeffs.erase(key);
747 if (key.level()==max_level) node.set_has_children(false);
748 }
749 this->undo_redundant(true);
750 }
751
752
753 /// Returns some asymmetry measure ... no comms
754 template <typename T, std::size_t NDIM>
758 return world.taskq.reduce<double,rangeT,do_check_symmetry_local>(rangeT(coeffs.begin(),coeffs.end()),
760 }
761
762
763 /// Refine multiple functions down to the same finest level
764
765 /// @param[v] is the vector of functions we are refining.
766 /// @param[key] is the current node.
767 /// @param[c] is the vector of coefficients passed from above.
768 template <typename T, std::size_t NDIM>
770 const std::vector<tensorT>& c,
771 const keyT key) {
772 if (key == cdata.key0 && coeffs.owner(key)!=world.rank()) return;
773
774 // First insert coefficients from above ... also get write accessors here
775 std::unique_ptr<typename dcT::accessor[]> acc(new typename dcT::accessor[v.size()]);
776 for (unsigned int i=0; i<c.size(); i++) {
777 MADNESS_ASSERT(v[i]->coeffs.get_pmap() == coeffs.get_pmap());
778 MADNESS_ASSERT(v[i]->coeffs.owner(key) == world.rank());
779 bool exists = ! v[i]->coeffs.insert(acc[i],key);
780 if (c[i].size()) {
781 MADNESS_CHECK(!exists);
782 acc[i]->second = nodeT(coeffT(c[i],targs),false);
783 }
784 else {
785 MADNESS_ASSERT(exists);
786 }
787 }
788
789 // If everyone has coefficients we are done
790 bool done = true;
791 for (unsigned int i=0; i<v.size(); i++) {
792 done &= acc[i]->second.has_coeff();
793 }
794
795 if (!done) {
796 // Those functions with coefficients need to be refined down
797 std::vector<tensorT> d(v.size());
798 for (unsigned int i=0; i<v.size(); i++) {
799 if (acc[i]->second.has_coeff()) {
800 tensorT s(cdata.v2k);
801 // s(cdata.s0) = acc[i]->second.coeff()(___);
802 s(cdata.s0) = acc[i]->second.coeff().full_tensor();
803 acc[i]->second.clear_coeff();
804 d[i] = unfilter(s);
805 acc[i]->second.set_has_children(true);
806 }
807 }
808
809 // Loop thru children and pass down
810 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
811 const keyT& child = kit.key();
812 std::vector<Slice> cp = child_patch(child);
813 std::vector<tensorT> childc(v.size());
814 for (unsigned int i=0; i<v.size(); i++) {
815 if (d[i].size()) childc[i] = copy(d[i](cp));
816 }
817 woT::task(coeffs.owner(child), &implT::refine_to_common_level, v, childc, child);
818 }
819 }
820 }
821
822 // horrifically non-scalable
823 template <typename T, std::size_t NDIM>
825 if (world.size()> 1000)
826 throw "NO!";
827 box_leaf[from] = nl;
828 box_interior[from] = ni;
829 }
830
831 /// Prints summary of data distribution
832 template <typename T, std::size_t NDIM>
834 if (world.size() >= 1000)
835 return;
836 for (int i=0; i<world.size(); ++i)
837 box_leaf[i] = box_interior[i] == 0;
838 world.gop.fence();
839 long nleaf=0, ninterior=0;
840 typename dcT::const_iterator end = coeffs.end();
841 for (typename dcT::const_iterator it=coeffs.begin(); it!=end; ++it) {
842 const nodeT& node = it->second;
843 if (node.is_leaf())
844 ++nleaf;
845 else
846 ++ninterior;
847 }
848 this->send(0, &implT::put_in_box, world.rank(), nleaf, ninterior);
849 world.gop.fence();
850 if (world.rank() == 0) {
851 for (int i=0; i<world.size(); ++i) {
852 printf("load: %5d %8ld %8ld\n", i, box_leaf[i], box_interior[i]);
853 }
854 }
855 world.gop.fence();
856 }
857
858 template <typename T, std::size_t NDIM>
859 bool FunctionImpl<T,NDIM>::noautorefine(const keyT& key, const tensorT& t) const {
860 return false;
861 }
862
863 /// Returns true if this block of coeffs needs autorefining
864 template <typename T, std::size_t NDIM>
865 bool FunctionImpl<T,NDIM>::autorefine_square_test(const keyT& key, const nodeT& t) const {
866 double lo, hi;
867 tnorm(t.coeff().full_tensor(), &lo, &hi);
868 double test = 2*lo*hi + hi*hi;
869 //print("autoreftest",key,thresh,truncate_tol(thresh, key),lo,hi,test);
870 return test> truncate_tol(thresh, key);
871 }
872
873
874 /// is this the same as trickle_down() ?
875 template <typename T, std::size_t NDIM>
877 typename dcT::accessor acc;
878 coeffs.insert(acc,key);
879 nodeT& node = acc->second;
880 coeffT& c = node.coeff();
881
882 //print(key,"received",s.normf(),c.normf(),node.has_children());
883
884 if (s.size() > 0) {
885 if (c.size() > 0)
886 c.gaxpy(1.0,s,1.0);
887 else
888 c = s;
889 }
890
891 if (node.has_children()) {
892 coeffT d;
893 if (c.has_data()) {
894 d = coeffT(cdata.v2k,targs);
895 d(cdata.s0) += c;
896 d = unfilter(d);
897 node.clear_coeff();
898 }
899 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
900 coeffT ss;
901 const keyT& child = kit.key();
902 if (d.size() > 0) ss = copy(d(child_patch(child)));
903 //print(key,"sending",ss.normf(),"to",child);
904 woT::task(coeffs.owner(child), &implT::sum_down_spawn, child, ss);
905 }
906 }
907 else {
908 // Missing coeffs assumed to be zero
909 if (c.size() <= 0) c = coeffT(cdata.vk,targs);
910 }
911 }
912
913 /// After 1d push operator must sum coeffs down the tree to restore correct scaling function coefficients
914
915 /// @warning NUMERICALLY UNSTABLE for tensor types other than TT_FULL: sum_down propagates
916 /// coefficients from the root to the leaves, and at each level the low-rank (SVD) tensor
917 /// operations (v2k construction at target thresh, unfilter, per-node add_SVD) re-truncate
918 /// the rank. These truncation errors compound multiplicatively down the tree, so a deeply
919 /// refined (e.g. cuspy) function loses ~1-2 digits relative to the TT_FULL result. Prefer a
920 /// path that avoids sum_down for TT_2D/TT_SVD where threshold-level accuracy is required.
921 template <typename T, std::size_t NDIM>
923 if (get_tensor_type()!=TT_FULL && world.rank()==0)
924 print("WARNING: sum_down is numerically unstable for tensor type",get_tensor_type());
925 tree_state=reconstructed;
926 if (world.rank() == coeffs.owner(cdata.key0)) sum_down_spawn(cdata.key0, coeffT());
927 if (fence) world.gop.fence();
928 }
929
930
931 template <typename T, std::size_t NDIM>
933 const implT* f,
934 const keyT& key,
935 const std::pair<keyT,coeffT>& left,
936 const std::pair<keyT,coeffT>& center,
937 const std::pair<keyT,coeffT>& right) {
938 D->forward_do_diff1(f,this,key,left,center,right);
939 }
940
941
942 template <typename T, std::size_t NDIM>
944 const implT* f,
945 const keyT& key,
946 const std::pair<keyT,coeffT>& left,
947 const std::pair<keyT,coeffT>& center,
948 const std::pair<keyT,coeffT>& right) {
949 D->do_diff1(f,this,key,left,center,right);
950 }
951
952
953 // Called by result function to differentiate f
954 template <typename T, std::size_t NDIM>
955 void FunctionImpl<T,NDIM>::diff(const DerivativeBase<T,NDIM>* D, const implT* f, bool fence) {
956 typedef std::pair<keyT,coeffT> argT;
957 if (D->parallel_submit_) {
958 D->submit_diff_tasks(f, this); // same tasks, spawned from the pool instead of here
959 if (fence) world.gop.fence();
960 return;
961 }
962 for (const auto& [key, node]: f->coeffs) {
963 if (node.has_coeff()) {
964 Future<argT> left = D->find_neighbor(f, key,-1);
965 argT center(key,node.coeff());
966 Future<argT> right = D->find_neighbor(f, key, 1);
967 world.taskq.add(*this, &implT::do_diff1, D, f, key, left, center, right, TaskAttributes::hipri());
968 }
969 else {
970 coeffs.replace(key,nodeT(coeffT(),true)); // Empty internal node
971 }
972 }
973 if (fence) world.gop.fence();
974 }
975
976
977 /// return the a std::pair<key, node>, which MUST exist
978 template <typename T, std::size_t NDIM>
980 MADNESS_ASSERT(coeffs.probe(key));
981 ShallowNode<T,NDIM> snode(coeffs.find(key).get()->second);
982 return std::pair<Key<NDIM>,ShallowNode<T,NDIM> >(key,snode);
983 }
984
985 /// multiply the ket with a one-electron potential rr(1,2)= f(1,2)*g(1)
986
987 /// @param[in] val_ket function values of f(1,2)
988 /// @param[in] val_pot function values of g(1)
989 /// @param[in] particle if 0 then g(1), if 1 then g(2)
990 /// @return the resulting function values
991 template <typename T, std::size_t NDIM>
993 const coeffT& val_pot, int particle) const {
994
996 MADNESS_ASSERT(val_pot.is_full_tensor());
997 MADNESS_ASSERT(val_ket.is_svd_tensor());
998
999 std::vector<long> vkhalf=std::vector<long>(NDIM/2,cdata.vk[0]);
1001 ones=1.0;
1002
1003 TensorArgs targs(-1.0,val_ket.tensor_type());
1004 coeffT pot12;
1005 if (particle==0) pot12=outer(val_pot.full_tensor(),ones,targs);
1006 else if (particle==1) pot12=outer(ones,val_pot.full_tensor(),targs);
1007
1008 coeffT result=copy(val_ket);
1009 result.emul(pot12);
1010
1011 return result;
1012 }
1013
1014
1015 /// given several coefficient tensors, assemble a result tensor
1016
1017 /// the result looks like: (v(1,2) + v(1) + v(2)) |ket(1,2)>
1018 /// or (v(1,2) + v(1) + v(2)) |p(1) p(2)>
1019 /// i.e. coefficients for the ket and coefficients for the two particles are
1020 /// mutually exclusive. All potential terms are optional, just pass in empty coeffs.
1021 /// @param[in] key the key of the FunctionNode to which these coeffs belong
1022 /// @param[in] cket coefficients of the ket
1023 /// @param[in] vpotential1 function values of the potential for particle 1
1024 /// @param[in] vpotential2 function values of the potential for particle 2
1025 /// @param[in] veri function values for the 2-particle potential
1026 template <typename T, std::size_t NDIM>
1028 const keyT& key, const coeffT& coeff_ket, const coeffT& vpotential1,
1029 const coeffT& vpotential2, const tensorT& veri) const {
1030
1031 // take a shortcut if we are already done
1032 bool ket_only=(not (vpotential1.has_data() or vpotential2.has_data() or veri.has_data()));
1033 if (ket_only) return coeff_ket;
1034
1035 // switch to values instead of coefficients
1036 coeffT val_ket=coeffs2values(key,coeff_ket);
1037
1038 // the result tensor
1039 coeffT val_result=coeffT(val_ket.ndim(),val_ket.dims(),this->get_tensor_args().tt);
1041
1042 // potential for particles 1 and 2, must be done in TT_2D
1043 if (vpotential1.has_data() or vpotential2.has_data()) {
1045 }
1048
1049 // values for eri: this must be done in full rank...
1050 if (veri.has_data()) {
1051 tensorT val_ket2=val_ket.full_tensor_copy().emul(veri);
1052 if (val_result.has_data()) val_ket2+=val_result.full_tensor();
1053 // values2coeffs expensive (30%), coeffT() (relatively) cheap (8%)
1054 coeff_result=coeffT(values2coeffs(key,val_ket2),this->get_tensor_args());
1055
1056 } else {
1057
1058 // convert back to original tensor type
1059 val_ket=val_ket.convert(get_tensor_args());
1060 MADNESS_ASSERT(val_result.has_data());
1061 coeff_result=values2coeffs(key,val_result);
1062 coeff_result.reduce_rank(this->get_tensor_args().thresh);
1063 }
1064
1065 return coeff_result;
1066
1067 }
1068
1069 /// Permute the dimensions of f according to map, result on this
1070 template <typename T, std::size_t NDIM>
1071 void FunctionImpl<T,NDIM>::mapdim(const implT& f, const std::vector<long>& map, bool fence) {
1072
1074 const_cast<implT*>(&f)->flo_unary_op_node_inplace(do_mapdim(map,*this),fence);
1075
1076 }
1077
1078 /// mirror the dimensions of f according to mirror, result on this
1079 template <typename T, std::size_t NDIM>
1080 void FunctionImpl<T,NDIM>::mirror(const implT& f, const std::vector<long>& mirrormap, bool fence) {
1082 const_cast<implT*>(&f)->flo_unary_op_node_inplace(do_mirror(mirrormap,*this),fence);
1083 }
1084
1085 /// map and mirror the translation index and the coefficients, result on this
1086
1087 /// first map the dimensions, the mirror!
1088 /// this = mirror(map(f))
1089 template <typename T, std::size_t NDIM>
1090 void FunctionImpl<T,NDIM>::map_and_mirror(const implT& f, const std::vector<long>& map,
1091 const std::vector<long>& mirror, bool fence) {
1093 const_cast<implT*>(&f)->flo_unary_op_node_inplace(do_map_and_mirror(map,mirror,*this),fence);
1094 }
1095
1096
1097
1098 /// take the average of two functions, similar to: this=0.5*(this+rhs)
1099
1100 /// works in either basis and also in nonstandard form
1101 template <typename T, std::size_t NDIM>
1103
1105 this->scale_inplace(0.5,true);
1106 flo_unary_op_node_inplace(do_reduce_rank(targs),true);
1107 }
1108
1109 /// change the tensor type of the coefficients in the FunctionNode
1110
1111 /// @param[in] targs target tensor arguments (threshold and full/low rank)
1112 template <typename T, std::size_t NDIM>
1114 flo_unary_op_node_inplace(do_change_tensor_type(targs,*this),fence);
1115 }
1116
1117 /// reduce the rank of the coefficients tensors
1118
1119 /// @param[in] targs target tensor arguments (threshold and full/low rank)
1120 template <typename T, std::size_t NDIM>
1121 void FunctionImpl<T,NDIM>::reduce_rank(const double thresh, bool fence) {
1122 flo_unary_op_node_inplace(do_reduce_rank(thresh),fence);
1123 }
1124
1125 /// reduce the rank of the coefficients tensors
1126
1127 /// @param[in] targs target tensor arguments (threshold and full/low rank)
1128 template <typename T, std::size_t NDIM>
1129 void FunctionImpl<T,NDIM>::chop_at_level(const int n, bool fence) {
1130 std::list<keyT> to_be_erased;
1131 for (auto it=coeffs.begin(); it!=coeffs.end(); ++it) {
1132 const keyT& key=it->first;
1133 nodeT& node=it->second;
1134 if (key.level()==n) node.set_is_leaf(true);
1135 if (key.level()>n) to_be_erased.push_back(key);
1136 }
1137 for (auto& key : to_be_erased) coeffs.erase(key);
1138 }
1139
1140
1141/// compute norm of s and d coefficients for all nodes
1142
1143 /// @param[in] targs target tensor arguments (threshold and full/low rank)
1144 template <typename T, std::size_t NDIM>
1146 //const auto& data=FunctionCommonData<T,NDIM>::get(get_k());
1147 flo_unary_op_node_inplace(
1148 do_compute_snorm_and_dnorm(cdata),fence);
1149 }
1150
1151
1152/// Transform sum coefficients at level n to sums+differences at level n-1
1153
1154/// Given scaling function coefficients s[n][l][i] and s[n][l+1][i]
1155/// return the scaling function and wavelet coefficients at the
1156/// coarser level. I.e., decompose Vn using Vn = Vn-1 + Wn-1.
1157 /// \code
1158 /// s_i = sum(j) h0_ij*s0_j + h1_ij*s1_j
1159 /// d_i = sum(j) g0_ij*s0_j + g1_ij*s1_j
1160 // \endcode
1161 /// Returns a new tensor and has no side effects. Works for any
1162 /// number of dimensions.
1163 ///
1164 /// No communication involved.
1165 template <typename T, std::size_t NDIM>
1167 tensorT r(cdata.v2k,false);
1168 tensorT w(cdata.v2k,false);
1169 return fast_transform(s,cdata.hgT,r,w);
1170 //return transform(s,cdata.hgT);
1171 }
1172
1173 template <typename T, std::size_t NDIM>
1175 coeffT result=transform(s,cdata.hgT);
1176 return result;
1177 }
1178
1179 /// Transform sums+differences at level n to sum coefficients at level n+1
1180
1181 /// Given scaling function and wavelet coefficients (s and d)
1182 /// returns the scaling function coefficients at the next finer
1183 /// level. I.e., reconstruct Vn using Vn = Vn-1 + Wn-1.
1184 /// \code
1185 /// s0 = sum(j) h0_ji*s_j + g0_ji*d_j
1186 /// s1 = sum(j) h1_ji*s_j + g1_ji*d_j
1187 /// \endcode
1188 /// Returns a new tensor and has no side effects
1189 ///
1190 /// If (sonly) ... then ss is only the scaling function coeff (and
1191 /// assume the d are zero). Works for any number of dimensions.
1192 ///
1193 /// No communication involved.
1194 template <typename T, std::size_t NDIM>
1196 tensorT r(cdata.v2k,false);
1197 tensorT w(cdata.v2k,false);
1198 return fast_transform(s,cdata.hg,r,w);
1199 //return transform(s, cdata.hg);
1200 }
1201
1202 template <typename T, std::size_t NDIM>
1204 return transform(s,cdata.hg);
1205 }
1206
1207 /// downsample the sum coefficients of level n+1 to sum coeffs on level n
1208
1209 /// specialization of the filter method, will yield only the sum coefficients
1210 /// @param[in] key key of level n
1211 /// @param[in] v vector of sum coefficients of level n+1
1212 /// @param[in] args TensorArguments for possible low rank approximations
1213 /// @return sum coefficients on level n in full tensor format
1214 template <typename T, std::size_t NDIM>
1216
1217 tensorT result(cdata.vk);
1218
1219 // the twoscale coefficients: for downsampling use h0/h1; see Alpert Eq (3.34a)
1220 const tensorT h[2] = {cdata.h0T, cdata.h1T};
1222
1223 // loop over all child nodes, transform and accumulate
1224 long i=0;
1225 for (KeyChildIterator<NDIM> kit(key); kit; ++kit,++i) {
1226
1227 // get the appropriate twoscale coefficients for each dimension
1228 for (size_t ii=0; ii<NDIM; ++ii) matrices[ii]=h[kit.key().translation()[ii]%2];
1229
1230 // transform and accumulate on the result
1231 result+=general_transform(v[i].get(),matrices).full_tensor();
1232
1233 }
1234 return result;
1235 }
1236
1237 /// upsample the sum coefficients of level 1 to sum coeffs on level n+1
1238
1239 /// specialization of the unfilter method, will transform only the sum coefficients
1240 /// @param[in] key key of level n+1
1241 /// @param[in] coeff sum coefficients of level n (does NOT belong to key!!)
1242 /// @param[in] args TensorArguments for possible low rank approximations
1243 /// @return sum coefficients on level n+1
1244 template <typename T, std::size_t NDIM>
1246
1247 // the twoscale coefficients: for upsampling use h0/h1; see Alpert Eq (3.35a/b)
1248 // note there are no difference coefficients; if you want that use unfilter
1249 const tensorT h[2] = {cdata.h0, cdata.h1};
1251
1252 // get the appropriate twoscale coefficients for each dimension
1253 for (size_t ii=0; ii<NDIM; ++ii) matrices[ii]=h[key.translation()[ii]%2];
1254
1255 // transform and accumulate on the result
1256 const coeffT result=general_transform(coeff,matrices);
1257 return result;
1258 }
1259
1260
1261 /// Projects old function into new basis (only in reconstructed form)
1262 template <typename T, std::size_t NDIM>
1263 void FunctionImpl<T,NDIM>::project(const implT& old, bool fence) {
1264 long kmin = std::min(cdata.k,old.cdata.k);
1265 std::vector<Slice> s(NDIM,Slice(0,kmin-1));
1266 typename dcT::const_iterator end = old.coeffs.end();
1267 for (typename dcT::const_iterator it=old.coeffs.begin(); it!=end; ++it) {
1268 const keyT& key = it->first;
1269 const nodeT& node = it->second;
1270 if (node.has_coeff()) {
1271 coeffT c(cdata.vk,targs);
1272 c(s) += node.coeff()(s);
1273 coeffs.replace(key,nodeT(c,false));
1274 }
1275 else {
1276 coeffs.replace(key,nodeT(coeffT(),true));
1277 }
1278 }
1279 if (fence)
1280 world.gop.fence();
1281 }
1282
1283 template <typename T, std::size_t NDIM>
1285 return coeffs.probe(key) && coeffs.find(key).get()->second.has_children();
1286 }
1287
1288 template <typename T, std::size_t NDIM>
1290 return coeffs.probe(key) && (not coeffs.find(key).get()->second.has_children());
1291 }
1292
1293
1294 template <typename T, std::size_t NDIM>
1295 void FunctionImpl<T,NDIM>::broaden_op(const keyT& key, const std::vector< Future <bool> >& v) {
1296 for (unsigned int i=0; i<v.size(); ++i) {
1297 if (v[i]) {
1298 refine_op(true_refine_test(), key);
1299 break;
1300 }
1301 }
1302 }
1303
1304 // For each local node sets value of norm tree, snorm and dnorm to 0.0
1305 template <typename T, std::size_t NDIM>
1307 typename dcT::iterator end = coeffs.end();
1308 for (typename dcT::iterator it=coeffs.begin(); it!=end; ++it) {
1309 it->second.set_norm_tree(0.0);
1310 it->second.set_snorm(0.0);
1311 it->second.set_dnorm(0.0);
1312 }
1313 }
1314
1315 // Broaden tree
1316 template <typename T, std::size_t NDIM>
1318 typename dcT::iterator end = coeffs.end();
1319 for (typename dcT::iterator it=coeffs.begin(); it!=end; ++it) {
1320 const keyT& key = it->first;
1321 typename dcT::accessor acc;
1322 const auto found = coeffs.find(acc,key);
1324 nodeT& node = acc->second;
1325 if (node.has_coeff() &&
1326 node.get_norm_tree() != -1.0 &&
1327 node.coeff().normf() >= truncate_tol(thresh,key)) {
1328
1329 node.set_norm_tree(-1.0); // Indicates already broadened or result of broadening/refining
1330
1331 //int ndir = std::pow(3,NDIM);
1332 int ndir = static_cast<int>(std::pow(static_cast<double>(3), static_cast<int>(NDIM)));
1333 std::vector< Future <bool> > v = future_vector_factory<bool>(ndir);
1334 keyT neigh;
1335 int i=0;
1336 for (HighDimIndexIterator it(NDIM,3); it; ++it) {
1338 for (std::size_t d=0; d<NDIM; ++d) {
1339 const int odd = key.translation()[d] & 0x1L; // 1 if odd, 0 if even
1340 l[d] -= 1; // (0,1,2) --> (-1,0,1)
1341 if (l[d] == -1)
1342 l[d] = -1-odd;
1343 else if (l[d] == 1)
1344 l[d] = 2 - odd;
1345 }
1346 keyT neigh = neighbor(key, keyT(key.level(),l), is_periodic);
1347
1348 if (neigh.is_valid()) {
1349 v[i++] = this->task(coeffs.owner(neigh), &implT::exists_and_has_children, neigh);
1350 }
1351 else {
1352 v[i++].set(false);
1353 }
1354 }
1355 woT::task(world.rank(), &implT::broaden_op, key, v);
1356 }
1357 }
1358 // Reset value of norm tree so that can repeat broadening
1359 if (fence) {
1360 world.gop.fence();
1361 zero_norm_tree();
1362 world.gop.fence();
1363 }
1364 }
1365
1366 /// sum all the contributions from all scales after applying an operator in mod-NS form
1367 template <typename T, std::size_t NDIM>
1369 set_tree_state(reconstructed);
1370 if (world.rank() == coeffs.owner(cdata.key0))
1371 woT::task(world.rank(), &implT::trickle_down_op, cdata.key0,coeffT());
1372 if (fence) world.gop.fence();
1373 }
1374
1375 /// sum all the contributions from all scales after applying an operator in mod-NS form
1376
1377 /// cf reconstruct_op
1378 template <typename T, std::size_t NDIM>
1380 // Note that after application of an integral operator not all
1381 // siblings may be present so it is necessary to check existence
1382 // and if absent insert an empty leaf node.
1383 //
1384 // If summing the result of an integral operator (i.e., from
1385 // non-standard form) there will be significant scaling function
1386 // coefficients at all levels and possibly difference coefficients
1387 // in leaves, hence the tree may refine as a result.
1388 typename dcT::iterator it = coeffs.find(key).get();
1389 if (it == coeffs.end()) {
1390 coeffs.replace(key,nodeT(coeffT(),false));
1391 it = coeffs.find(key).get();
1392 }
1393 nodeT& node = it->second;
1394
1395 // The integral operator will correctly connect interior nodes
1396 // to children but may leave interior nodes without coefficients
1397 // ... but they still need to sum down so just give them zeros
1398 if (node.coeff().has_no_data()) node.coeff()=coeffT(cdata.vk,targs);
1399
1400 // if (node.has_children() || node.has_coeff()) { // Must allow for inconsistent state from transform, etc.
1401 if (node.has_children()) { // Must allow for inconsistent state from transform, etc.
1402 coeffT d = node.coeff();
1403 if (key.level() > 0) d += s; // -- note accumulate for NS summation
1404 node.clear_coeff();
1405 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
1406 const keyT& child = kit.key();
1407 coeffT ss= upsample(child,d);
1408 ss.reduce_rank(thresh);
1410 woT::task(coeffs.owner(child), &implT::trickle_down_op, child, ss);
1411 }
1412 }
1413 else {
1414 node.coeff()+=s;
1415 node.coeff().reduce_rank(thresh);
1417 }
1418
1419 /// change the tree state of this function, might or might not respect fence!
1420 template <typename T, std::size_t NDIM>
1424 if (current_state==finalstate) return;
1426 // try direct conversion if possible, otherwise change to reconstructed state,
1427 // and then convert to final state
1428
1429 // try direct conversion
1430 if (finalstate==reconstructed) { // this MUST cover all cases
1432 else if (current_state==nonstandard) reconstruct(fence);
1435 remove_internal_coefficients(fence);
1436 set_tree_state(reconstructed);
1437 }
1438 else if (current_state==redundant) {
1439 remove_internal_coefficients(fence);
1440 set_tree_state(reconstructed);
1443 sum_down(fence);
1444 set_tree_state(reconstructed);
1446 else if (current_state==redundant_after_merge) sum_down(fence);
1447 else MADNESS_EXCEPTION("unknown/unsupported current tree state",1);
1448 set_tree_state(reconstructed);
1449 } else if (finalstate==compressed) { // cases that are not covered will be done in two steps
1451 if (current_state==nonstandard) standard(fence);
1453 } else if (finalstate==nonstandard) { // cases that are not covered will be done in two steps
1456 remove_leaf_coefficients(fence);
1457 set_tree_state(nonstandard);
1458 }
1459 } else if (finalstate==nonstandard_with_leaves) { // cases that are not covered will be done in two steps
1461 } else if (finalstate==redundant) { // cases that are not covered will be done in two steps
1463 } else {
1464 MADNESS_EXCEPTION("unknown/unsupported final tree state",1);
1465 }
1466 if (fence && VERIFY_TREE) verify_tree(); // Must be after in case nonstandard
1467
1468 // direct conversion worked, we're good
1469 if (finalstate==get_tree_state()) return;
1471
1472 // go through reconstructed state -- requires fence! The uncovered transitions need the
1473 // reconstructed coefficients before the second pass, so the fence is unavoidable; report a
1474 // broken no-fence promise once, from one rank, rather than per rank per call.
1475 if (not fence and FunctionDefaults<NDIM>::get_debug() and world.rank() == 0) {
1476 static std::atomic<bool> reported(false);
1477 if (not reported.exchange(true))
1478 print("change_tree_state:", current_state, "->", finalstate,
1479 "must reconstruct first and therefore fences, despite fence=false");
1480 }
1483
1484 }
1485
1487
1488 template <typename T, std::size_t NDIM>
1490
1491 if (is_reconstructed()) return;
1492
1493 if (is_redundant() or is_nonstandard_with_leaves()) {
1494 set_tree_state(reconstructed);
1495 this->remove_internal_coefficients(fence);
1496 } else if (is_compressed() or tree_state==nonstandard_after_apply) {
1497 // Must set true here so that successive calls without fence do the right thing
1498 set_tree_state(reconstructed);
1499 if (world.rank() == coeffs.owner(cdata.key0))
1500 woT::task(world.rank(), &implT::reconstruct_op, cdata.key0,coeffT(), true);
1501 } else if (is_nonstandard()) {
1502 // Must set true here so that successive calls without fence do the right thing
1503 set_tree_state(reconstructed);
1504 if (world.rank() == coeffs.owner(cdata.key0))
1505 woT::task(world.rank(), &implT::reconstruct_op, cdata.key0,coeffT(), false);
1506 } else {
1507 MADNESS_EXCEPTION("cannot reconstruct this tree",1);
1508 }
1509 if (fence) world.gop.fence();
1511 }
1512
1513 /// compress the wave function
1514
1515 /// after application there will be sum coefficients at the root level,
1516 /// and difference coefficients at all other levels; furthermore:
1517 /// @param[in] nonstandard keep sum coeffs at all other levels, except leaves
1518 /// @param[in] keepleaves keep sum coeffs (but no diff coeffs) at leaves
1519 /// @param[in] redundant keep only sum coeffs at all levels, discard difference coeffs
1520 template <typename T, std::size_t NDIM>
1522 MADNESS_CHECK_THROW(is_reconstructed(),"impl::compress wants a reconstructe tree");
1523 // Must set true here so that successive calls without fence do the right thing
1524 set_tree_state(newstate);
1525 bool keepleaves1=(tree_state==nonstandard_with_leaves) or (tree_state==redundant);
1526 bool nonstandard1=(tree_state==nonstandard) or (tree_state==nonstandard_with_leaves);
1527 bool redundant1=(tree_state==redundant);
1528
1529 if (world.rank() == coeffs.owner(cdata.key0)) {
1530
1531 compress_spawn(cdata.key0, nonstandard1, keepleaves1, redundant1);
1532 }
1533 if (fence)
1534 world.gop.fence();
1535 }
1536
1537 template <typename T, std::size_t NDIM>
1539 flo_unary_op_node_inplace(remove_internal_coeffs(),fence);
1540 }
1541
1542 template <typename T, std::size_t NDIM>
1544 flo_unary_op_node_inplace(remove_leaf_coeffs(),fence);
1546
1547 /// convert this to redundant, i.e. have sum coefficients on all levels
1548 template <typename T, std::size_t NDIM>
1551 // fast return if possible
1552 if (is_redundant()) return;
1553 MADNESS_CHECK_THROW(is_reconstructed(),"impl::make_redundant() wants a reconstructed tree");
1554 compress(redundant,fence);
1555 }
1556
1557 /// convert this from redundant to standard reconstructed form
1558 template <typename T, std::size_t NDIM>
1560 MADNESS_CHECK_THROW(is_redundant(),"impl::undo_redundant() wants a redundant tree");
1561 set_tree_state(reconstructed);
1562 flo_unary_op_node_inplace(remove_internal_coeffs(),fence);
1563 }
1564
1565
1566 /// compute for each FunctionNode the norm of the function inside that node
1567 template <typename T, std::size_t NDIM>
1569 if (world.rank() == coeffs.owner(cdata.key0))
1570 norm_tree_spawn(cdata.key0);
1571 if (fence)
1572 world.gop.fence();
1573 }
1574
1575 template <typename T, std::size_t NDIM>
1576 double FunctionImpl<T,NDIM>::norm_tree_op(const keyT& key, const std::vector< Future<double> >& v) {
1577 //PROFILE_MEMBER_FUNC(FunctionImpl);
1578 double sum = 0.0;
1579 int i=0;
1580 for (KeyChildIterator<NDIM> kit(key); kit; ++kit,++i) {
1581 double value = v[i].get();
1582 sum += value*value;
1583 }
1584 sum = sqrt(sum);
1585 coeffs.task(key, &nodeT::set_norm_tree, sum); // why a task? because send is deprecated to keep comm thread free
1586 //if (key.level() == 0) std::cout << "NORM_TREE_TOP " << sum << "\n";
1587 return sum;
1588 }
1589
1590 template <typename T, std::size_t NDIM>
1592 nodeT& node = coeffs.find(key).get()->second;
1593 if (node.has_children()) {
1594 std::vector< Future<double> > v = future_vector_factory<double>(1<<NDIM);
1595 int i=0;
1596 for (KeyChildIterator<NDIM> kit(key); kit; ++kit,++i) {
1597 v[i] = woT::task(coeffs.owner(kit.key()), &implT::norm_tree_spawn, kit.key());
1598 }
1599 return woT::task(world.rank(),&implT::norm_tree_op, key, v);
1601 else {
1602 // return Future<double>(node.coeff().normf());
1603 const double norm=node.coeff().normf();
1604 // invoked locally anyways
1605 node.set_norm_tree(norm);
1607 }
1608 }
1610 /// truncate using a tree in reconstructed form
1611
1612 /// must be invoked where key is local
1613 template <typename T, std::size_t NDIM>
1615 MADNESS_ASSERT(coeffs.probe(key));
1616 nodeT& node = coeffs.find(key).get()->second;
1617
1618 // if this is a leaf node just return the sum coefficients
1619 if (not node.has_children()) return Future<coeffT>(node.coeff());
1620
1621 // if this is an internal node, wait for all the children's sum coefficients
1622 // and use them to determine if the children can be removed
1623 std::vector<Future<coeffT> > v = future_vector_factory<coeffT>(1<<NDIM);
1624 int i=0;
1625 for (KeyChildIterator<NDIM> kit(key); kit; ++kit,++i) {
1626 v[i] = woT::task(coeffs.owner(kit.key()), &implT::truncate_reconstructed_spawn, kit.key(),tol,TaskAttributes::hipri());
1627 }
1628
1629 // will return (possibly empty) sum coefficients
1630 return woT::task(world.rank(),&implT::truncate_reconstructed_op,key,v,tol,TaskAttributes::hipri());
1631
1632 }
1633
1634 /// given the sum coefficients of all children, truncate or not
1635
1636 /// @return new sum coefficients (empty if internal, not empty, if new leaf); might delete its children
1637 template <typename T, std::size_t NDIM>
1638 typename FunctionImpl<T,NDIM>::coeffT FunctionImpl<T,NDIM>::truncate_reconstructed_op(const keyT& key, const std::vector< Future<coeffT > >& v, const double tol) {
1639
1640 MADNESS_ASSERT(coeffs.probe(key));
1641
1642 // the sum coefficients might be empty, which means they come from an internal node
1643 // and we must not truncate; so just return empty coeffs again
1644 for (size_t i=0; i<v.size(); ++i) if (v[i].get().has_no_data()) return coeffT();
1645
1646 // do not truncate below level 1
1647 if (key.level()<2) return coeffT();
1648
1649 // compute the wavelet coefficients from the child nodes
1650 typename dcT::accessor acc;
1651 const auto found = coeffs.find(acc, key);
1653 int i=0;
1654 tensorT d(cdata.v2k);
1655 for (KeyChildIterator<NDIM> kit(key); kit; ++kit,++i) {
1656 // d(child_patch(kit.key())) += v[i].get();
1657 d(child_patch(kit.key())) += v[i].get().full_tensor();
1658 }
1659
1660 d = filter(d);
1661 tensorT s=copy(d(cdata.s0));
1662 d(cdata.s0) = 0.0;
1663 const double error=d.normf();
1664
1665 nodeT& node = coeffs.find(key).get()->second;
1666
1667 if (error < truncate_tol(tol,key)) {
1668 node.set_has_children(false);
1669 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
1670 coeffs.erase(kit.key());
1671 }
1672 // "replace" children with new sum coefficients
1673 coeffT ss=coeffT(s,targs);
1674 acc->second.set_coeff(ss);
1675 return ss;
1676 } else {
1677 return coeffT();
1678 }
1679 }
1680
1681 /// calculate the wavelet coefficients using the sum coefficients of all child nodes
1682
1683 /// @param[in] key this's key
1684 /// @param[in] v sum coefficients and propagated norms of the child nodes
1685 /// @param[in] nonstandard keep the sum coefficients with the wavelet coefficients
1686 /// @return the sum coefficients and propagated norms
1687 template <typename T, std::size_t NDIM>
1689 const std::vector< Future<compressT> >& v, bool nonstandard1) {
1690 //PROFILE_MEMBER_FUNC(FunctionImpl);
1691
1692 double cpu0=cpu_time();
1693 // Copy child scaling coeffs into contiguous block
1694 tensorT d(cdata.v2k);
1695 // coeffT d(cdata.v2k,targs);
1696 int i=0;
1697 double norm_tree2=0.0, dnorm_tree2=0.0;
1698 for (KeyChildIterator<NDIM> kit(key); kit; ++kit,++i) {
1699 // d(child_patch(kit.key())) += v[i].get();
1700 d(child_patch(kit.key())) += v[i].get().first.full_tensor();
1701 norm_tree2+=v[i].get().second.first*v[i].get().second.first;
1702 dnorm_tree2+=v[i].get().second.second*v[i].get().second.second;
1703 }
1704
1705 d = filter(d);
1706 double cpu1=cpu_time();
1707 timer_filter.accumulate(cpu1-cpu0);
1708 cpu0=cpu1;
1709
1710 typename dcT::accessor acc;
1711 const auto found = coeffs.find(acc, key);
1713 MADNESS_CHECK_THROW(!acc->second.has_coeff(),"compress_op: existing coeffs where there should be none");
1714
1715 // tighter thresh for internal nodes
1716 TensorArgs targs2=targs;
1717 targs2.thresh*=0.1;
1718
1719 // need the deep copy for contiguity
1720 coeffT ss=coeffT(copy(d(cdata.s0)));
1721 double snorm=ss.normf();
1722
1723 if (key.level()> 0 && !nonstandard1) d(cdata.s0) = 0.0;
1724
1726 double dnorm=dd.normf();
1727 double norm_tree=sqrt(norm_tree2);
1728 // dnorm_tree accumulates this node's d coefficients and all those below it
1729 double dnorm_tree=sqrt(dnorm_tree2+dnorm*dnorm);
1730
1731 acc->second.set_snorm(snorm);
1732 acc->second.set_dnorm(dnorm);
1733 acc->second.set_norm_tree(norm_tree);
1734 acc->second.set_dnorm_tree(dnorm_tree);
1735
1736 acc->second.set_coeff(dd);
1737 cpu1=cpu_time();
1738 timer_compress_svd.accumulate(cpu1-cpu0);
1739
1740 // return sum coefficients and propagated norms
1741 return std::make_pair(ss,std::make_pair(norm_tree,dnorm_tree));
1742 }
1743
1744 /// similar to compress_op, but insert only the sum coefficients in the tree
1745
1746 /// also sets snorm, dnorm, norm_tree and dnorm_tree for all nodes
1747 /// @param[in] key this's key
1748 /// @param[in] v sum coefficients and propagated norms of the child nodes
1749 /// @return the sum coefficients and propagated norms
1750 template <typename T, std::size_t NDIM>
1753
1754 tensorT d(cdata.v2k);
1755 int i=0;
1756 double norm_tree2=0.0, dnorm_tree2=0.0;
1757 for (KeyChildIterator<NDIM> kit(key); kit; ++kit,++i) {
1758 d(child_patch(kit.key())) += v[i].get().first.full_tensor();
1759 norm_tree2+=v[i].get().second.first*v[i].get().second.first;
1760 dnorm_tree2+=v[i].get().second.second*v[i].get().second.second;
1761 }
1762 d = filter(d);
1763 double norm_tree=sqrt(norm_tree2);
1764
1765 // tighter thresh for internal nodes
1766 TensorArgs targs2=targs;
1767 targs2.thresh*=0.1;
1768
1769 // need the deep copy for contiguity
1770 coeffT s=coeffT(copy(d(cdata.s0)),targs2);
1771 d(cdata.s0)=0.0;
1772 double dnorm=d.normf();
1773 double snorm=s.normf();
1774
1775 typename dcT::accessor acc;
1776 const auto found = coeffs.find(acc, key);
1778
1779 // dnorm_tree accumulates this node's d coefficients and all those below it
1780 double dnorm_tree=sqrt(dnorm_tree2+dnorm*dnorm);
1781
1782 acc->second.set_coeff(s);
1783 acc->second.set_dnorm(dnorm);
1784 acc->second.set_snorm(snorm);
1785 acc->second.set_norm_tree(norm_tree);
1786 acc->second.set_dnorm_tree(dnorm_tree);
1787
1788 // return sum coefficients and propagated norms
1789 return std::make_pair(s,std::make_pair(norm_tree,dnorm_tree));
1790 }
1791
1792 /// Changes non-standard compressed form to standard compressed form
1793 template <typename T, std::size_t NDIM>
1795
1796 if (is_compressed()) return;
1797 set_tree_state(compressed);
1798 flo_unary_op_node_inplace(do_standard(this),fence);
1799// make_nonstandard = false;
1800 }
1801
1802
1803 /// after apply we need to do some cleanup;
1804
1805 /// forces fence
1806 template <typename T, std::size_t NDIM>
1808 bool print_timings=false;
1809 bool printme=(world.rank()==0 and print_timings);
1810 TensorArgs tight_args(targs);
1811 tight_args.thresh*=0.01;
1812 double begin=wall_time();
1813 double begin1=wall_time();
1814 flo_unary_op_node_inplace(do_consolidate_buffer(tight_args),true);
1815 double end1=wall_time();
1816 if (printme) printf("time in consolidate_buffer %8.4f\n",end1-begin1);
1817
1818
1819 // reduce the rank of the final nodes, leave full tensors unchanged
1820 // flo_unary_op_node_inplace(do_reduce_rank(tight_args.thresh),true);
1821 begin1=wall_time();
1822 flo_unary_op_node_inplace(do_reduce_rank(targs),true);
1823 end1=wall_time();
1824 if (printme) printf("time in do_reduce_rank %8.4f\n",end1-begin1);
1825
1826 // change TT_FULL to low rank
1827 begin1=wall_time();
1828 flo_unary_op_node_inplace(do_change_tensor_type(targs,*this),true);
1829 end1=wall_time();
1830 if (printme) printf("time in do_change_tensor_type %8.4f\n",end1-begin1);
1831
1832 // truncate leaf nodes to avoid excessive tree refinement
1833 begin1=wall_time();
1834 flo_unary_op_node_inplace(do_truncate_NS_leafs(this),true);
1835 end1=wall_time();
1836 if (printme) printf("time in do_truncate_NS_leafs %8.4f\n",end1-begin1);
1837
1838 double end=wall_time();
1839 double elapsed=end-begin;
1840 set_tree_state(nonstandard_after_apply);
1841 world.gop.fence();
1842 return elapsed;
1843 }
1845
1846 /// after summing up we need to do some cleanup;
1847
1848 /// forces fence
1849 template <typename T, std::size_t NDIM>
1851 world.gop.fence();
1852 flo_unary_op_node_inplace(do_consolidate_buffer(get_tensor_args()), true);
1853 sum_down(true);
1854 set_tree_state(reconstructed);
1855 }
1856
1857 /// Returns the square of the local norm ... no comms
1858 template <typename T, std::size_t NDIM>
1862 return world.taskq.reduce<double,rangeT,do_norm2sq_local>(rangeT(coeffs.begin(),coeffs.end()),
1864 }
1866
1867
1868
1869 /// Returns the maximum local depth of the tree ... no communications.
1870 template <typename T, std::size_t NDIM>
1872 std::size_t maxdepth = 0;
1873 typename dcT::const_iterator end = coeffs.end();
1874 for (typename dcT::const_iterator it=coeffs.begin(); it!=end; ++it) {
1875 std::size_t N = (std::size_t) it->first.level();
1877 maxdepth = N;
1878 }
1879 return maxdepth;
1880 }
1881
1882
1883 /// Returns the maximum depth of the tree ... collective ... global sum/broadcast
1884 template <typename T, std::size_t NDIM>
1886 std::size_t maxdepth = max_local_depth();
1887 world.gop.max(maxdepth);
1888 return maxdepth;
1889 }
1890
1891 /// Returns the max number of nodes on a processor
1892 template <typename T, std::size_t NDIM>
1894 std::size_t maxsize = 0;
1895 maxsize = coeffs.size();
1896 world.gop.max(maxsize);
1897 return maxsize;
1898 }
1899
1900 /// Returns the min number of nodes on a processor
1901 template <typename T, std::size_t NDIM>
1903 std::size_t minsize = 0;
1904 minsize = coeffs.size();
1905 world.gop.min(minsize);
1906 return minsize;
1907 }
1908
1909 /// Returns the size of the tree structure of the function ... collective global sum
1910 template <typename T, std::size_t NDIM>
1912 std::size_t sum = 0;
1913 sum = coeffs.size();
1914 world.gop.sum(sum);
1915 return sum;
1916 }
1917
1918 /// Returns the number of coefficients in the function for each rank
1919 template <typename T, std::size_t NDIM>
1921 std::size_t sum = 0;
1922 for (const auto& [key,node] : coeffs) {
1923 if (node.has_coeff()) sum+=node.size();
1924 }
1925 return sum;
1926 }
1927
1928 /// Returns the number of coefficients in the function ... collective global sum
1929 template <typename T, std::size_t NDIM>
1930 std::size_t FunctionImpl<T,NDIM>::size() const {
1931 std::size_t sum = size_local();
1932 world.gop.sum(sum);
1933 return sum;
1934 }
1935
1936 /// Returns the number of coefficients in the function ... collective global sum
1937 template <typename T, std::size_t NDIM>
1939 std::size_t sum = coeffs.size() * (sizeof(keyT) + sizeof(nodeT));
1940 typename dcT::const_iterator end = coeffs.end();
1941 for (typename dcT::const_iterator it=coeffs.begin(); it!=end; ++it) {
1942 const nodeT& node = it->second;
1943 if (node.has_coeff()) sum+=node.coeff().real_size();
1944 }
1945 world.gop.sum(sum);
1946 return sum;
1947 }
1948
1949 /// Returns the number of coefficients in the function on this MPI rank
1950 template <typename T, std::size_t NDIM>
1952 std::size_t sum =0;
1953 for (auto& [key,node] : coeffs) {
1954 if (node.has_coeff()) sum+=node.coeff().nCoeff();
1955 }
1956 return sum;
1957 }
1958
1959 /// Returns the number of coefficients in the function ... collective global sum
1960 template <typename T, std::size_t NDIM>
1961 std::size_t FunctionImpl<T,NDIM>::nCoeff() const {
1962 std::size_t sum = nCoeff_local();
1963 world.gop.sum(sum);
1964 return sum;
1965 }
1966
1967
1968 /// print tree size and size
1969 template <typename T, std::size_t NDIM>
1970 void FunctionImpl<T,NDIM>::print_size(const std::string name) const {
1971 const size_t tsize=this->tree_size();
1972// const size_t size=this->size();
1973 const size_t ncoeff=this->nCoeff();
1974 const double wall=wall_time();
1975 const double d=sizeof(T);
1976 const double fac=1024*1024*1024;
1977
1978 double norm=0.0;
1979 {
1980 double local = norm2sq_local();
1981 this->world.gop.sum(local);
1982 this->world.gop.fence();
1983 norm=sqrt(local);
1984 }
1985
1986 if (this->world.rank()==0) {
1987
1988 constexpr std::size_t bufsize=128;
1989 char buf[bufsize];
1990 snprintf(buf, bufsize, "%40s at time %.1fs: norm/tree/#coeff/size: %7.5f %zu, %6.3f m, %6.3f GByte",
1991 (name.c_str()), wall, norm, tsize,double(ncoeff)*1.e-6,double(ncoeff)/fac*d);
1992 print(std::string(buf));
1993 }
1994 }
1995
1996 /// print the number of configurations per node
1997 template <typename T, std::size_t NDIM>
1999 if (this->targs.tt==TT_FULL) return;
2000 int dim=NDIM/2;
2001 int k0=k;
2002 if (is_compressed()) k0=2*k;
2003 Tensor<long> n(int(std::pow(double(k0),double(dim))+1));
2004 long n_full=0;
2005 long n_large=0;
2006
2007 if (world.rank()==0) print("n.size(),k0,dim",n.size(),k0,dim);
2008 typename dcT::const_iterator end = coeffs.end();
2009 for (typename dcT::const_iterator it=coeffs.begin(); it!=end; ++it) {
2010 const nodeT& node = it->second;
2011 if (node.has_coeff()) {
2012 if (node.coeff().rank()>long(n.size())) {
2013 ++n_large;
2014 } else if (node.coeff().rank()==-1) {
2015 ++n_full;
2016 } else if (node.coeff().rank()<0) {
2017 print("small rank",node.coeff().rank());
2018 } else {
2019 n[node.coeff().rank()]++;
2020 }
2021 }
2022 }
2023
2024 world.gop.sum(n.ptr(), n.size());
2025
2026 if (world.rank()==0) {
2027 print("configurations number of nodes");
2028 print(" full rank ",n_full);
2029 for (unsigned int i=0; i<n.size(); i++) {
2030 print(" ",i," ",n[i]);
2031 }
2032 print(" large rank ",n_large);
2033
2034 // repeat for logarithmic scale: <3, <10, <30, <100, ..
2035 Tensor<long> nlog(6);
2036 nlog=0;
2037 for (unsigned int i=0; i<std::min(3l,n.size()); i++) nlog[0]+=n[i];
2038 for (unsigned int i=3; i<std::min(10l,n.size()); i++) nlog[1]+=n[i];
2039 for (unsigned int i=10; i<std::min(30l,n.size()); i++) nlog[2]+=n[i];
2040 for (unsigned int i=30; i<std::min(100l,n.size()); i++) nlog[3]+=n[i];
2041 for (unsigned int i=100; i<std::min(300l,n.size()); i++) nlog[4]+=n[i];
2042 for (unsigned int i=300; i<std::min(1000l,n.size()); i++) nlog[5]+=n[i];
2043
2044 std::vector<std::string> slog={"3","10","30","100","300","1000"};
2045 for (unsigned int i=0; i<nlog.size(); i++) {
2046 print(" < ",slog[i]," ",nlog[i]);
2047 }
2048 print(" large rank ",n_large);
2049
2050 }
2051 }
2052
2053 template <typename T, std::size_t NDIM>
2056 const int k = cdata.k;
2057 T sum = T(0.0);
2058
2059 // v = sum_{p,q,...} c[p,q,...] phi_p(x0) phi_q(x1) ... is a separable
2060 // contraction; the fastest evaluation depends on the dimension.
2061 //
2062 // NDIM<=2 (deep 1-D radial trees are the hottest eval workload): a
2063 // factored register-resident loop. Partial sums stay in registers, the
2064 // only memory traffic is one streaming read of c, and no per-thread
2065 // scratch is needed (px is <= NDIM*MAXK*8 bytes of stack). Routing the
2066 // tiny (k x 1) contraction through general_fast_transform's dispatch
2067 // and ping-pong scratch measured ~20% slower at NDIM=1.
2068 //
2069 // NDIM>=3: general_fast_transform's staged contraction vectorizes
2070 // (the factored loop's inner reduction cannot under strict FP) and
2071 // measured 4-5x faster at NDIM=6; the phi matrices and scratch are
2072 // thread_local, so this path stays allocation-free after warm-up.
2073 if constexpr (NDIM <= 2) {
2074 MADNESS_ASSERT(k <= MAXK);
2075 double px[NDIM][MAXK];
2076 for (std::size_t i=0; i<NDIM; ++i) legendre_scaling_functions(x[i],k,px[i]);
2077
2078 if constexpr (NDIM == 1) {
2079 const T* cp = c.ptr();
2080 for (int p=0; p<k; ++p) sum += cp[p]*px[0][p];
2081 }
2082 else {
2083 for (int p=0; p<k; ++p) {
2084 const double a = px[0][p];
2085 const T* cq = &c(p,0);
2086 T s2 = T(0);
2087 for (int q=0; q<k; ++q) s2 += cq[q]*px[1][q];
2088 sum += a*s2;
2089 }
2090 }
2091 }
2092 else {
2093 thread_local Tensor<double> phi[NDIM];
2094 thread_local int phi_k = -1;
2095 if (phi_k != k) {
2096 for (std::size_t i=0; i<NDIM; ++i) phi[i] = Tensor<double>(long(k), 1L);
2097 phi_k = k;
2098 }
2099 for (std::size_t i=0; i<NDIM; ++i)
2100 legendre_scaling_functions(x[i], k, phi[i].ptr());
2101
2102 typedef TENSOR_RESULT_TYPE(T,double) evalR;
2103 // ws/res are references bound to the thread_local scratch tensors.
2104 auto [ws, res] = madness::detail::eval_scratch<evalR>(c.size());
2106 sum = res.ptr()[0];
2107 }
2108 // exp2 replaces pow for the level scaling; 1/sqrt(cell_volume) is cached
2109 // per thread and refreshed only if the cell changes (perf-doc change #2).
2110 thread_local double cached_cell_volume = -1.0;
2111 thread_local double cached_inv_sqrt_cell_vol = 0.0;
2115 cached_inv_sqrt_cell_vol = 1.0/std::sqrt(cell_volume);
2116 }
2117 return sum * std::exp2(0.5*NDIM*n) * cached_inv_sqrt_cell_vol;
2118 }
2119
2120 template <typename T, std::size_t NDIM>
2121 void FunctionImpl<T,NDIM>::reconstruct_op(const keyT& key, const coeffT& s, const bool accumulate_NS) {
2122 //PROFILE_MEMBER_FUNC(FunctionImpl);
2123 // Note that after application of an integral operator not all
2124 // siblings may be present so it is necessary to check existence
2125 // and if absent insert an empty leaf node.
2126 //
2127 // If summing the result of an integral operator (i.e., from
2128 // non-standard form) there will be significant scaling function
2129 // coefficients at all levels and possibly difference coefficients
2130 // in leaves, hence the tree may refine as a result.
2131 typename dcT::iterator it = coeffs.find(key).get();
2132 if (it == coeffs.end()) {
2133 coeffs.replace(key,nodeT(coeffT(),false));
2134 it = coeffs.find(key).get();
2135 }
2136 nodeT& node = it->second;
2137
2138 // The integral operator will correctly connect interior nodes
2139 // to children but may leave interior nodes without coefficients
2140 // ... but they still need to sum down so just give them zeros
2141 if (node.has_children() && !node.has_coeff()) {
2142 node.set_coeff(coeffT(cdata.v2k,targs));
2143 }
2144
2145 if (node.has_children() || node.has_coeff()) { // Must allow for inconsistent state from transform, etc.
2146 coeffT d = node.coeff();
2147 if (!d.has_data()) d = coeffT(cdata.v2k,targs);
2148 if (accumulate_NS and (key.level() > 0)) d(cdata.s0) += s; // -- note accumulate for NS summation
2149 if (d.dim(0)==2*get_k()) { // d might be pre-truncated if it's a leaf
2150 d = unfilter(d);
2151 node.clear_coeff();
2152 node.set_has_children(true);
2153 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
2154 const keyT& child = kit.key();
2155 coeffT ss = copy(d(child_patch(child)));
2156 ss.reduce_rank(thresh);
2157 //PROFILE_BLOCK(recon_send); // Too fine grain for routine profiling
2158 woT::task(coeffs.owner(child), &implT::reconstruct_op, child, ss, accumulate_NS);
2159 }
2160 } else {
2161 MADNESS_ASSERT(node.is_leaf());
2162 // node.coeff()+=s;
2163 node.coeff().reduce_rank(targs.thresh);
2164 }
2165 }
2166 else {
2167 coeffT ss=s;
2168 if (s.has_no_data()) ss=coeffT(cdata.vk,targs);
2169 if (key.level()) node.set_coeff(copy(ss));
2170 else node.set_coeff(ss);
2171 }
2172 }
2173
2174 template <typename T, std::size_t NDIM>
2175 Tensor<T> fcube(const Key<NDIM>& key, T (*f)(const Vector<double,NDIM>&), const Tensor<double>& qx) {
2176 // fcube(key,typename FunctionFactory<T,NDIM>::FunctorInterfaceWrapper(f) , qx, fval);
2177 std::vector<long> npt(NDIM,qx.dim(0));
2178 Tensor<T> fval(npt);
2180 return fval;
2181 }
2182
2183 template <typename T, std::size_t NDIM>
2185 // fcube(key,typename FunctionFactory<T,NDIM>::FunctorInterfaceWrapper(f) , qx, fval);
2186 std::vector<long> npt(NDIM,qx.dim(0));
2187 Tensor<T> fval(npt);
2188 fcube(key, f, qx, fval);
2189 return fval;
2190 }
2191
2192 template <typename T, std::size_t NDIM>
2193 // void FunctionImpl<T,NDIM>::fcube(const keyT& key, const FunctionFunctorInterface<T,NDIM>& f, const Tensor<double>& qx, tensorT& fval) const {
2195 //~ template <typename T, std::size_t NDIM> template< typename FF>
2196 //~ void FunctionImpl<T,NDIM>::fcube(const keyT& key, const FF& f, const Tensor<double>& qx, tensorT& fval) const {
2198 //PROFILE_MEMBER_FUNC(FunctionImpl);
2199 const Vector<Translation,NDIM>& l = key.translation();
2200 const Level n = key.level();
2201 const double h = std::pow(0.5,double(n));
2202 coordT c; // will hold the point in user coordinates
2203 const int npt = qx.dim(0);
2204
2207
2208 // Do pre-screening of the FunctionFunctorInterface, f, before calculating f(r) at quadrature points
2209 coordT c1, c2;
2210 for (std::size_t i = 0; i < NDIM; i++) {
2211 c1[i] = cell(i,0) + h*cell_width[i]*(l[i] + qx((long)0));
2212 c2[i] = cell(i,0) + h*cell_width[i]*(l[i] + qx(npt-1));
2213 }
2214 if (f.screened(c1, c2)) {
2215 fval(___) = 0.0;
2216 return;
2217 }
2218
2220 bool vectorized = f.supports_vectorized();
2221 if (vectorized) {
2222 T* fvptr = fval.ptr();
2223 if (NDIM == 1) {
2224 double* x1 = new double[npt];
2225 int idx = 0;
2226 for (int i=0; i<npt; ++i, ++idx) {
2227 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2228 x1[idx] = c[0];
2229 }
2231 f(xvals, fvptr, npt);
2232 delete [] x1;
2233 }
2234 else if (NDIM == 2) {
2235 double* x1 = new double[npt*npt];
2236 double* x2 = new double[npt*npt];
2237 int idx = 0;
2238 for (int i=0; i<npt; ++i) {
2239 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2240 for (int j=0; j<npt; ++j, ++idx) {
2241 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
2242 x1[idx] = c[0];
2243 x2[idx] = c[1];
2244 }
2245 }
2247 f(xvals, fvptr, npt*npt);
2248 delete [] x1;
2249 delete [] x2;
2250 }
2251 else if (NDIM == 3) {
2252 double* x1 = new double[npt*npt*npt];
2253 double* x2 = new double[npt*npt*npt];
2254 double* x3 = new double[npt*npt*npt];
2255 int idx = 0;
2256 for (int i=0; i<npt; ++i) {
2257 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2258 for (int j=0; j<npt; ++j) {
2259 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
2260 for (int k=0; k<npt; ++k, ++idx) {
2261 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
2262 x1[idx] = c[0];
2263 x2[idx] = c[1];
2264 x3[idx] = c[2];
2265 }
2266 }
2267 }
2269 f(xvals, fvptr, npt*npt*npt);
2270 delete [] x1;
2271 delete [] x2;
2272 delete [] x3;
2273 }
2274 else if (NDIM == 4) {
2275 double* x1 = new double[npt*npt*npt*npt];
2276 double* x2 = new double[npt*npt*npt*npt];
2277 double* x3 = new double[npt*npt*npt*npt];
2278 double* x4 = new double[npt*npt*npt*npt];
2279 int idx = 0;
2280 for (int i=0; i<npt; ++i) {
2281 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2282 for (int j=0; j<npt; ++j) {
2283 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
2284 for (int k=0; k<npt; ++k) {
2285 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
2286 for (int m=0; m<npt; ++m, ++idx) {
2287 c[3] = cell(3,0) + h*cell_width[3]*(l[3] + qx(m)); // xx
2288 x1[idx] = c[0];
2289 x2[idx] = c[1];
2290 x3[idx] = c[2];
2291 x4[idx] = c[3];
2292 }
2293 }
2294 }
2295 }
2297 f(xvals, fvptr, npt*npt*npt*npt);
2298 delete [] x1;
2299 delete [] x2;
2300 delete [] x3;
2301 delete [] x4;
2302 }
2303 else if (NDIM == 5) {
2304 double* x1 = new double[npt*npt*npt*npt*npt];
2305 double* x2 = new double[npt*npt*npt*npt*npt];
2306 double* x3 = new double[npt*npt*npt*npt*npt];
2307 double* x4 = new double[npt*npt*npt*npt*npt];
2308 double* x5 = new double[npt*npt*npt*npt*npt];
2309 int idx = 0;
2310 for (int i=0; i<npt; ++i) {
2311 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2312 for (int j=0; j<npt; ++j) {
2313 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
2314 for (int k=0; k<npt; ++k) {
2315 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
2316 for (int m=0; m<npt; ++m) {
2317 c[3] = cell(3,0) + h*cell_width[3]*(l[3] + qx(m)); // xx
2318 for (int n=0; n<npt; ++n, ++idx) {
2319 c[4] = cell(4,0) + h*cell_width[4]*(l[4] + qx(n)); // yy
2320 x1[idx] = c[0];
2321 x2[idx] = c[1];
2322 x3[idx] = c[2];
2323 x4[idx] = c[3];
2324 x5[idx] = c[4];
2325 }
2326 }
2327 }
2328 }
2329 }
2331 f(xvals, fvptr, npt*npt*npt*npt*npt);
2332 delete [] x1;
2333 delete [] x2;
2334 delete [] x3;
2335 delete [] x4;
2336 delete [] x5;
2337 }
2338 else if (NDIM == 6) {
2339 double* x1 = new double[npt*npt*npt*npt*npt*npt];
2340 double* x2 = new double[npt*npt*npt*npt*npt*npt];
2341 double* x3 = new double[npt*npt*npt*npt*npt*npt];
2342 double* x4 = new double[npt*npt*npt*npt*npt*npt];
2343 double* x5 = new double[npt*npt*npt*npt*npt*npt];
2344 double* x6 = new double[npt*npt*npt*npt*npt*npt];
2345 int idx = 0;
2346 for (int i=0; i<npt; ++i) {
2347 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2348 for (int j=0; j<npt; ++j) {
2349 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
2350 for (int k=0; k<npt; ++k) {
2351 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
2352 for (int m=0; m<npt; ++m) {
2353 c[3] = cell(3,0) + h*cell_width[3]*(l[3] + qx(m)); // xx
2354 for (int n=0; n<npt; ++n) {
2355 c[4] = cell(4,0) + h*cell_width[4]*(l[4] + qx(n)); // yy
2356 for (int p=0; p<npt; ++p, ++idx) {
2357 c[5] = cell(5,0) + h*cell_width[5]*(l[5] + qx(p)); // zz
2358 x1[idx] = c[0];
2359 x2[idx] = c[1];
2360 x3[idx] = c[2];
2361 x4[idx] = c[3];
2362 x5[idx] = c[4];
2363 x6[idx] = c[5];
2364 }
2365 }
2366 }
2367 }
2368 }
2369 }
2371 f(xvals, fvptr, npt*npt*npt*npt*npt*npt);
2372 delete [] x1;
2373 delete [] x2;
2374 delete [] x3;
2375 delete [] x4;
2376 delete [] x5;
2377 delete [] x6;
2378 }
2379 else {
2380 MADNESS_EXCEPTION("FunctionImpl: fcube: confused about NDIM?",NDIM);
2381 }
2382 }
2383 else {
2385 MADNESS_PRAGMA_CLANG(diagnostic ignored "-Wtautological-constant-compare")
2386 auto isnan = [](T v) { return std::isnan(v); };
2388 if (NDIM == 1) {
2389 for (int i=0; i<npt; ++i) {
2390 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2391 fval(i) = f(c);
2393 }
2394 }
2395 else if (NDIM == 2) {
2396 for (int i=0; i<npt; ++i) {
2397 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2398 for (int j=0; j<npt; ++j) {
2399 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
2400 fval(i,j) = f(c);
2401 MADNESS_ASSERT(!isnan(fval(i,j)));
2402 }
2403 }
2404 }
2405 else if (NDIM == 3) {
2406 for (int i=0; i<npt; ++i) {
2407 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2408 for (int j=0; j<npt; ++j) {
2409 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
2410 for (int k=0; k<npt; ++k) {
2411 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
2412 fval(i,j,k) = f(c);
2413 MADNESS_ASSERT(!isnan(fval(i,j,k)));
2414 }
2415 }
2416 }
2417 }
2418 else if (NDIM == 4) {
2419 for (int i=0; i<npt; ++i) {
2420 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2421 for (int j=0; j<npt; ++j) {
2422 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
2423 for (int k=0; k<npt; ++k) {
2424 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
2425 for (int m=0; m<npt; ++m) {
2426 c[3] = cell(3,0) + h*cell_width[3]*(l[3] + qx(m)); // xx
2427 fval(i,j,k,m) = f(c);
2428 MADNESS_ASSERT(!isnan(fval(i,j,k,m)));
2429 }
2430 }
2431 }
2432 }
2433 }
2434 else if (NDIM == 5) {
2435 for (int i=0; i<npt; ++i) {
2436 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2437 for (int j=0; j<npt; ++j) {
2438 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
2439 for (int k=0; k<npt; ++k) {
2440 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
2441 for (int m=0; m<npt; ++m) {
2442 c[3] = cell(3,0) + h*cell_width[3]*(l[3] + qx(m)); // xx
2443 for (int n=0; n<npt; ++n) {
2444 c[4] = cell(4,0) + h*cell_width[4]*(l[4] + qx(n)); // yy
2445 fval(i,j,k,m,n) = f(c);
2446 MADNESS_ASSERT(!isnan(fval(i,j,k,m,n)));
2447 }
2448 }
2449 }
2450 }
2451 }
2452 }
2453 else if (NDIM == 6) {
2454 for (int i=0; i<npt; ++i) {
2455 c[0] = cell(0,0) + h*cell_width[0]*(l[0] + qx(i)); // x
2456 for (int j=0; j<npt; ++j) {
2457 c[1] = cell(1,0) + h*cell_width[1]*(l[1] + qx(j)); // y
2458 for (int k=0; k<npt; ++k) {
2459 c[2] = cell(2,0) + h*cell_width[2]*(l[2] + qx(k)); // z
2460 for (int m=0; m<npt; ++m) {
2461 c[3] = cell(3,0) + h*cell_width[3]*(l[3] + qx(m)); // xx
2462 for (int n=0; n<npt; ++n) {
2463 c[4] = cell(4,0) + h*cell_width[4]*(l[4] + qx(n)); // yy
2464 for (int p=0; p<npt; ++p) {
2465 c[5] = cell(5,0) + h*cell_width[5]*(l[5] + qx(p)); // zz
2466 fval(i,j,k,m,n,p) = f(c);
2467 MADNESS_ASSERT(!isnan(fval(i,j,k,m,n,p)));
2468 }
2469 }
2470 }
2471 }
2472 }
2473 }
2474 }
2475 else {
2476 MADNESS_EXCEPTION("FunctionImpl: fcube: confused about NDIM?",NDIM);
2477 }
2478 }
2479 }
2480
2481 template <typename T, std::size_t NDIM>
2482 void FunctionImpl<T,NDIM>::fcube(const keyT& key, T (*f)(const coordT&), const Tensor<double>& qx, tensorT& fval) const {
2483 // fcube(key,typename FunctionFactory<T,NDIM>::FunctorInterfaceWrapper(f) , qx, fval);
2485 }
2486
2487 template <typename T, std::size_t NDIM>
2489 madness::fcube(key,f,qx,fval);
2490 }
2491
2492
2493 /// project the functor into this functionimpl, and "return" a tree in reconstructed,
2494 /// rank-reduced form.
2495
2496 /// @param[in] key current FunctionNode
2497 /// @param[in] do_refine
2498 /// @param[in] specialpts in case these are very spiky functions -- don't undersample
2499 template <typename T, std::size_t NDIM>
2501 bool do_refine,
2502 const std::vector<Vector<double,NDIM> >& specialpts) {
2503 //PROFILE_MEMBER_FUNC(FunctionImpl);
2504 if (do_refine && key.level() < max_refine_level) {
2505
2506 // Restrict special points to this box
2507 std::vector<Vector<double,NDIM> > newspecialpts;
2508 if (key.level() < special_level && specialpts.size() > 0) {
2510 const auto bperiodic = bc.is_periodic();
2511 for (unsigned int i = 0; i < specialpts.size(); ++i) {
2512 coordT simpt;
2513 user_to_sim(specialpts[i], simpt);
2515 if (specialkey.is_neighbor_of(key,bperiodic)) {
2516 newspecialpts.push_back(specialpts[i]);
2517 }
2518 if (key.is_neighbor_of(simpt, bperiodic)) {
2519 newspecialpts.push_back(specialpts[i]);
2520 }
2521 }
2522 }
2523
2524 // If refining compute scaling function coefficients and
2525 // norm of difference coefficients
2526 tensorT r, s0;
2527 double dnorm = 0.0;
2528 //////////////////////////if (newspecialpts.size() == 0)
2529 {
2530 // Make in r child scaling function coeffs at level n+1
2531 r = tensorT(cdata.v2k);
2532 for (KeyChildIterator<NDIM> it(key); it; ++it) {
2533 const keyT& child = it.key();
2534 r(child_patch(child)) = project(child);
2535 }
2536 // Filter then test difference coeffs at level n
2537 tensorT d = filter(r);
2538 if (truncate_on_project) s0 = copy(d(cdata.s0));
2539 d(cdata.s0) = T(0);
2540 dnorm = d.normf();
2541 }
2542
2543 // If have special points always refine. If don't have special points
2544 // refine if difference norm is big
2545 if (newspecialpts.size() > 0 || dnorm >=truncate_tol(thresh,key.level())) {
2546 coeffs.replace(key,nodeT(coeffT(),true)); // Insert empty node for parent
2547 for (KeyChildIterator<NDIM> it(key); it; ++it) {
2548 const keyT& child = it.key();
2549 ProcessID p;
2551 p = world.random_proc();
2552 }
2553 else {
2554 p = coeffs.owner(child);
2555 }
2556 //PROFILE_BLOCK(proj_refine_send); // Too fine grain for routine profiling
2557 woT::task(p, &implT::project_refine_op, child, do_refine, newspecialpts);
2558 }
2559 }
2560 else {
2561 if (truncate_on_project) {
2563 coeffs.replace(key,nodeT(s,false));
2564 }
2565 else {
2566 coeffs.replace(key,nodeT(coeffT(),true)); // Insert empty node for parent
2567 for (KeyChildIterator<NDIM> it(key); it; ++it) {
2568 const keyT& child = it.key();
2569 coeffT s(r(child_patch(child)),thresh,FunctionDefaults<NDIM>::get_tensor_type());
2570 coeffs.replace(child,nodeT(s,false));
2571 }
2572 }
2573 }
2574 }
2575 else {
2576 coeffs.replace(key,nodeT(coeffT(project(key),targs),false));
2577 }
2578 }
2579
2580 template <typename T, std::size_t NDIM>
2582 std::vector<long> v0(NDIM,0L);
2583 std::vector<long> v1(NDIM,1L);
2584 std::vector<Slice> s(NDIM,Slice(0,0));
2585 const TensorArgs full_args(-1.0,TT_FULL);
2586 if (is_compressed()) {
2587 if (world.rank() == coeffs.owner(cdata.key0)) {
2588 typename dcT::iterator it = coeffs.find(cdata.key0).get();
2589 MADNESS_ASSERT(it != coeffs.end());
2590 nodeT& node = it->second;
2591 MADNESS_ASSERT(node.has_coeff());
2592 // node.node_to_full_rank();
2593 // node.full_tensor_reference()(v0) += t*sqrt(FunctionDefaults<NDIM>::get_cell_volume());
2594 // node.node_to_low_rank();
2597 change_tensor_type(node.coeff(),targs);
2598 }
2599 }
2600 else {
2601 for (typename dcT::iterator it=coeffs.begin(); it!=coeffs.end(); ++it) {
2602 Level n = it->first.level();
2603 nodeT& node = it->second;
2604 if (node.has_coeff()) {
2605 // this looks funny, but is necessary for GenTensor, since you can't access a
2606 // single matrix element. Therefore make a (1^NDIM) tensor, convert to GenTensor, then
2607 // add to the original one by adding a slice.
2608 tensorT ttt(v1);
2610 coeffT tt(ttt,get_tensor_args());
2611 node.coeff()(s) += tt;
2612 // this was the original line:
2613 // node.coeff().full_tensor()(v0) += t*sqrt(FunctionDefaults<NDIM>::get_cell_volume()*pow(0.5,double(NDIM*n)));
2614
2615 }
2616 }
2617 }
2618 if (fence) world.gop.fence();
2619 }
2620
2621 template <typename T, std::size_t NDIM>
2624 if (is_compressed()) initial_level = std::max(initial_level,1); // Otherwise zero function is confused
2625 if (coeffs.is_local(key)) {
2626 if (is_compressed()) {
2627 if (key.level() == initial_level) {
2628 coeffs.replace(key, nodeT(coeffT(), false));
2629 }
2630 else {
2631 coeffs.replace(key, nodeT(coeffT(cdata.v2k,targs), true));
2632 }
2633 }
2634 else {
2635 if (key.level()<initial_level) {
2636 coeffs.replace(key, nodeT(coeffT(), true));
2637 }
2638 else {
2639 coeffs.replace(key, nodeT(coeffT(cdata.vk,targs), false));
2640 }
2641 }
2642 }
2643 if (key.level() < initial_level) {
2644 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
2645 insert_zero_down_to_initial_level(kit.key());
2646 }
2647 }
2648
2649 }
2650
2651
2652 template <typename T, std::size_t NDIM>
2654 //PROFILE_MEMBER_FUNC(FunctionImpl);
2655 typename dcT::iterator it = coeffs.find(key).get();
2656 if (it == coeffs.end()) {
2657 // In a standard tree all children would exist but some ops (transform)
2658 // can leave the tree in a messy state. Just make the missing node as an
2659 // empty leaf.
2660 coeffs.replace(key,nodeT());
2661 it = coeffs.find(key).get();
2662 }
2663 nodeT& node = it->second;
2664 if (node.has_children()) {
2665 std::vector< Future<bool> > v = future_vector_factory<bool>(1<<NDIM);
2666 int i=0;
2667 for (KeyChildIterator<NDIM> kit(key); kit; ++kit,++i) {
2668 v[i] = woT::task(coeffs.owner(kit.key()), &implT::truncate_spawn, kit.key(), tol, TaskAttributes::generator());
2669 }
2670 return woT::task(world.rank(),&implT::truncate_op, key, tol, v);
2671 }
2672 else {
2673 // In compressed form leaves should not have coeffs ... however the
2674 // transform op could leave the tree with leaves that do have coeffs
2675 // in which case we want something sensible to happen
2676 //MADNESS_ASSERT(!node.has_coeff());
2677 if (node.has_coeff() && key.level()>1) {
2678 double dnorm = node.coeff().normf();
2679 if (dnorm < truncate_tol(tol,key)) {
2680 node.clear_coeff();
2681 }
2682 }
2683 return Future<bool>(node.has_coeff());
2684 }
2685 }
2686
2687
2688 template <typename T, std::size_t NDIM>
2689 bool FunctionImpl<T,NDIM>::truncate_op(const keyT& key, double tol, const std::vector< Future<bool> >& v) {
2690 //PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
2691 // If any child has coefficients, a parent cannot truncate
2692 for (int i=0; i<(1<<NDIM); ++i) if (v[i].get()) return true;
2693 nodeT& node = coeffs.find(key).get()->second;
2694
2695 // Interior nodes should always have coeffs but transform might
2696 // leave empty interior nodes ... hence just force no coeffs to
2697 // be zero coeff unless it is a leaf.
2698 if (node.has_children() && !node.has_coeff()) node.set_coeff(coeffT(cdata.v2k,targs));
2699
2700 if (key.level() > 1) { // >1 rather >0 otherwise reconstruct might get confused
2701 double dnorm = node.coeff().normf();
2702 if (dnorm < truncate_tol(tol,key)) {
2703 node.clear_coeff();
2704 if (node.has_children()) {
2705 node.set_has_children(false);
2706 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
2707 coeffs.erase(kit.key());
2708 }
2709 }
2710 }
2711 }
2712 return node.has_coeff();
2713 }
2714
2715
2716 template <typename T, std::size_t NDIM>
2717 void FunctionImpl<T,NDIM>::print_tree(std::ostream& os, Level maxlevel) const {
2718 if (world.rank() == 0) do_print_tree(cdata.key0, os, maxlevel);
2719 world.gop.fence();
2720 if (world.rank() == 0) os.flush();
2721 world.gop.fence();
2722 }
2723
2724
2725 template <typename T, std::size_t NDIM>
2726 void FunctionImpl<T,NDIM>::do_print_tree(const keyT& key, std::ostream& os, Level maxlevel) const {
2727 typename dcT::const_iterator it = coeffs.find(key).get();
2728 if (it == coeffs.end()) {
2729 //MADNESS_EXCEPTION("FunctionImpl: do_print_tree: null node pointer",0);
2730 for (int i=0; i<key.level(); ++i) os << " ";
2731 os << key << " missing --> " << coeffs.owner(key) << "\n";
2732 }
2733 else {
2734 const nodeT& node = it->second;
2735 for (int i=0; i<key.level(); ++i) os << " ";
2736 os << key << " " << node << " --> " << coeffs.owner(key) << "\n";
2737 if (key.level() < maxlevel && node.has_children()) {
2738 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
2739 do_print_tree(kit.key(),os,maxlevel);
2740 }
2741 }
2742 }
2743 }
2744
2745 template <typename T, std::size_t NDIM>
2747 std::multimap<Level, std::tuple<tranT, std::string>> data;
2748 if (world.rank() == 0) do_print_tree_json(cdata.key0, data, maxlevel);
2749 world.gop.fence();
2750 if (world.rank() == 0) {
2751 for (Level level = 0; level != maxlevel; ++level) {
2752 if (data.count(level) == 0)
2753 break;
2754 else {
2755 if (level > 0)
2756 os << ",";
2757 os << "\"" << level << "\":{";
2758 os << "\"level\": " << level << ",";
2759 os << "\"nodes\":{";
2760 auto range = data.equal_range(level);
2761 for (auto it = range.first; it != range.second; ++it) {
2762 os << "\"" << std::get<0>(it->second) << "\":"
2763 << std::get<1>(it->second);
2764 if (std::next(it) != range.second)
2765 os << ",";
2766 }
2767 os << "}}";
2768 }
2769 }
2770 os.flush();
2771 }
2772 world.gop.fence();
2773 }
2774
2775
2776 template <typename T, std::size_t NDIM>
2777 void FunctionImpl<T,NDIM>::do_print_tree_json(const keyT& key, std::multimap<Level, std::tuple<tranT, std::string>>& data, Level maxlevel) const {
2778 typename dcT::const_iterator it = coeffs.find(key).get();
2779 if (it == coeffs.end()) {
2780 MADNESS_EXCEPTION("FunctionImpl: do_print_tree_json: null node pointer",0);
2781 }
2782 else {
2783 const nodeT& node = it->second;
2784 std::ostringstream oss;
2785 oss << "{";
2786 node.print_json(oss);
2787 oss << ",\"owner\": " << coeffs.owner(key) << "}";
2788 auto node_json_str = oss.str();
2789 data.insert(std::make_pair(key.level(), std::make_tuple(key.translation(), node_json_str)));
2790 if (key.level() < maxlevel && node.has_children()) {
2791 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
2792 do_print_tree_json(kit.key(),data, maxlevel);
2793 }
2794 }
2795 }
2796 }
2797
2798 template <typename T, std::size_t NDIM>
2800 // aggregate data by level, thus collect data first, then dump
2801 if (world.rank() == 0) do_print_tree_graphviz(cdata.key0, os, maxlevel);
2802 world.gop.fence();
2803 if (world.rank() == 0) os.flush();
2804 world.gop.fence();
2805 }
2806
2807 template <typename T, std::size_t NDIM>
2808 void FunctionImpl<T,NDIM>::do_print_tree_graphviz(const keyT& key, std::ostream& os, Level maxlevel) const {
2809
2810 struct uniqhash {
2811 static int64_t value(const keyT& key) {
2812 int64_t result = 0;
2813 for (int64_t j = 0; j <= key.level()-1; ++j) {
2814 result += (1 << j*NDIM);
2815 }
2816 result += key.translation()[0];
2817 return result;
2818 }
2819 };
2820
2821 typename dcT::const_iterator it = coeffs.find(key).get();
2822 if (it != coeffs.end()) {
2823 const nodeT& node = it->second;
2824 if (key.level() < maxlevel && node.has_children()) {
2825 for (KeyChildIterator<NDIM> kit(key); kit; ++kit) {
2826 os << uniqhash::value(key) << " -> " << uniqhash::value(kit.key()) << "\n";
2827 do_print_tree_graphviz(kit.key(),os,maxlevel);
2828 }
2829 }
2830 }
2831 }
2832
2833 template <typename T, std::size_t NDIM>
2835 //PROFILE_MEMBER_FUNC(FunctionImpl);
2836
2837 if (not functor) MADNESS_EXCEPTION("FunctionImpl: project: confusion about function?",0);
2838
2839 // if functor provides coeffs directly, awesome; otherwise use compute by yourself
2840 if (functor->provides_coeff()) return functor->coeff(key).full_tensor_copy();
2841
2842 MADNESS_ASSERT(cdata.npt == cdata.k); // only necessary due to use of fast transform
2843 tensorT fval(cdata.vq,false); // this will be the returned result
2844 tensorT work(cdata.vk,false); // initially evaluate the function in here
2845 tensorT workq(cdata.vq,false); // initially evaluate the function in here
2846
2847 // compute the values of the functor at the quadrature points and scale appropriately
2848 madness::fcube(key,*functor,cdata.quad_x,work);
2849 work.scale(sqrt(FunctionDefaults<NDIM>::get_cell_volume()*pow(0.5,double(NDIM*key.level()))));
2850 //return transform(work,cdata.quad_phiw);
2851 return fast_transform(work,cdata.quad_phiw,fval,workq);
2852 }
2853
2854 template <typename T, std::size_t NDIM>
2856 if (coeffs.probe(key)) {
2857 return Future<double>(coeffs.find(key).get()->second.get_norm_tree());
2858 }
2859 MADNESS_ASSERT(key.level());
2860 keyT parent = key.parent();
2861 return woT::task(coeffs.owner(parent), &implT::get_norm_tree_recursive, parent, TaskAttributes::hipri());
2862 }
2863
2864
2865 template <typename T, std::size_t NDIM>
2867 const RemoteReference< FutureImpl< std::pair<keyT,coeffT> > >& ref) const {
2868 //PROFILE_MEMBER_FUNC(FunctionImpl);
2869 if (coeffs.probe(key)) {
2870 const nodeT& node = coeffs.find(key).get()->second;
2871 Future< std::pair<keyT,coeffT> > result(ref);
2872 if (node.has_coeff()) {
2873 //madness::print("sock found it with coeff",key);
2874 result.set(std::pair<keyT,coeffT>(key,node.coeff()));
2875 }
2876 else {
2877 //madness::print("sock found it without coeff",key);
2878 result.set(std::pair<keyT,coeffT>(key,coeffT()));
2879 }
2880 }
2881 else {
2882 keyT parent = key.parent();
2883 //madness::print("sock forwarding to parent",key,parent);
2884 //PROFILE_BLOCK(sitome_send); // Too fine grain for routine profiling
2885 if (coeffs.is_local(parent))
2886 woT::send(coeffs.owner(parent), &FunctionImpl<T,NDIM>::sock_it_to_me, parent, ref);
2887 else
2888 woT::task(coeffs.owner(parent), &FunctionImpl<T,NDIM>::sock_it_to_me, parent, ref, TaskAttributes::hipri());
2889 }
2890 }
2891
2892 // like sock_it_to_me, but it replaces empty node with averaged coeffs from further down the tree
2893 template <typename T, std::size_t NDIM>
2895 const RemoteReference< FutureImpl< std::pair<keyT,coeffT> > >& ref) const {
2897 if (coeffs.probe(key)) {
2898 const nodeT& node = coeffs.find(key).get()->second;
2899 Future< std::pair<keyT,coeffT> > result(ref);
2900 if (node.has_coeff()) {
2901 result.set(std::pair<keyT,coeffT>(key,node.coeff()));
2902 }
2903 else {
2904 result.set(std::pair<keyT,coeffT>(key,nodeT(coeffT(project(key),targs),false).coeff()));
2905 }
2906 }
2907 else {
2908 keyT parent = key.parent();
2909 //PROFILE_BLOCK(sitome2_send); // Too fine grain for routine profiling
2910 woT::task(coeffs.owner(parent), &FunctionImpl<T,NDIM>::sock_it_to_me_too, parent, ref, TaskAttributes::hipri());
2911 }
2912 }
2913
2914
2915 template <typename T, std::size_t NDIM>
2917 const keyT& keyin,
2918 const typename Future<T>::remote_refT& ref) {
2919
2921 // This is ugly. We must figure out a clean way to use
2922 // owner computes rule from the container.
2924 keyT key = keyin;
2926 ProcessID me = world.rank();
2927 while (1) {
2928 ProcessID owner = coeffs.owner(key);
2929 if (owner != me) {
2930 //PROFILE_BLOCK(eval_send); // Too fine grain for routine profiling
2931 woT::task(owner, &implT::eval, x, key, ref, TaskAttributes::hipri());
2932 return;
2933 }
2934 else {
2935 typename dcT::futureT fut = coeffs.find(key);
2936 typename dcT::iterator it = fut.get();
2937 nodeT& node = it->second;
2938 if (node.has_coeff()) {
2939 Future<T>(ref).set(eval_cube(key.level(), x, node.coeff().full_tensor()));
2940 return;
2941 }
2942 else {
2943 for (std::size_t i=0; i<NDIM; ++i) {
2944 double xi = x[i]*2.0;
2945 int li = int(xi);
2946 if (li == 2) li = 1;
2947 x[i] = xi - li;
2948 l[i] = 2*l[i] + li;
2949 }
2950 key = keyT(key.level()+1,l);
2951 }
2952 }
2953 }
2954 //MADNESS_EXCEPTION("should not be here",0);
2955 }
2956
2957
2958 template <typename T, std::size_t NDIM>
2959 std::pair<bool,T>
2962 keyT key(0);
2964 const ProcessID me = world.rank();
2965 while (key.level() <= maxlevel) {
2966 if (coeffs.owner(key) == me) {
2967 typename dcT::futureT fut = coeffs.find(key);
2968 typename dcT::iterator it = fut.get();
2969 if (it != coeffs.end()) {
2970 nodeT& node = it->second;
2971 if (node.has_coeff()) {
2972 return std::pair<bool,T>(true,eval_cube(key.level(), x, node.coeff().full_tensor()));
2973 }
2974 }
2975 }
2976 for (std::size_t i=0; i<NDIM; ++i) {
2977 double xi = x[i]*2.0;
2978 int li = int(xi);
2979 if (li == 2) li = 1;
2980 x[i] = xi - li;
2981 l[i] = 2*l[i] + li;
2982 }
2983 key = keyT(key.level()+1,l);
2984 }
2985 return std::pair<bool,T>(false,0.0);
2986 }
2987
2988 template <typename T, std::size_t NDIM>
2989 void
2991 std::size_t npt, Level maxlevel,
2992 std::pair<bool,T>* results) {
2993 const ProcessID me = world.rank();
2994
2995 // Memoize the most recently hit leaf (key + shallow coefficient copy).
2996 // Quadrature callers stream spatially coherent points, so consecutive
2997 // points usually land in the same leaf box; replaying the exact
2998 // coordinate-refinement arithmetic against the cached key costs a few
2999 // flops per level and skips the per-level container find()s that
3000 // dominate the descent. A miss falls through to the verbatim
3001 // single-point walk below (owner()+find()+Future -- no const_accessor,
3002 // which doubles NUMA descent cost). Leaves partition the domain and
3003 // interior nodes of a reconstructed function hold no coeffs, so a
3004 // translation match at the cached level identifies exactly the leaf the
3005 // scalar descent would have stopped at: results are bit-for-bit
3006 // identical to the single-point overload. No fence intervenes within a
3007 // call, so the cached node cannot be invalidated mid-call.
3008 bool have_cache = false;
3011
3012 for (std::size_t ip=0; ip<npt; ++ip) {
3013 results[ip] = std::pair<bool,T>(false, T(0));
3014
3015 if (have_cache) {
3018 for (std::size_t i=0; i<NDIM; ++i) l[i] = 0;
3019 const Level nl = cached_key.level();
3020 for (Level nn=0; nn<nl; ++nn) {
3021 for (std::size_t i=0; i<NDIM; ++i) {
3022 double xi = x[i]*2.0;
3023 int li = int(xi);
3024 if (li == 2) li = 1;
3025 x[i] = xi - li;
3026 l[i] = 2*l[i] + li;
3027 }
3028 }
3029 bool same = true;
3030 const Vector<Translation,NDIM>& lc = cached_key.translation();
3031 for (std::size_t i=0; i<NDIM; ++i) same = same && (l[i] == lc[i]);
3032 if (same) {
3033 results[ip] = std::pair<bool,T>(true, eval_cube(nl, x, cached_c));
3034 continue;
3035 }
3036 }
3037
3038 // Verbatim single-point descent (keep in sync with the scalar
3039 // overload above).
3041 keyT key(0);
3043 while (key.level() <= maxlevel) {
3044 if (coeffs.owner(key) == me) {
3045 typename dcT::futureT fut = coeffs.find(key);
3046 typename dcT::iterator it = fut.get();
3047 if (it != coeffs.end()) {
3048 nodeT& node = it->second;
3049 if (node.has_coeff()) {
3050 cached_key = key;
3051 cached_c = node.coeff().full_tensor();
3052 have_cache = true;
3053 results[ip] = std::pair<bool,T>(true,
3054 eval_cube(key.level(), x, cached_c));
3055 break;
3056 }
3057 }
3058 }
3059 for (std::size_t i=0; i<NDIM; ++i) {
3060 double xi = x[i]*2.0;
3061 int li = int(xi);
3062 if (li == 2) li = 1;
3063 x[i] = xi - li;
3064 l[i] = 2*l[i] + li;
3065 }
3066 key = keyT(key.level()+1,l);
3067 }
3068 }
3069 }
3070
3071 template <typename T, std::size_t NDIM>
3072 std::vector<std::pair<bool,T>>
3074 std::vector<std::pair<bool,T>> results(xin.size(), std::pair<bool,T>(false,T(0)));
3075 eval_local_only(xin.data(), xin.size(), maxlevel, results.data());
3076 return results;
3077 }
3078
3079 template <typename T, std::size_t NDIM>
3081 const keyT& keyin,
3082 const typename Future<Level>::remote_refT& ref) {
3083
3085 // This is ugly. We must figure out a clean way to use
3086 // owner computes rule from the container.
3088 keyT key = keyin;
3090 ProcessID me = world.rank();
3091 while (1) {
3092 ProcessID owner = coeffs.owner(key);
3093 if (owner != me) {
3094 //PROFILE_BLOCK(eval_send); // Too fine grain for routine profiling
3095 woT::task(owner, &implT::evaldepthpt, x, key, ref, TaskAttributes::hipri());
3096 return;
3097 }
3098 else {
3099 typename dcT::futureT fut = coeffs.find(key);
3100 typename dcT::iterator it = fut.get();
3101 nodeT& node = it->second;
3102 if (node.has_coeff()) {
3103 Future<Level>(ref).set(key.level());
3104 return;
3105 }
3106 else {
3107 for (std::size_t i=0; i<NDIM; ++i) {
3108 double xi = x[i]*2.0;
3109 int li = int(xi);
3110 if (li == 2) li = 1;
3111 x[i] = xi - li;
3112 l[i] = 2*l[i] + li;
3113 }
3114 key = keyT(key.level()+1,l);
3115 }
3116 }
3117 }
3118 //MADNESS_EXCEPTION("should not be here",0);
3119 }
3120
3121 template <typename T, std::size_t NDIM>
3123 const keyT& keyin,
3124 const typename Future<long>::remote_refT& ref) {
3125
3127 // This is ugly. We must figure out a clean way to use
3128 // owner computes rule from the container.
3130 keyT key = keyin;
3132 ProcessID me = world.rank();
3133 while (1) {
3134 ProcessID owner = coeffs.owner(key);
3135 if (owner != me) {
3136 //PROFILE_BLOCK(eval_send); // Too fine grain for routine profiling
3137 woT::task(owner, &implT::evalR, x, key, ref, TaskAttributes::hipri());
3138 return;
3139 }
3140 else {
3141 typename dcT::futureT fut = coeffs.find(key);
3142 typename dcT::iterator it = fut.get();
3143 nodeT& node = it->second;
3144 if (node.has_coeff()) {
3145 Future<long>(ref).set(node.coeff().rank());
3146 return;
3147 }
3148 else {
3149 for (std::size_t i=0; i<NDIM; ++i) {
3150 double xi = x[i]*2.0;
3151 int li = int(xi);
3152 if (li == 2) li = 1;
3153 x[i] = xi - li;
3154 l[i] = 2*l[i] + li;
3155 }
3156 key = keyT(key.level()+1,l);
3157 }
3158 }
3159 }
3160 //MADNESS_EXCEPTION("should not be here",0);
3161 }
3162
3163
3164 template <typename T, std::size_t NDIM>
3165 void FunctionImpl<T,NDIM>::tnorm(const tensorT& t, double* lo, double* hi) {
3166 //PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
3167 auto& cdata=FunctionCommonData<T,NDIM>::get(t.dim(0));
3168 tensorT work = copy(t);
3169 tensorT tlo = work(cdata.sh);
3170 *lo = tlo.normf();
3171 tlo.fill(0.0);
3172 *hi = work.normf();
3173 }
3174
3175 template <typename T, std::size_t NDIM>
3176 void FunctionImpl<T,NDIM>::tnorm(const GenTensor<T>& t, double* lo, double* hi) {
3177 auto& cdata=FunctionCommonData<T,NDIM>::get(t.dim(0));
3178 coeffT shalf=t(cdata.sh);
3179 *lo=shalf.normf();
3180 coeffT sfull=copy(t);
3181 sfull(cdata.sh)-=shalf;
3182 *hi=sfull.normf();
3183 }
3184
3185 template <typename T, std::size_t NDIM>
3186 void FunctionImpl<T,NDIM>::tnorm(const SVDTensor<T>& t, double* lo, double* hi,
3187 const int particle) {
3188 *lo=0.0;
3189 *hi=0.0;
3190 auto& cdata=FunctionCommonData<T,NDIM>::get(t.dim(0));
3191 if (t.rank()==0) return;
3192 const tensorT vec=t.flat_vector(particle-1);
3193 for (long i=0; i<t.rank(); ++i) {
3194 double lo1,hi1;
3195 tensorT c=vec(Slice(i,i),_).reshape(cdata.vk);
3196 tnorm(c, &lo1, &hi1); // note we use g instead of h, since g is 3D
3197 *lo+=lo1*t.weights(i);
3198 *hi+=hi1*t.weights(i);
3199 }
3200 }
3201
3202
3203 namespace detail {
3204 template <typename A, typename B>
3205 struct noop {
3206 void operator()(const A& a, const B& b) const {};
3207
3208 template <typename Archive> void serialize(Archive& ar) {}
3209 };
3210
3211 template <typename T, std::size_t NDIM>
3213 T q;
3215 // G++ 4.1.2 ICEs on BGP ... scaleinplace(T q) : q(q) {}
3216 scaleinplace(T q) {this->q = q;}
3217 void operator()(const Key<NDIM>& key, Tensor<T>& t) const {
3218 t.scale(q);
3219 }
3220 void operator()(const Key<NDIM>& key, FunctionNode<T,NDIM>& node) const {
3221 node.coeff().scale(q);
3222 }
3223 template <typename Archive> void serialize(Archive& ar) {
3224 ar & q;
3225 }
3226 };
3227
3228 template <typename T, std::size_t NDIM>
3230 void operator()(const Key<NDIM>& key, Tensor<T>& t) const {
3231 t.emul(t);
3232 }
3233 template <typename Archive> void serialize(Archive& ar) {}
3234 };
3235
3236 template <typename T, std::size_t NDIM>
3237 struct absinplace {
3238 void operator()(const Key<NDIM>& key, Tensor<T>& t) const {t=abs(t);}
3239 template <typename Archive> void serialize(Archive& ar) {}
3240 };
3241
3242 template <typename T, std::size_t NDIM>
3244 void operator()(const Key<NDIM>& key, Tensor<T>& t) const {abs(t.emul(t));}
3245 template <typename Archive> void serialize(Archive& ar) {}
3246 };
3247
3248 }
3249
3250template <typename T, std::size_t NDIM>
3251 void FunctionImpl<T,NDIM>::scale_inplace(const T q, bool fence) {
3252 // unary_op_coeff_inplace(detail::scaleinplace<T,NDIM>(q), fence);
3253 unary_op_node_inplace(detail::scaleinplace<T,NDIM>(q), fence);
3254 }
3255
3256 template <typename T, std::size_t NDIM>
3258 //unary_op_value_inplace(&implT::autorefine_square_test, detail::squareinplace<T,NDIM>(), fence);
3259 unary_op_value_inplace(detail::squareinplace<T,NDIM>(), fence);
3260 }
3261
3262 template <typename T, std::size_t NDIM>
3264 unary_op_value_inplace(detail::absinplace<T,NDIM>(), fence);
3265 }
3266
3267 template <typename T, std::size_t NDIM>
3269 unary_op_value_inplace(detail::abssquareinplace<T,NDIM>(), fence);
3270 }
3271
3272 template <typename T, std::size_t NDIM>
3274 //PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
3275 double p[200];
3276 double scale = pow(2.0,double(np-nc));
3277 for (int mu=0; mu<cdata.npt; ++mu) {
3278 double xmu = scale*(cdata.quad_x(mu)+lc) - lp;
3279 MADNESS_ASSERT(xmu>-1e-15 && xmu<(1+1e-15));
3281 for (int i=0; i<k; ++i) phi(i,mu) = p[i];
3282 }
3283 phi.scale(pow(2.0,0.5*np));
3284 }
3285
3286 template <typename T, std::size_t NDIM>
3287
3288 const GenTensor<T> FunctionImpl<T,NDIM>::parent_to_child(const coeffT& s, const keyT& parent, const keyT& child) const {
3289 //PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
3290 // An invalid parent/child means that they are out of the box
3291 // and it is the responsibility of the caller to worry about that
3292 // ... most likely the coefficients (s) are zero to reflect
3293 // zero B.C. so returning s makes handling this easy.
3294 if (parent == child || parent.is_invalid() || child.is_invalid()) return s;
3295
3296 coeffT result = fcube_for_mul<T>(child, parent, s);
3297 result.scale(sqrt(FunctionDefaults<NDIM>::get_cell_volume()*pow(0.5,double(NDIM*child.level()))));
3298 result = transform(result,cdata.quad_phiw);
3299
3300 return result;
3301 }
3302
3303
3304 template <typename T, std::size_t NDIM>
3307 std::vector<long> v0(NDIM,0);
3308 T sum = 0.0;
3309 if (is_compressed()) {
3310 if (world.rank() == coeffs.owner(cdata.key0)) {
3311 typename dcT::const_iterator it = coeffs.find(cdata.key0).get();
3312 if (it != coeffs.end()) {
3313 const nodeT& node = it->second;
3314 if (node.has_coeff()) sum = node.coeff().full_tensor()(v0);
3315 }
3316 }
3317 }
3318 else {
3319 for (typename dcT::const_iterator it=coeffs.begin(); it!=coeffs.end(); ++it) {
3320 const keyT& key = it->first;
3321 const nodeT& node = it->second;
3322 if (node.has_coeff()) sum += node.coeff().full_tensor()(v0)*pow(0.5,NDIM*key.level()*0.5);
3323 }
3324 }
3326 }
3327
3328
3329 // Return whether l is in the interval [0, 2n).
3330 // If is_periodic, then this is checked modulo 2n. The function always
3331 // returns true, but l is *modified* to be in the interval.
3332 static inline bool enforce_bc(bool is_periodic, Level n, Translation& l) {
3333 const Translation two2n = 1ul << n;
3334 if (l < 0) {
3335 if (is_periodic) {
3336 do {
3337 l += two2n; // Periodic BC
3338 } while (l < 0);
3339 } else
3340 return false; // Zero BC
3341 } else if (l >= two2n) {
3342 if (is_periodic) {
3343 do {
3344 l -= two2n; // Periodic BC
3345 } while (l >= two2n);
3346 } else
3347 return false; // Zero BC
3348 }
3349 return true;
3350 }
3351
3352 static inline bool enforce_in_volume(Level n, const Translation& l) {
3353 Translation two2n = 1ul << n;
3354 return l >= 0 && l < two2n;
3355 }
3356
3357 // Return the key corresponding to `key` + `disp`.
3358 // If is_periodic, then translations in the key are taken modulo the box
3359 // dimensions. Otherwise, displacements outside the box are invalid.
3360 template <typename T, std::size_t NDIM>
3363
3364 for (std::size_t axis=0; axis<NDIM; ++axis) {
3365 l[axis] += disp.translation()[axis];
3366
3367 //if (!enforce_bc(bc(axis,0), bc(axis,1), key.level(), l[axis])) {
3368 if (!enforce_bc(is_periodic[axis], key.level(), l[axis])) {
3369 return keyT::invalid();
3370 }
3371 }
3372 return keyT(key.level(),l);
3373 }
3374
3375 template <typename T, std::size_t NDIM>
3378
3379 for (std::size_t axis = 0; axis < NDIM; ++axis) {
3380 l[axis] += disp.translation()[axis];
3381
3382 if (!enforce_in_volume(key.level(), l[axis])) {
3383 return keyT::invalid();
3384 }
3385 }
3386 return keyT(key.level(), l);
3387 }
3388
3389 template <typename T, std::size_t NDIM>
3392 //PROFILE_MEMBER_FUNC(FunctionImpl); // Too fine grain for routine profiling
3393 typedef std::pair< Key<NDIM>,coeffT > argT;
3394 Future<argT> result;
3395 //PROFILE_BLOCK(find_me_send); // Too fine grain for routine profiling
3396 woT::task(coeffs.owner(key), &implT::sock_it_to_me_too, key, result.remote_ref(world), TaskAttributes::hipri());
3397 return result;
3398 }
3399
3400
3401 /// will insert
3402 /// @return s coefficient and (norm_tree, dnorm_tree) for key
3403 template <typename T, std::size_t NDIM>
3405 bool nonstandard1, bool keepleaves, bool redundant1) {
3406 if (!coeffs.probe(key)) print("missing node",key);
3407 MADNESS_ASSERT(coeffs.probe(key));
3408
3409 // get fetches remote data (here actually local)
3410 nodeT& node = coeffs.find(key).get()->second;
3411
3412 // internal node -> continue recursion
3413 if (node.has_children()) {
3414 std::vector< Future<compressT> > v = future_vector_factory<compressT>(1<<NDIM);
3415 int i=0;
3416 for (KeyChildIterator<NDIM> kit(key); kit; ++kit,++i) {
3417 //PROFILE_BLOCK(compress_send); // Too fine grain for routine profiling
3418 // readily available
3419 v[i] = woT::task(coeffs.owner(kit.key()), &implT::compress_spawn, kit.key(),
3421 }
3422 if (redundant1) return woT::task(world.rank(),&implT::make_redundant_op, key, v);
3423 return woT::task(world.rank(),&implT::compress_op, key, v, nonstandard1);
3424 }
3425
3426 // leaf node -> remove coefficients here and pass them back to parent for filtering
3427 // insert snorm, dnorm=0.0, normtree (=snorm), dnormtree (=0.0)
3428 else {
3429 // special case: tree has only root node: keep sum coeffs and make zero diff coeffs
3430 if (key.level()==0) {
3431 if (redundant1) {
3432 // with only the root node existing redundant and reconstructed are the same
3433 coeffT result(node.coeff());
3434 double snorm=node.coeff().normf();
3435 node.set_dnorm(0.0);
3436 node.set_snorm(snorm);
3437 node.set_norm_tree(snorm);
3438 node.set_dnorm_tree(0.0);
3439 return Future<compressT>(std::make_pair(result,std::make_pair(snorm,0.0)));
3440 } else {
3441 // compress
3442 coeffT result(node.coeff());
3443 coeffT sdcoeff(cdata.v2k,this->get_tensor_type());
3444 sdcoeff(cdata.s0)+=node.coeff();
3446 double snorm=node.coeff().normf();
3447 node.set_dnorm(0.0);
3448 node.set_snorm(snorm);
3449 node.set_norm_tree(snorm);
3450 node.set_dnorm_tree(0.0);
3451 return Future<compressT>(std::make_pair(result,std::make_pair(node.coeff().normf(),0.0)));
3452 }
3453
3454 } else { // this is a leaf node
3455 Future<coeffT > result(node.coeff());
3456 if (not keepleaves) node.clear_coeff();
3458 auto snorm=(keepleaves) ? node.coeff().normf() : 0.0;
3459 node.set_norm_tree(snorm);
3460 node.set_dnorm_tree(0.0);
3461 node.set_snorm(snorm);
3462 node.set_dnorm(0.0);
3464 return Future<compressT>(std::make_pair(result,std::make_pair(snorm,0.0)));
3465 }
3466 }
3467 }
3468
3469 template <typename T, std::size_t NDIM>
3471 const keyT& key,
3472 const coordT& plotlo, const coordT& plothi, const std::vector<long>& npt,
3473 bool eval_refine) const {
3474
3475 Tensor<T>& r = *ptr;
3476
3477 coordT h; // Increment between points in each dimension
3478 for (std::size_t i=0; i<NDIM; ++i) {
3479 if (npt[i] > 1) {
3480 h[i] = (plothi[i]-plotlo[i])/(npt[i]-1);
3481 }
3482 else {
3483 MADNESS_ASSERT(plotlo[i] == plothi[i]);
3484 h[i] = 0.0;
3485 }
3486 }
3487
3488 const Level n = key.level();
3490 const double twon = pow(2.0,double(n));
3491 const tensorT& coeff = coeffs.find(key).get()->second.coeff().full_tensor(); // Ugh!
3492 long ind[NDIM];
3493 coordT x;
3497 double fac = pow(0.5,double(key.level()));
3498 int npttotal = 1;
3499 for (std::size_t d=0; d<NDIM; ++d) {
3500 // Coords of box
3501 boxlo[d] = fac*key.translation()[d];
3502 boxhi[d] = boxlo[d]+fac;
3503
3504 if (boxlo[d] > plothi[d] || boxhi[d] < plotlo[d]) {
3505 // Discard boxes out of the plot range
3506 npttotal = boxnpt[d] = 0;
3507 //print("OO range?");
3508 break;
3510 else if (npt[d] == 1) {
3511 // This dimension is only a single point
3512 boxlo[d] = boxhi[d] = plotlo[d];
3513 boxnpt[d] = 1;
3514 }
3515 else {
3516 // Restrict to plot range
3517 boxlo[d] = std::max(boxlo[d],plotlo[d]);
3518 boxhi[d] = std::min(boxhi[d],plothi[d]);
3520 // Round lo up to next plot point; round hi down
3521 double xlo = long((boxlo[d]-plotlo[d])/h[d])*h[d] + plotlo[d];
3522 if (xlo < boxlo[d]) xlo += h[d];
3523 boxlo[d] = xlo;
3524 double xhi = long((boxhi[d]-plotlo[d])/h[d])*h[d] + plotlo[d];
3525 if (xhi > boxhi[d]) xhi -= h[d];
3526 // MADNESS_ASSERT(xhi >= xlo); // nope
3528 boxnpt[d] = long(round((boxhi[d] - boxlo[d])/h[d])) + 1;
3529 }
3530 npttotal *= boxnpt[d];
3531 }
3532 //print(" box", boxlo, boxhi, boxnpt, npttotal);
3533 if (npttotal > 0) {
3534 for (IndexIterator it(boxnpt); it; ++it) {
3535 for (std::size_t d=0; d<NDIM; ++d) {
3536 double xd = boxlo[d] + it[d]*h[d]; // Sim. coords of point
3537 x[d] = twon*xd - l[d]; // Offset within box
3538 MADNESS_ASSERT(x[d]>=0.0 && x[d] <=1.0); // sanity
3539 if (npt[d] > 1) {
3540 ind[d] = long(round((xd-plotlo[d])/h[d])); // Index of plot point
3541 }
3542 else {
3543 ind[d] = 0;
3544 }
3545 MADNESS_ASSERT(ind[d]>=0 && ind[d]<npt[d]); // sanity
3546 }
3547 if (eval_refine) {
3548 r(ind) = n;
3549 }
3550 else {
3551 T tmp = eval_cube(n, x, coeff);
3552 r(ind) = tmp;
3553 //print(" eval", ind, tmp, r(ind));
3555 }
3556 }
3557 }
3558
3559 /// Set plot_refine=true to get a plot of the refinment levels of
3560 /// the given function (defaulted to false in prototype).
3561 template <typename T, std::size_t NDIM>
3563 const coordT& plothi,
3564 const std::vector<long>& npt,
3565 const bool eval_refine) const {
3567 Tensor<T> r(NDIM, &npt[0]);
3568 //r(___) = 99.0;
3569 MADNESS_ASSERT(is_reconstructed());
3570
3571 for (typename dcT::const_iterator it=coeffs.begin(); it!=coeffs.end(); ++it) {
3572 const keyT& key = it->first;
3573 const nodeT& node = it->second;
3574 if (node.has_coeff()) {
3575 woT::task(world.rank(), &implT::plot_cube_kernel,
3577 }
3578 }
3579
3580 // ITERATOR(r, if (r(IND) == 99.0) {print("BAD", IND); error("bad",0);});
3581
3582 world.taskq.fence();
3583 world.gop.sum(r.ptr(), r.size());
3584 world.gop.fence();
3585
3586 return r;
3587 }
3588
3589 static inline void dxprintvalue(FILE* f, const double t) {
3590 fprintf(f,"%.6e\n",t);
3591 }
3592
3593 static inline void dxprintvalue(FILE* f, const double_complex& t) {
3594 fprintf(f,"%.6e %.6e\n", t.real(), t.imag());
3596
3597 template <typename T, std::size_t NDIM>
3599 const char* filename,
3600 const Tensor<double>& cell,
3601 const std::vector<long>& npt,
3602 bool binary) {
3605 // The plot cell must be an (NDIM x 2) [lo,hi]-per-dimension tensor; an empty
3606 // or ill-shaped cell would dereference out of bounds below (cell(d,0)/(d,1)),
3607 // which previously segfaulted. Convert that into a clear error. Callers must
3608 // default an unset cell to the simulation cell first (see SCF::do_plots).
3609 MADNESS_CHECK_THROW(cell.ndim()==2 && cell.dim(0)>=static_cast<long>(NDIM)
3610 && cell.dim(1)>=2,
3611 "plotdx: plot cell must be an (NDIM x 2) [lo,hi] tensor "
3612 "(got an empty or ill-shaped cell)");
3613 const char* element[6] = {"lines","quads","cubes","cubes4D","cubes5D","cubes6D"};
3614
3615 function.verify();
3616 World& world = const_cast< Function<T,NDIM>& >(function).world();
3617 FILE *f=0;
3618 if (world.rank() == 0) {
3619 f = fopen(filename, "w");
3620 if (!f) MADNESS_EXCEPTION("plotdx: failed to open the plot file", 0);
3621
3622 fprintf(f,"object 1 class gridpositions counts ");
3623 for (std::size_t d=0; d<NDIM; ++d) fprintf(f," %ld",npt[d]);
3624 fprintf(f,"\n");
3625
3626 fprintf(f,"origin ");
3627 for (std::size_t d=0; d<NDIM; ++d) fprintf(f, " %.6e", cell(d,0));
3628 fprintf(f,"\n");
3629
3630 for (std::size_t d=0; d<NDIM; ++d) {
3631 fprintf(f,"delta ");
3632 for (std::size_t c=0; c<d; ++c) fprintf(f, " 0");
3633 double h = 0.0;
3634 if (npt[d]>1) h = (cell(d,1)-cell(d,0))/(npt[d]-1);
3635 fprintf(f," %.6e", h);
3636 for (std::size_t c=d+1; c<NDIM; ++c) fprintf(f, " 0");
3637 fprintf(f,"\n");
3638 }
3639 fprintf(f,"\n");
3640
3641 fprintf(f,"object 2 class gridconnections counts ");
3642 for (std::size_t d=0; d<NDIM; ++d) fprintf(f," %ld",npt[d]);
3643 fprintf(f,"\n");
3644 fprintf(f, "attribute \"element type\" string \"%s\"\n", element[NDIM-1]);
3645 fprintf(f, "attribute \"ref\" string \"positions\"\n");
3646 fprintf(f,"\n");
3647
3648 int npoint = 1;
3649 for (std::size_t d=0; d<NDIM; ++d) npoint *= npt[d];
3650 const char* iscomplex = "";
3651 if (TensorTypeData<T>::iscomplex) iscomplex = "category complex";
3652 const char* isbinary = "";
3653 if (binary) isbinary = "binary";
3654 fprintf(f,"object 3 class array type double %s rank 0 items %d %s data follows\n",
3656 }
3657
3658 world.gop.fence();
3659 Tensor<T> r = function.eval_cube(cell, npt);
3660
3661 if (world.rank() == 0) {
3662 if (binary) {
3663 // This assumes that the values are double precision
3664 fflush(f);
3665 fwrite((void *) r.ptr(), sizeof(T), r.size(), f);
3666 fflush(f);
3667 }
3668 else {
3669 for (IndexIterator it(npt); it; ++it) {
3670 //fprintf(f,"%.6e\n",r(*it));
3671 dxprintvalue(f,r(*it));
3672 }
3673 }
3674 fprintf(f,"\n");
3675
3676 fprintf(f,"object \"%s\" class field\n",filename);
3677 fprintf(f,"component \"positions\" value 1\n");
3678 fprintf(f,"component \"connections\" value 2\n");
3679 fprintf(f,"component \"data\" value 3\n");
3680 fprintf(f,"\nend\n");
3681 fclose(f);
3682 }
3683 world.gop.fence();
3684 }
3685
3686 template <std::size_t NDIM>
3688 k = 6;
3689 thresh = 1e-4;
3690 initial_level = 2;
3691 special_level = 3;
3692 max_refine_level = 30;
3693 truncate_mode = 0;
3694 refine = true;
3695 autorefine = true;
3696 debug = false;
3697 truncate_on_project = true;
3698 apply_randomize = false;
3699 project_randomize = false;
3700 if (!bc.has_value()) bc = BoundaryConditions<NDIM>(BC_FREE);
3701 tt = TT_FULL;
3702 cell = make_default_cell();
3703 recompute_cell_info();
3704 set_default_pmap(world);
3705 }
3706
3707 template <std::size_t NDIM>
3708 std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > > FunctionDefaults<NDIM>::make_default_pmap(World& world) {
3709 // return std::make_shared<WorldDCDefaultPmap< Key<NDIM> >>(world);
3710 return std::make_shared<LevelPmap< Key<NDIM> >>(world);
3711 // return std::make_shared<SimplePmap< Key<NDIM> >>(world);
3712 }
3713
3714 template <std::size_t NDIM>
3716 pmap = make_default_pmap(world);
3717 pmap_nproc = world.nproc();
3718 }
3719
3720
3721 template <std::size_t NDIM>
3723 std::cout << "Function Defaults:" << std::endl;
3724 std::cout << " Dimension " << ": " << NDIM << std::endl;
3725 std::cout << " k" << ": " << k << std::endl;
3726 std::cout << " thresh" << ": " << thresh << std::endl;
3727 std::cout << " initial_level" << ": " << initial_level << std::endl;
3728 std::cout << " special_level" << ": " << special_level << std::endl;
3729 std::cout << " max_refine_level" << ": " << max_refine_level << std::endl;
3730 std::cout << " truncate_mode" << ": " << truncate_mode << std::endl;
3731 std::cout << " refine" << ": " << refine << std::endl;
3732 std::cout << " autorefine" << ": " << autorefine << std::endl;
3733 std::cout << " debug" << ": " << debug << std::endl;
3734 std::cout << " truncate_on_project" << ": " << truncate_on_project << std::endl;
3735 std::cout << " apply_randomize" << ": " << apply_randomize << std::endl;
3736 std::cout << " project_randomize" << ": " << project_randomize << std::endl;
3737 std::cout << " bc" << ": " << get_bc() << std::endl;
3738 std::cout << " tt" << ": " << tt << std::endl;
3739 std::cout << " cell" << ": " << cell << std::endl;
3740 }
3741
3742 template <typename T, std::size_t NDIM>
3743 const FunctionCommonData<T,NDIM>* FunctionCommonData<T,NDIM>::data[MAXK] = {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
3744
3745 // default values match those in FunctionDefaults::set_defaults(world)
3746 template <std::size_t NDIM> int FunctionDefaults<NDIM>::k = 6;
3747 template <std::size_t NDIM> double FunctionDefaults<NDIM>::thresh = 1e-4;
3748 template <std::size_t NDIM> int FunctionDefaults<NDIM>::initial_level = 2;
3749 template <std::size_t NDIM> int FunctionDefaults<NDIM>::special_level = 3;
3750 template <std::size_t NDIM> int FunctionDefaults<NDIM>::max_refine_level = 30;
3751 template <std::size_t NDIM> int FunctionDefaults<NDIM>::truncate_mode = 0;
3752 template <std::size_t NDIM> bool FunctionDefaults<NDIM>::refine = true;
3753 template <std::size_t NDIM> bool FunctionDefaults<NDIM>::autorefine = true;
3754 template <std::size_t NDIM> bool FunctionDefaults<NDIM>::debug = false;
3755 template <std::size_t NDIM> bool FunctionDefaults<NDIM>::truncate_on_project = true;
3756 template <std::size_t NDIM> bool FunctionDefaults<NDIM>::apply_randomize = false;
3757 template <std::size_t NDIM> bool FunctionDefaults<NDIM>::project_randomize = false;
3758 template <std::size_t NDIM> std::optional<BoundaryConditions<NDIM>> FunctionDefaults<NDIM>::bc;
3759 template <std::size_t NDIM> TensorType FunctionDefaults<NDIM>::tt = TT_FULL;
3760 template <std::size_t NDIM> Tensor<double> FunctionDefaults<NDIM>::cell = FunctionDefaults<NDIM>::make_default_cell();
3761 template <std::size_t NDIM> Tensor<double> FunctionDefaults<NDIM>::cell_width = FunctionDefaults<NDIM>::make_default_cell_width();
3762 template <std::size_t NDIM> Tensor<double> FunctionDefaults<NDIM>::rcell_width = FunctionDefaults<NDIM>::make_default_cell_width();
3763 template <std::size_t NDIM> double FunctionDefaults<NDIM>::cell_volume = 1.;
3764 template <std::size_t NDIM> double FunctionDefaults<NDIM>::cell_min_width = 1.;
3765 template <std::size_t NDIM> std::shared_ptr< WorldDCPmapInterface< Key<NDIM> > > FunctionDefaults<NDIM>::pmap;
3766 template <std::size_t NDIM> int FunctionDefaults<NDIM>::pmap_nproc{-1};
3767
3768}
3769
3770#endif // MADNESS_MRA_MRAIMPL_H__INCLUDED
double w(double t, double eps)
Definition DKops.h:22
double q(double t)
Definition DKops.h:18
std::complex< double > double_complex
Definition cfft.h:14
Definition test_ar.cc:118
Definition test_ar.cc:141
Definition test_tree.cc:78
long dim(int i) const
Returns the size of dimension i.
Definition basetensor.h:147
long ndim() const
Returns the number of dimensions in the tensor.
Definition basetensor.h:144
long size() const
Returns the number of elements in the tensor.
Definition basetensor.h:138
This class is used to specify boundary conditions for all operators.
Definition bc.h:72
a class to track where relevant (parent) coeffs are
Definition funcimpl.h:814
Tri-diagonal operator traversing tree primarily for derivative operator.
Definition derivative.h:73
ElementaryInterface (formerly FunctorInterfaceWrapper) interfaces a c-function.
Definition function_interface.h:275
FunctionCommonData holds all Function data common for given k.
Definition function_common_data.h:52
static const FunctionCommonData< T, NDIM > & get(int k)
Definition function_common_data.h:111
static void _init_quadrature(int k, int npt, Tensor< double > &quad_x, Tensor< double > &quad_w, Tensor< double > &quad_phi, Tensor< double > &quad_phiw, Tensor< double > &quad_phit)
Initialize the quadrature information.
Definition mraimpl.h:91
void _init_twoscale()
Private. Initialize the twoscale coefficients.
Definition mraimpl.h:70
FunctionDefaults holds default paramaters as static class members.
Definition funcdefaults.h:100
static Tensor< double > make_default_cell_width()
Definition funcdefaults.h:136
static std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > make_default_pmap(World &world)
Makes a default process map for the given world.
Definition mraimpl.h:3708
static bool truncate_on_project
If true initial projection inserts at n-1 not n.
Definition funcdefaults.h:114
static bool apply_randomize
If true use randomization for load balancing in apply integral operator.
Definition funcdefaults.h:115
static double cell_volume
Volume of simulation cell.
Definition funcdefaults.h:121
static int k
Wavelet order.
Definition funcdefaults.h:105
static double get_cell_volume()
Returns the volume of the user cell.
Definition funcdefaults.h:394
static int truncate_mode
Truncation method.
Definition funcdefaults.h:110
static void set_default_pmap(World &world)
Definition mraimpl.h:3715
static int pmap_nproc
Number of processes assumed by pmap, -1 indicates uninitialized pmap.
Definition funcdefaults.h:125
static Tensor< double > cell_width
Width of simulation cell in each dimension.
Definition funcdefaults.h:119
static double thresh
Truncation threshold.
Definition funcdefaults.h:106
static bool debug
Controls output of debug info.
Definition funcdefaults.h:113
static int special_level
Minimum level for fine scale projection of special boxes.
Definition funcdefaults.h:108
static double cell_min_width
Size of smallest dimension.
Definition funcdefaults.h:122
static std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > pmap
Default mapping of keys to processes.
Definition funcdefaults.h:124
static const Tensor< double > & get_cell_width()
Returns the width of each user cell dimension.
Definition funcdefaults.h:379
static const BoundaryConditions< NDIM > & get_bc()
Returns the default boundary conditions.
Definition funcdefaults.h:309
static Tensor< double > cell
cell[NDIM][2] Simulation cell, cell(0,0)=xlo, cell(0,1)=xhi, ...
Definition funcdefaults.h:118
static int initial_level
Initial level for fine scale projection.
Definition funcdefaults.h:107
static bool autorefine
Whether to autorefine in multiplication, etc.
Definition funcdefaults.h:112
static Tensor< double > make_default_cell()
Definition funcdefaults.h:127
static std::optional< BoundaryConditions< NDIM > > bc
Default boundary conditions, not initialized by default and must be set explicitly before use.
Definition funcdefaults.h:117
static void print()
Definition mraimpl.h:3722
static Tensor< double > rcell_width
Reciprocal of width.
Definition funcdefaults.h:120
static double get_cell_min_width()
Returns the minimum width of any user cell dimension.
Definition funcdefaults.h:389
static bool project_randomize
If true use randomization for load balancing in project/refine.
Definition funcdefaults.h:116
static const Tensor< double > & get_cell()
Gets the user cell for the simulation.
Definition funcdefaults.h:346
static void set_defaults(World &world)
Definition mraimpl.h:3687
static int max_refine_level
Level at which to stop refinement.
Definition funcdefaults.h:109
static TensorType tt
structure of the tensor in FunctionNode
Definition funcdefaults.h:123
static bool refine
Whether to refine new functions.
Definition funcdefaults.h:111
Abstract base class interface required for functors used as input to Functions.
Definition function_interface.h:68
Definition funcimpl.h:5669
FunctionImpl holds all Function state to facilitate shallow copy semantics.
Definition funcimpl.h:968
bool is_nonstandard() const
Definition mraimpl.h:273
T eval_cube(Level n, coordT &x, const tensorT &c) const
Definition mraimpl.h:2054
void do_print_tree_graphviz(const keyT &key, std::ostream &os, Level maxlevel) const
Functor for the do_print_tree method (using GraphViz)
Definition mraimpl.h:2808
void change_tensor_type1(const TensorArgs &targs, bool fence)
change the tensor type of the coefficients in the FunctionNode
Definition mraimpl.h:1113
compressT compress_op(const keyT &key, const std::vector< Future< compressT > > &v, bool nonstandard)
calculate the wavelet coefficients using the sum coefficients of all child nodes
Definition mraimpl.h:1688
void evaldepthpt(const Vector< double, NDIM > &xin, const keyT &keyin, const typename Future< Level >::remote_refT &ref)
Get the depth of the tree at a point in simulation coordinates.
Definition mraimpl.h:3080
void scale_inplace(const T q, bool fence)
In-place scale by a constant.
Definition mraimpl.h:3251
void gaxpy_oop_reconstructed(const double alpha, const implT &f, const double beta, const implT &g, const bool fence)
perform: this= alpha*f + beta*g, invoked by result
Definition mraimpl.h:223
bool is_redundant() const
Returns true if the function is redundant.
Definition mraimpl.h:262
std::size_t nCoeff_local() const
Returns the number of coefficients in the function for this MPI rank.
Definition mraimpl.h:1951
void print_size(const std::string name) const
print tree size and size
Definition mraimpl.h:1970
void print_info() const
Prints summary of data distribution.
Definition mraimpl.h:833
void abs_inplace(bool fence)
Definition mraimpl.h:3263
void print_timer() const
Definition mraimpl.h:357
void evalR(const Vector< double, NDIM > &xin, const keyT &keyin, const typename Future< long >::remote_refT &ref)
Get the rank of leaf box of the tree at a point in simulation coordinates.
Definition mraimpl.h:3122
void do_print_grid(const std::string filename, const std::vector< keyT > &keys) const
print the grid in xyz format
Definition mraimpl.h:584
Future< compressT > compress_spawn(const keyT &key, bool nonstandard, bool keepleaves, bool redundant1)
Invoked on node where key is local.
Definition mraimpl.h:3404
std::size_t nCoeff() const
Returns the number of coefficients in the function ... collective global sum.
Definition mraimpl.h:1961
keyT neighbor_in_volume(const keyT &key, const keyT &disp) const
Returns key of general neighbor that resides in-volume.
Definition mraimpl.h:3376
void compress(const TreeState newstate, bool fence)
compress the wave function
Definition mraimpl.h:1521
Future< bool > truncate_spawn(const keyT &key, double tol)
Returns true if after truncation this node has coefficients.
Definition mraimpl.h:2653
Future< double > norm_tree_spawn(const keyT &key)
Definition mraimpl.h:1591
std::vector< keyT > local_leaf_keys() const
return the keys of the local leaf boxes
Definition mraimpl.h:558
void do_print_tree(const keyT &key, std::ostream &os, Level maxlevel) const
Functor for the do_print_tree method.
Definition mraimpl.h:2726
void unset_functor()
Definition mraimpl.h:312
void do_print_plane(const std::string filename, std::vector< Tensor< double > > plotinfo, const int xaxis, const int yaxis, const coordT el2)
print the MRA structure
Definition mraimpl.h:499
std::pair< Key< NDIM >, ShallowNode< T, NDIM > > find_datum(keyT key) const
return the a std::pair<key, node>, which MUST exist
Definition mraimpl.h:979
void set_functor(const std::shared_ptr< FunctionFunctorInterface< T, NDIM > > functor1)
Definition mraimpl.h:293
bool verify_tree_state_local() const
check that the tree state and the coeffs are consistent
Definition mraimpl.h:169
const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > & get_pmap() const
Definition mraimpl.h:207
void sock_it_to_me(const keyT &key, const RemoteReference< FutureImpl< std::pair< keyT, coeffT > > > &ref) const
Walk up the tree returning pair(key,node) for first node with coefficients.
Definition mraimpl.h:2866
double get_thresh() const
Definition mraimpl.h:328
void trickle_down(bool fence)
sum all the contributions from all scales after applying an operator in mod-NS form
Definition mraimpl.h:1368
void set_autorefine(bool value)
Definition mraimpl.h:337
tensorT filter(const tensorT &s) const
Transform sum coefficients at level n to sums+differences at level n-1.
Definition mraimpl.h:1166
void chop_at_level(const int n, const bool fence=true)
remove all nodes with level higher than n
Definition mraimpl.h:1129
void print_tree_json(std::ostream &os=std::cout, Level maxlevel=10000) const
Definition mraimpl.h:2746
coeffT parent_to_child_NS(const keyT &child, const keyT &parent, const coeffT &coeff) const
Directly project parent NS coeffs to child NS coeffs.
Definition mraimpl.h:707
void mapdim(const implT &f, const std::vector< long > &map, bool fence)
Permute the dimensions of f according to map, result on this.
Definition mraimpl.h:1071
bool is_compressed() const
Returns true if the function is compressed.
Definition mraimpl.h:250
void mirror(const implT &f, const std::vector< long > &mirror, bool fence)
mirror the dimensions of f according to map, result on this
Definition mraimpl.h:1080
void print_stats() const
print the number of configurations per node
Definition mraimpl.h:1998
void broaden(const array_of_bools< NDIM > &is_periodic, bool fence)
Definition mraimpl.h:1317
coeffT truncate_reconstructed_op(const keyT &key, const std::vector< Future< coeffT > > &v, const double tol)
given the sum coefficients of all children, truncate or not
Definition mraimpl.h:1638
void fcube(const keyT &key, const FunctionFunctorInterface< T, NDIM > &f, const Tensor< double > &qx, tensorT &fval) const
Evaluate function at quadrature points in the specified box.
Definition mraimpl.h:2488
void forward_do_diff1(const DerivativeBase< T, NDIM > *D, const implT *f, const keyT &key, const std::pair< keyT, coeffT > &left, const std::pair< keyT, coeffT > &center, const std::pair< keyT, coeffT > &right)
Definition mraimpl.h:932
std::vector< Slice > child_patch(const keyT &child) const
Returns patch referring to coeffs of child in parent box.
Definition mraimpl.h:696
void print_tree_graphviz(std::ostream &os=std::cout, Level maxlevel=10000) const
Definition mraimpl.h:2799
std::size_t min_nodes() const
Returns the min number of nodes on a processor.
Definition mraimpl.h:1902
void make_redundant(const bool fence)
convert this to redundant, i.e. have sum coefficients on all levels
Definition mraimpl.h:1549
std::size_t max_nodes() const
Returns the max number of nodes on a processor.
Definition mraimpl.h:1893
coeffT upsample(const keyT &key, const coeffT &coeff) const
upsample the sum coefficients of level 1 to sum coeffs on level n+1
Definition mraimpl.h:1245
void flo_unary_op_node_inplace(const opT &op, bool fence)
Definition funcimpl.h:2313
std::size_t size_local() const
Returns the number of coefficients in the function for each rank.
Definition mraimpl.h:1920
void plot_cube_kernel(archive::archive_ptr< Tensor< T > > ptr, const keyT &key, const coordT &plotlo, const coordT &plothi, const std::vector< long > &npt, bool eval_refine) const
Definition mraimpl.h:3470
T trace_local() const
Returns int(f(x),x) in local volume.
Definition mraimpl.h:3305
void print_grid(const std::string filename) const
Definition mraimpl.h:542
bool get_autorefine() const
Definition mraimpl.h:334
void phi_for_mul(Level np, Translation lp, Level nc, Translation lc, Tensor< double > &phi) const
Compute the Legendre scaling functions for multiplication.
Definition mraimpl.h:3273
Future< std::pair< keyT, coeffT > > find_me(const keyT &key) const
find_me. Called by diff_bdry to get coefficients of boundary function
Definition mraimpl.h:3391
TensorType get_tensor_type() const
Definition mraimpl.h:319
void remove_leaf_coefficients(const bool fence)
Definition mraimpl.h:1543
void insert_zero_down_to_initial_level(const keyT &key)
Initialize nodes to zero function at initial_level of refinement.
Definition mraimpl.h:2622
void do_diff1(const DerivativeBase< T, NDIM > *D, const implT *f, const keyT &key, const std::pair< keyT, coeffT > &left, const std::pair< keyT, coeffT > &center, const std::pair< keyT, coeffT > &right)
Definition mraimpl.h:943
void do_print_tree_json(const keyT &key, std::multimap< Level, std::tuple< tranT, std::string > > &data, Level maxlevel) const
Functor for the do_print_tree_json method.
Definition mraimpl.h:2777
void finalize_sum()
after summing up we need to do some cleanup;
Definition mraimpl.h:1850
bool exists_and_is_leaf(const keyT &key) const
Definition mraimpl.h:1289
void verify_tree() const
Verify tree is properly constructed ... global synchronization involved.
Definition mraimpl.h:111
void set_tensor_args(const TensorArgs &t)
Definition mraimpl.h:325
std::size_t real_size() const
Returns the number of coefficients in the function ... collective global sum.
Definition mraimpl.h:1938
bool exists_and_has_children(const keyT &key) const
Definition mraimpl.h:1284
void sum_down_spawn(const keyT &key, const coeffT &s)
is this the same as trickle_down() ?
Definition mraimpl.h:876
keyT neighbor(const keyT &key, const keyT &disp, const array_of_bools< NDIM > &is_periodic) const
Returns key of general neighbor enforcing BC.
Definition mraimpl.h:3361
void norm_tree(bool fence)
compute for each FunctionNode the norm of the function inside that node
Definition mraimpl.h:1568
bool has_leaves() const
Definition mraimpl.h:288
bool verify_parents_and_children() const
check that parents and children are consistent
Definition mraimpl.h:119
void reconstruct_op(const keyT &key, const coeffT &s, const bool accumulate_NS=true)
Definition mraimpl.h:2121
const coeffT parent_to_child(const coeffT &s, const keyT &parent, const keyT &child) const
Directly project parent coeffs to child coeffs.
Definition mraimpl.h:3288
void undo_redundant(const bool fence)
convert this from redundant to standard reconstructed form
Definition mraimpl.h:1559
const keyT & key0() const
Returns cdata.key0.
Definition mraimpl.h:394
double finalize_apply()
after apply we need to do some cleanup;
Definition mraimpl.h:1807
const dcT & get_coeffs() const
Definition mraimpl.h:343
compressT make_redundant_op(const keyT &key, const std::vector< Future< compressT > > &v)
similar to compress_op, but insert only the sum coefficients in the tree
Definition mraimpl.h:1752
double norm2sq_local() const
Returns the square of the local norm ... no comms.
Definition mraimpl.h:1859
const FunctionCommonData< T, NDIM > & get_cdata() const
Definition mraimpl.h:349
void sum_down(bool fence)
After 1d push operator must sum coeffs down the tree to restore correct scaling function coefficients...
Definition mraimpl.h:922
bool noautorefine(const keyT &key, const tensorT &t) const
Always returns false (for when autorefine is not wanted)
Definition mraimpl.h:859
double truncate_tol(double tol, const keyT &key) const
Returns the truncation threshold according to truncate_method.
Definition mraimpl.h:649
bool autorefine_square_test(const keyT &key, const nodeT &t) const
Returns true if this block of coeffs needs autorefining.
Definition mraimpl.h:865
void erase(const Level &max_level)
truncate tree at a certain level
Definition mraimpl.h:739
std::pair< coeffT, std::pair< double, double > > compressT
s coefficients plus the (snorm_tree, dnorm_tree) pair propagated up by compress
Definition funcimpl.h:4788
void reconstruct(bool fence)
reconstruct this tree – respects fence
Definition mraimpl.h:1489
void multiply(const implT *f, const FunctionImpl< T, LDIM > *g, const int particle)
multiply f (a pair function of NDIM) with an orbital g (LDIM=NDIM/2)
Definition funcimpl.h:3786
coeffT assemble_coefficients(const keyT &key, const coeffT &coeff_ket, const coeffT &vpotential1, const coeffT &vpotential2, const tensorT &veri) const
given several coefficient tensors, assemble a result tensor
Definition mraimpl.h:1027
static void tnorm(const tensorT &t, double *lo, double *hi)
Computes norm of low/high-order polyn. coeffs for autorefinement test.
Definition mraimpl.h:3165
std::pair< bool, T > eval_local_only(const Vector< double, NDIM > &xin, Level maxlevel)
Evaluate function only if point is local returning (true,value); otherwise return (false,...
Definition mraimpl.h:2960
std::size_t max_depth() const
Returns the maximum depth of the tree ... collective ... global sum/broadcast.
Definition mraimpl.h:1885
std::size_t size() const
Returns the number of coefficients in the function ... collective global sum.
Definition mraimpl.h:1930
void reduce_rank(const double thresh, bool fence)
reduce the rank of the coefficients tensors
Definition mraimpl.h:1121
std::shared_ptr< FunctionFunctorInterface< T, NDIM > > get_functor()
Definition mraimpl.h:300
tensorT unfilter(const tensorT &s) const
Transform sums+differences at level n to sum coefficients at level n+1.
Definition mraimpl.h:1195
Tensor< T > eval_plot_cube(const coordT &plotlo, const coordT &plothi, const std::vector< long > &npt, const bool eval_refine=false) const
Definition mraimpl.h:3562
void change_tree_state(const TreeState finalstate, bool fence=true)
change the tree state of this function, might or might not respect fence!
Definition mraimpl.h:1421
Future< coeffT > truncate_reconstructed_spawn(const keyT &key, const double tol)
truncate using a tree in reconstructed form
Definition mraimpl.h:1614
void map_and_mirror(const implT &f, const std::vector< long > &map, const std::vector< long > &mirror, bool fence)
map and mirror the translation index and the coefficients, result on this
Definition mraimpl.h:1090
void truncate(double tol, bool fence)
Truncate according to the threshold with optional global fence.
Definition mraimpl.h:378
bool is_reconstructed() const
Returns true if the function is compressed.
Definition mraimpl.h:256
double norm_tree_op(const keyT &key, const std::vector< Future< double > > &v)
Definition mraimpl.h:1576
void reset_timer()
Definition mraimpl.h:366
void refine_to_common_level(const std::vector< FunctionImpl< T, NDIM > * > &v, const std::vector< tensorT > &c, const keyT key)
Refine multiple functions down to the same finest level.
Definition mraimpl.h:769
int get_k() const
Definition mraimpl.h:340
void eval(const Vector< double, NDIM > &xin, const keyT &keyin, const typename Future< T >::remote_refT &ref)
Evaluate the function at a point in simulation coordinates.
Definition mraimpl.h:2916
bool truncate_op(const keyT &key, double tol, const std::vector< Future< bool > > &v)
Definition mraimpl.h:2689
void zero_norm_tree()
Definition mraimpl.h:1306
std::size_t max_local_depth() const
Returns the maximum local depth of the tree ... no communications.
Definition mraimpl.h:1871
tensorT project(const keyT &key) const
Definition mraimpl.h:2834
double check_symmetry_local() const
Returns some asymmetry measure ... no comms.
Definition mraimpl.h:755
Future< double > get_norm_tree_recursive(const keyT &key) const
Definition mraimpl.h:2855
bool is_redundant_after_merge() const
Returns true if the function is redundant_after_merge.
Definition mraimpl.h:268
bool is_on_demand() const
Definition mraimpl.h:283
void accumulate_timer(const double time) const
Definition mraimpl.h:352
void trickle_down_op(const keyT &key, const coeffT &s)
sum all the contributions from all scales after applying an operator in mod-NS form
Definition mraimpl.h:1379
void set_thresh(double value)
Definition mraimpl.h:331
Tensor< double > print_plane_local(const int xaxis, const int yaxis, const coordT &el2)
collect the data for a plot of the MRA structure locally on each node
Definition mraimpl.h:423
void sock_it_to_me_too(const keyT &key, const RemoteReference< FutureImpl< std::pair< keyT, coeffT > > > &ref) const
Definition mraimpl.h:2894
void broaden_op(const keyT &key, const std::vector< Future< bool > > &v)
Definition mraimpl.h:1295
void print_plane(const std::string filename, const int xaxis, const int yaxis, const coordT &el2)
Print a plane ("xy", "xz", or "yz") containing the point x to file.
Definition mraimpl.h:403
void print_tree(std::ostream &os=std::cout, Level maxlevel=10000) const
Definition mraimpl.h:2717
void project_refine_op(const keyT &key, bool do_refine, const std::vector< Vector< double, NDIM > > &specialpts)
Definition mraimpl.h:2500
std::size_t tree_size() const
Returns the size of the tree structure of the function ... collective global sum.
Definition mraimpl.h:1911
void add_scalar_inplace(T t, bool fence)
Adds a constant to the function. Local operation, optional fence.
Definition mraimpl.h:2581
tensorT downsample(const keyT &key, const std::vector< Future< coeffT > > &v) const
downsample the sum coefficients of level n+1 to sum coeffs on level n
Definition mraimpl.h:1215
void abs_square_inplace(bool fence)
Definition mraimpl.h:3268
void put_in_box(ProcessID from, long nl, long ni) const
Definition mraimpl.h:824
TensorArgs get_tensor_args() const
Definition mraimpl.h:322
void average(const implT &rhs)
take the average of two functions, similar to: this=0.5*(this+rhs)
Definition mraimpl.h:1102
void diff(const DerivativeBase< T, NDIM > *D, const implT *f, bool fence)
Definition mraimpl.h:955
void square_inplace(bool fence)
Pointwise squaring of function with optional global fence.
Definition mraimpl.h:3257
void remove_internal_coefficients(const bool fence)
Definition mraimpl.h:1538
void compute_snorm_and_dnorm(bool fence=true)
compute norm of s and d coefficients for all nodes
Definition mraimpl.h:1145
void standard(bool fence)
Changes non-standard compressed form to standard compressed form.
Definition mraimpl.h:1794
bool is_nonstandard_with_leaves() const
Definition mraimpl.h:278
FunctionNode holds the coefficients, etc., at each node of the 2^NDIM-tree.
Definition funcimpl.h:136
bool has_coeff() const
Returns true if there are coefficients in this node.
Definition funcimpl.h:210
size_t size() const
Returns the number of coefficients in this node.
Definition funcimpl.h:252
void clear_coeff()
Clears the coefficients (has_coeff() will subsequently return false)
Definition funcimpl.h:305
bool is_leaf() const
Returns true if this does not have children.
Definition funcimpl.h:223
void set_has_children(bool flag)
Sets has_children attribute to value of flag.
Definition funcimpl.h:264
double get_norm_tree() const
Gets the value of norm_tree.
Definition funcimpl.h:326
void set_snorm(const double sn)
set the precomputed norm of the (virtual) s coefficients
Definition funcimpl.h:341
void set_is_leaf(bool flag)
Sets has_children attribute to value of !flag.
Definition funcimpl.h:290
void print_json(std::ostream &s) const
Definition funcimpl.h:487
void set_dnorm_tree(double dnorm_tree)
Sets the value of dnorm_tree.
Definition funcimpl.h:321
bool has_children() const
Returns true if this node has children.
Definition funcimpl.h:217
void set_coeff(const coeffT &coeffs)
Takes a shallow copy of the coeff — same as this->coeff()=coeff.
Definition funcimpl.h:295
void set_dnorm(const double dn)
set the precomputed norm of the (virtual) d coefficients
Definition funcimpl.h:346
coeffT & coeff()
Returns a non-const reference to the tensor containing the coeffs.
Definition funcimpl.h:237
void set_norm_tree(double norm_tree)
Sets the value of norm_tree.
Definition funcimpl.h:316
A multiresolution adaptive numerical function.
Definition mra.h:144
Implements the functionality of futures.
Definition future.h:75
A future is a possibly yet unevaluated value.
Definition future.h:370
T & get(bool dowork=true) &
Gets the value, waiting if necessary.
Definition future.h:571
remote_refT remote_ref(World &world) const
Returns a structure used to pass references to another process.
Definition future.h:672
void set(const Future< T > &other)
A.set(B), where A and B are futures ensures A has/will have the same value as B.
Definition future.h:505
Definition lowranktensor.h:59
GenTensor convert(const TensorArgs &targs) const
Definition gentensor.h:198
long dim(const int i) const
return the number of entries in dimension i
Definition lowranktensor.h:391
bool has_no_data() const
Definition gentensor.h:211
size_t real_size() const
Definition gentensor.h:214
GenTensor< T > & emul(const GenTensor< T > &other)
Inplace multiply by corresponding elements of argument Tensor.
Definition lowranktensor.h:637
float_scalar_type normf() const
Definition lowranktensor.h:406
void reduce_rank(const double &eps)
Definition gentensor.h:217
long rank() const
Definition gentensor.h:212
const Tensor< T > & full_tensor() const
Definition gentensor.h:200
size_t nCoeff() const
Definition gentensor.h:215
TensorType tensor_type() const
Definition gentensor.h:221
GenTensor & gaxpy(const T alpha, const GenTensor &other, const T beta)
Definition lowranktensor.h:586
IsSupported< TensorTypeData< Q >, GenTensor< T > & >::type scale(Q fac)
Inplace multiplication by scalar of supported type (legacy name)
Definition lowranktensor.h:426
Definition worldhashmap.h:330
Definition indexit.h:141
Iterates in lexical order thru all children of a key.
Definition key.h:548
Key is the index for a node of the 2^NDIM-tree.
Definition key.h:70
Level level() const
Definition key.h:169
bool is_neighbor_of(const Key &key, const array_of_bools< NDIM > &bperiodic) const
Assuming keys are at the same level, returns true if displaced by no more than 1 in any direction.
Definition key.h:325
bool thisKeyContains(const Vector< double, NDIM > &x, const unsigned int &dim0, const unsigned int &dim1) const
check if this MultiIndex contains point x, disregarding these two dimensions
Definition key.h:400
bool is_invalid() const
Checks if a key is invalid.
Definition key.h:119
Key parent(int generation=1) const
Returns the key of the parent.
Definition key.h:290
const Vector< Translation, NDIM > & translation() const
Definition key.h:174
Range, vaguely a la Intel TBB, to encapsulate a random-access, STL-like start and end iterator with c...
Definition range.h:64
Simple structure used to manage references/pointers to remote instances.
Definition worldref.h:395
double weights(const unsigned int &i) const
return the weight
Definition srconf.h:671
const Tensor< T > flat_vector(const unsigned int &idim) const
return shallow copy of a slice of one of the vectors, flattened to (r,kVec)
Definition srconf.h:545
Definition SVDTensor.h:42
long rank() const
Definition SVDTensor.h:77
A slice defines a sub-range or patch of a dimension.
Definition slice.h:103
static TaskAttributes hipri()
Definition thread.h:456
static TaskAttributes generator()
Definition thread.h:448
A tensor is a multidimensional array.
Definition tensor.h:318
T sum() const
Returns the sum of all elements of the tensor.
Definition tensor.h:1663
Tensor< T > reshape(int ndimnew, const long *d)
Returns new view/tensor reshaping size/number of dimensions to conforming tensor.
Definition tensor.h:1385
T * ptr()
Returns a pointer to the internal data.
Definition tensor.h:1841
IsSupported< TensorTypeData< Q >, Tensor< T > & >::type scale(Q x)
Inplace multiplication by scalar of supported type (legacy name)
Definition tensor.h:687
Tensor< T > & emul(const Tensor< T > &t)
Inplace multiply by corresponding elements of argument Tensor.
Definition tensor.h:1800
Iterator for distributed container wraps the local iterator.
Definition worlddc.h:386
void fence(bool debug=false)
Synchronizes all processes in communicator AND globally ensures no pending AM or tasks.
Definition worldgop.cc:176
A parallel world class.
Definition world.h:134
ProcessID rank() const
Returns the process rank in this World (same as MPI_Comm_rank()).
Definition world.h:344
WorldGopInterface & gop
Global operations.
Definition world.h:216
ProcessID nproc() const
Returns the number of processes in this World (same as MPI_Comm_size()).
Definition world.h:349
Wrapper for an opaque pointer for serialization purposes.
Definition archive.h:851
syntactic sugar for std::array<bool, N>
Definition array_of_bools.h:19
char * p(char *buf, const char *name, int k, int initial_level, double thresh, int order)
Definition derivatives.cc:72
const std::size_t bufsize
Definition derivatives.cc:16
static double lo
Definition dirac-hatom.cc:23
static bool debug
Definition dirac-hatom.cc:16
Provides FunctionCommonData, FunctionImpl and FunctionFactory.
static double function(const coord_3d &r)
Normalized gaussian.
Definition functionio.cc:100
Tensor< TENSOR_RESULT_TYPE(T, Q) > & fast_transform(const Tensor< T > &t, const Tensor< Q > &c, Tensor< TENSOR_RESULT_TYPE(T, Q) > &result, Tensor< TENSOR_RESULT_TYPE(T, Q) > &workspace)
Restricted but heavily optimized form of transform()
Definition tensor.h:2460
Tensor< T > transpose(const Tensor< T > &t)
Returns a new deep copy of the transpose of the input tensor.
Definition tensor.h:2035
const double beta
Definition gygi_soltion.cc:62
static const double v
Definition hatom_sf_dirac.cc:20
Tensor< double > op(const Tensor< double > &x)
Definition kain.cc:508
static double pow(const double *a, const double *b)
Definition lda.h:74
Macros and tools pertaining to the configuration of MADNESS.
#define MADNESS_PRAGMA_CLANG(x)
Definition madness_config.h:200
#define MADNESS_CHECK(condition)
Check a condition — even in a release build the condition is always evaluated so it can have side eff...
Definition madness_exception.h:182
#define MADNESS_EXCEPTION(msg, value)
Macro for throwing a MADNESS exception.
Definition madness_exception.h:119
#define MADNESS_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
static const bool VERIFY_TREE
Definition mra.h:57
Definition potentialmanager.cc:41
Namespace for all elements and tools of MADNESS.
Definition DFParameters.h:10
bool two_scale_hg(int k, Tensor< double > *hg)
Definition twoscale.cc:151
@ BC_FREE
Definition bc.h:53
void make_redundant(World &world, const std::vector< Function< T, NDIM > > &v, bool fence=true)
change tree_state of a vector of functions to redundant
Definition vmra.h:186
static const char * filename
Definition legendre.cc:96
static const std::vector< Slice > ___
Entire dimension.
Definition slice.h:128
static bool enforce_in_volume(Level n, const Translation &l)
Definition mraimpl.h:3352
double abs(double x)
Definition complexfun.h:48
static double cpu_time()
Returns the cpu time in seconds relative to an arbitrary origin.
Definition timers.h:128
Vector< double, 3 > coordT
Definition corepotential.cc:54
GenTensor< TENSOR_RESULT_TYPE(R, Q)> general_transform(const GenTensor< R > &t, const Tensor< Q > c[])
Definition gentensor.h:274
void legendre_scaling_functions(double x, long k, double *p)
Evaluate the first k Legendre scaling functions.
Definition legendre.cc:85
void norm_tree(World &world, const std::vector< Function< T, NDIM > > &v, bool fence=true)
Makes the norm tree for all functions in a vector.
Definition vmra.h:1252
std::vector< Function< TENSOR_RESULT_TYPE(T, R), NDIM > > transform(World &world, const std::vector< Function< T, NDIM > > &v, const Tensor< R > &c, bool fence=true)
Transforms a vector of functions according to new[i] = sum[j] old[j]*c[j,i].
Definition vmra.h:757
static Key< NDIM > simpt2key(const Vector< T, NDIM > &pt, Level n)
Definition funcdefaults.h:462
TreeState
Definition funcdefaults.h:59
@ nonstandard_after_apply
s and d coeffs, state after operator application
Definition funcdefaults.h:64
@ on_demand
no coeffs anywhere, but a functor providing if necessary
Definition funcdefaults.h:67
@ redundant_after_merge
s coeffs everywhere, must be summed up to yield the result
Definition funcdefaults.h:66
@ reconstructed
s coeffs at the leaves only
Definition funcdefaults.h:60
@ nonstandard
s and d coeffs in internal nodes
Definition funcdefaults.h:62
@ unknown
Definition funcdefaults.h:68
@ compressed
d coeffs in internal nodes, s and d coeffs at the root, empty leaves may be present
Definition funcdefaults.h:61
@ redundant
s coeffs everywhere
Definition funcdefaults.h:65
@ nonstandard_with_leaves
like nonstandard, with s coeffs at the leaves
Definition funcdefaults.h:63
static void user_to_sim(const Vector< double, NDIM > &xuser, Vector< double, NDIM > &xsim)
Convert user coords (cell[][]) to simulation coords ([0,1]^ndim)
Definition funcdefaults.h:453
void standard(World &world, std::vector< Function< T, NDIM > > &v, bool fence=true)
Generates standard form of a vector of functions.
Definition vmra.h:243
Tensor< double > tensorT
Definition distpm.cc:21
void compress(World &world, const std::vector< Function< T, NDIM > > &v, bool fence=true)
Compress a vector of functions.
Definition vmra.h:149
const std::vector< Function< T, NDIM > > & reconstruct(const std::vector< Function< T, NDIM > > &v)
reconstruct a vector of functions
Definition vmra.h:162
int64_t Translation
Definition key.h:58
Tensor< TENSOR_RESULT_TYPE(T, Q)> & general_fast_transform(const Tensor< T > &t, const Tensor< Q > *c, Tensor< TENSOR_RESULT_TYPE(T, Q)> &result, Tensor< TENSOR_RESULT_TYPE(T, Q)> &workspace)
Definition tensor.h:2525
void plotdx(const Function< T, NDIM > &f, const char *filename, const Tensor< double > &cell=FunctionDefaults< NDIM >::get_cell(), const std::vector< long > &npt=std::vector< long >(NDIM, 201L), bool binary=true)
Writes an OpenDX format file with a cube/slice of points on a uniform grid.
Definition mraimpl.h:3598
Function< T, NDIM > mirror(const Function< T, NDIM > &f, const std::vector< long > &mirrormap, bool fence=true)
Generate a new function by mirroring within the dimensions .. optional fence.
Definition mra.h:2474
static void verify_tree(World &world, const std::vector< Function< T, NDIM > > &v)
Definition SCF.cc:74
static const Slice _(0,-1, 1)
void change_tensor_type(GenTensor< T > &t, const TensorArgs &targs)
change representation to targ.tt
Definition gentensor.h:284
int Level
Definition key.h:59
std::enable_if< std::is_base_of< ProjectorBase, projT >::value, OuterProjector< projT, projQ > >::type outer(const projT &p0, const projQ &p1)
Definition projector.h:457
TreeState get_tree_state(const Function< T, NDIM > &f)
get tree state of a function
Definition mra.h:2935
bool gauss_legendre(int n, double xlo, double xhi, double *x, double *w)
Definition legendre.cc:226
static double pop(std::vector< double > &v)
Definition SCF.cc:115
void print(const T &t, const Ts &... ts)
Print items to std::cout (items separated by spaces) and terminate with a new line.
Definition print.h:227
Tensor< T > fcube(const Key< NDIM > &, T(*f)(const Vector< double, NDIM > &), const Tensor< double > &)
Definition mraimpl.h:2175
TensorType
low rank representations of tensors (see gentensor.h)
Definition gentensor.h:120
@ TT_2D
Definition gentensor.h:120
@ TT_FULL
Definition gentensor.h:120
static void dxprintvalue(FILE *f, const double t)
Definition mraimpl.h:3589
void refine(World &world, const std::vector< Function< T, NDIM > > &vf, bool fence=true)
refine the functions according to the autorefine criteria
Definition vmra.h:196
NDIM & f
Definition mra.h:2622
void error(const char *msg)
Definition world.cc:147
const Function< T, NDIM > & change_tree_state(const Function< T, NDIM > &f, const TreeState finalstate, bool fence=true)
change tree state of a function
Definition mra.h:2948
NDIM const Function< R, NDIM > & g
Definition mra.h:2622
double wall_time()
Returns the wall time in seconds relative to an arbitrary origin.
Definition timers.cc:48
static bool print_timings
Definition SCF.cc:106
constexpr Vector< T, sizeof...(Ts)+1 > vec(T t, Ts... ts)
Factory function for creating a madness::Vector.
Definition vector.h:750
Function< T, NDIM > multiply(const Function< T, NDIM > f, const Function< T, LDIM > g, const int particle, const bool fence=true)
multiply a high-dimensional function with a low-dimensional function
Definition mra.h:2575
void scale(World &world, std::vector< Function< T, NDIM > > &v, const std::vector< Q > &factors, bool fence=true)
Scales inplace a vector of functions by distinct values.
Definition vmra.h:873
static std::vector< double > ttt
Definition SCF.cc:107
Function< T, NDIM > project(const Function< T, NDIM > &other, int k=FunctionDefaults< NDIM >::get_k(), double thresh=FunctionDefaults< NDIM >::get_thresh(), bool fence=true)
Definition mra.h:2605
static XNonlinearSolver< std::vector< Function< T, NDIM > >, T, vector_function_allocator< T, NDIM > > nonlinear_vector_solver(World &world, const long nvec)
Definition nonlinsol.h:371
std::string name(const FuncType &type, const int ex=-1)
Definition ccpairfunction.h:28
Function< T, NDIM > copy(const Function< T, NDIM > &f, const std::shared_ptr< WorldDCPmapInterface< Key< NDIM > > > &pmap, bool fence=true)
Create a new copy of the function with different distribution and optional fence.
Definition mra.h:2187
static const int MAXK
The maximum wavelet order presently supported.
Definition funcdefaults.h:54
static bool enforce_bc(bool is_periodic, Level n, Translation &l)
Definition mraimpl.h:3332
Definition mraimpl.h:51
bool isnan(const std::complex< T > &v)
Definition mraimpl.h:53
const double mu
Definition navstokes_cosines.cc:95
static const double b
Definition nonlinschro.cc:119
static const double d
Definition nonlinschro.cc:121
static const double a
Definition nonlinschro.cc:118
static const double c
Definition relops.cc:10
static const double m
Definition relops.cc:9
static const double L
Definition rk.cc:46
static const double thresh
Definition rk.cc:45
static const long k
Definition rk.cc:44
const double xi
Exponent for delta function approx.
Definition siam_example.cc:60
Definition test_ar.cc:204
Definition test_dc.cc:47
Definition test_ccpairfunction.cc:22
add two functions f and g: result=alpha * f + beta * g
Definition funcimpl.h:3725
"put" this on g
Definition funcimpl.h:2738
change representation of nodes' coeffs to low rank, optional fence
Definition funcimpl.h:2771
check symmetry wrt particle exchange
Definition funcimpl.h:2444
compute the norm of the wavelet coefficients
Definition funcimpl.h:4622
mirror dimensions of this, write result on f
Definition funcimpl.h:2672
map this on f
Definition funcimpl.h:2592
mirror dimensions of this, write result on f
Definition funcimpl.h:2622
Definition funcimpl.h:5723
reduce the rank of the nodes, optional fence
Definition funcimpl.h:2418
Changes non-standard compressed form to standard compressed form.
Definition funcimpl.h:4845
remove all coefficients of internal nodes
Definition funcimpl.h:2364
remove all coefficients of leaf nodes
Definition funcimpl.h:2381
Definition funcimpl.h:4694
shallow-copy, pared-down version of FunctionNode, for special purpose only
Definition funcimpl.h:772
TensorArgs holds the arguments for creating a LowRankTensor.
Definition gentensor.h:134
double thresh
Definition gentensor.h:135
Definition mraimpl.h:3237
void operator()(const Key< NDIM > &key, Tensor< T > &t) const
Definition mraimpl.h:3238
void serialize(Archive &ar)
Definition mraimpl.h:3239
Definition mraimpl.h:3243
void operator()(const Key< NDIM > &key, Tensor< T > &t) const
Definition mraimpl.h:3244
void serialize(Archive &ar)
Definition mraimpl.h:3245
Definition mraimpl.h:3205
void operator()(const A &a, const B &b) const
Definition mraimpl.h:3206
void serialize(Archive &ar)
Definition mraimpl.h:3208
Definition mraimpl.h:3212
void operator()(const Key< NDIM > &key, FunctionNode< T, NDIM > &node) const
Definition mraimpl.h:3220
void serialize(Archive &ar)
Definition mraimpl.h:3223
T q
Definition mraimpl.h:3213
scaleinplace()
Definition mraimpl.h:3214
scaleinplace(T q)
Definition mraimpl.h:3216
void operator()(const Key< NDIM > &key, Tensor< T > &t) const
Definition mraimpl.h:3217
Definition mraimpl.h:3229
void serialize(Archive &ar)
Definition mraimpl.h:3233
void operator()(const Key< NDIM > &key, Tensor< T > &t) const
Definition mraimpl.h:3230
insert/replaces the coefficients into the function
Definition funcimpl.h:715
Definition lowrankfunction.h:336
int np
Definition tdse1d.cc:165
static const double s0
Definition tdse4.cc:83
AtomicInt sum
Definition test_atomicint.cc:46
int me
Definition test_binsorter.cc:10
double norm(const T i1)
Definition test_cloud.cc:85
int task(int i)
Definition test_runtime.cpp:4
void e()
Definition test_sig.cc:75
static double g1(const Vector< double, D > &r)
Definition test_state_archive_hdf5.cpp:34
static double g0(const Vector< double, D > &r)
Definition test_state_archive_hdf5.cpp:31
#define N
Definition testconv.cc:37
static const double alpha
Definition testcosine.cc:10
static const int truncate_mode
Definition testcosine.cc:14
double cell_volume()
Definition testgconv.cc:86
constexpr std::size_t NDIM
Definition testgconv.cc:54
double h(const coord_1d &r)
Definition testgconv.cc:175
std::size_t axis
Definition testpdiff.cc:59
double k0
Definition testperiodic.cc:66
#define TENSOR_RESULT_TYPE(L, R)
This macro simplifies access to TensorResultType.
Definition type_data.h:205
Defines and implements WorldObject.
Implements WorldContainer.
Defines and implements a concurrent hashmap.
#define PROFILE_FUNC
Definition worldprofile.h:209
#define PROFILE_MEMBER_FUNC(classname)
Definition worldprofile.h:210
#define PROFILE_BLOCK(name)
Definition worldprofile.h:208
int ProcessID
Used to clearly identify process number/rank.
Definition worldtypes.h:43
Key< D > keyT
Definition writecoeff2.cc:11
void test()
Definition y.cc:696