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