fmm-wrapper.txx 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831
  1. #include SCTL_INCLUDE(kernel_functions.hpp)
  2. #include SCTL_INCLUDE(vector.hpp)
  3. #include SCTL_INCLUDE(matrix.hpp)
  4. #ifdef SCTL_HAVE_PVFMM
  5. #include <pvfmm.hpp>
  6. #endif
  7. namespace SCTL_NAMESPACE {
  8. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::test(const Comm& comm) {
  9. if (DIM != 3) return ParticleFMM<Real,3>::test(comm);
  10. Stokes3D_FSxU kernel_m2l;
  11. Stokes3D_FxU kernel_sl;
  12. Stokes3D_DxU kernel_dl;
  13. srand48(comm.Rank());
  14. // Create target and source vectors.
  15. const Long N = 5000/comm.Size();
  16. Vector<Real> trg_coord(N*DIM);
  17. Vector<Real> sl_coord(N*DIM);
  18. Vector<Real> dl_coord(N*DIM);
  19. Vector<Real> dl_norml(N*DIM);
  20. for (auto& a : trg_coord) a = (Real)(drand48()-0.5);
  21. for (auto& a : sl_coord) a = (Real)(drand48()-0.5);
  22. for (auto& a : dl_coord) a = (Real)(drand48()-0.5);
  23. for (auto& a : dl_norml) a = (Real)(drand48()-0.5);
  24. Long n_sl = sl_coord.Dim()/DIM;
  25. Long n_dl = dl_coord.Dim()/DIM;
  26. // Set source charges.
  27. Vector<Real> sl_den(n_sl*kernel_sl.SrcDim());
  28. Vector<Real> dl_den(n_dl*kernel_dl.SrcDim());
  29. for (auto& a : sl_den) a = (Real)(drand48() - 0.5);
  30. for (auto& a : dl_den) a = (Real)(drand48() - 0.5);
  31. ParticleFMM fmm(comm);
  32. fmm.SetAccuracy(10);
  33. fmm.SetKernels(kernel_m2l, kernel_m2l, kernel_sl);
  34. fmm.AddTrg("Potential", kernel_m2l, kernel_sl);
  35. fmm.AddSrc("SingleLayer", kernel_sl, kernel_sl);
  36. fmm.AddSrc("DoubleLayer", kernel_dl, kernel_dl);
  37. fmm.SetKernelS2T("SingleLayer", "Potential",kernel_sl);
  38. fmm.SetKernelS2T("DoubleLayer", "Potential",kernel_dl);
  39. fmm.SetTrgCoord("Potential", trg_coord);
  40. fmm.SetSrcCoord("SingleLayer", sl_coord);
  41. fmm.SetSrcCoord("DoubleLayer", dl_coord, dl_norml);
  42. fmm.SetSrcDensity("SingleLayer", sl_den);
  43. fmm.SetSrcDensity("DoubleLayer", dl_den);
  44. Vector<Real> Ufmm, Uref;
  45. fmm.Eval(Ufmm, "Potential"); // Warm-up run
  46. Ufmm = 0;
  47. Profile::Enable(true);
  48. Profile::Tic("FMM-Eval", &comm);
  49. fmm.Eval(Ufmm, "Potential");
  50. Profile::Toc();
  51. Profile::Tic("Direct", &comm);
  52. fmm.EvalDirect(Uref, "Potential");
  53. Profile::Toc();
  54. Profile::print(&comm);
  55. Vector<Real> Uerr = Uref - Ufmm;
  56. { // Print error
  57. StaticArray<Real,2> loc_err{0,0}, glb_err{0,0};
  58. for (const auto& a : Uerr) loc_err[0] = std::max<Real>(loc_err[0], fabs(a));
  59. for (const auto& a : Uref) loc_err[1] = std::max<Real>(loc_err[1], fabs(a));
  60. comm.Allreduce<Real>(loc_err, glb_err, 2, Comm::CommOp::MAX);
  61. if (!comm.Rank()) std::cout<<"Maximum relative error: "<<glb_err[0]/glb_err[1]<<'\n';
  62. }
  63. }
  64. template <Integer DIM, class Real> void BoundingBox(StaticArray<Real,DIM*2>& bbox, const Vector<Real>& X, const Comm& comm) {
  65. const Long N = X.Dim()/DIM;
  66. SCTL_ASSERT(X.Dim() == N * DIM);
  67. StaticArray<Real, DIM> bbox0, bbox1;
  68. { // Init bbox0, bbox1
  69. for (Integer k = 0; k < DIM; k++) {
  70. bbox0[k] = bbox1[k] = (N ? X[k] : (Real)0);
  71. }
  72. // Handle (N == 0) case
  73. StaticArray<Real, DIM> bbox0_glb, bbox1_glb;
  74. comm.Allreduce((ConstIterator<Real>)bbox0, (Iterator<Real>)bbox0_glb, DIM, Comm::CommOp::MIN);
  75. comm.Allreduce((ConstIterator<Real>)bbox1, (Iterator<Real>)bbox1_glb, DIM, Comm::CommOp::MAX);
  76. for (Integer k = 0; k < DIM; k++) {
  77. bbox0[k] = bbox1[k] = (fabs(bbox0_glb[k]) > fabs(bbox1_glb[k]) ? bbox0_glb[k] : bbox1_glb[k]);
  78. }
  79. }
  80. for (Long i = 0; i < N; i++) {
  81. for (Integer k = 0; k < DIM; k++) {
  82. bbox0[k] = std::min<Real>(bbox0[k], X[i*DIM+k]);
  83. bbox1[k] = std::max<Real>(bbox1[k], X[i*DIM+k]);
  84. }
  85. }
  86. StaticArray<Real, DIM> bbox0_glb, bbox1_glb;
  87. comm.Allreduce((ConstIterator<Real>)bbox0, (Iterator<Real>)bbox0_glb, DIM, Comm::CommOp::MIN);
  88. comm.Allreduce((ConstIterator<Real>)bbox1, (Iterator<Real>)bbox1_glb, DIM, Comm::CommOp::MAX);
  89. for (Integer k = 0; k < DIM; k++) {
  90. bbox[k*2+0] = bbox0_glb[k];
  91. bbox[k*2+1] = bbox1_glb[k];
  92. }
  93. }
  94. template <class Real, Integer DIM> ParticleFMM<Real,DIM>::ParticleFMM(const Comm& comm) : comm_(comm), digits_(10) {
  95. fmm_ker.ker_m2m = NullIterator<char>();
  96. fmm_ker.ker_m2l = NullIterator<char>();
  97. fmm_ker.ker_l2l = NullIterator<char>();
  98. }
  99. template <class Real, Integer DIM> ParticleFMM<Real,DIM>::~ParticleFMM() {
  100. Vector<std::string> src_lst, trg_lst;
  101. for (auto& it : src_map) src_lst.PushBack(it.first);
  102. for (auto& it : trg_map) trg_lst.PushBack(it.first);
  103. for (const auto& name : src_lst) DeleteSrc(name);
  104. for (const auto& name : trg_lst) DeleteTrg(name);
  105. Vector<std::pair<std::string,std::string>> s2t_lst;
  106. for (auto& it : s2t_map) s2t_lst.PushBack(it.first);
  107. for (const auto& name : s2t_lst) DeleteS2T(name.first, name.second);
  108. if (fmm_ker.ker_m2m != NullIterator<char>()) fmm_ker.delete_ker_m2m(fmm_ker.ker_m2m);
  109. if (fmm_ker.ker_m2l != NullIterator<char>()) fmm_ker.delete_ker_m2l(fmm_ker.ker_m2l);
  110. if (fmm_ker.ker_l2l != NullIterator<char>()) fmm_ker.delete_ker_l2l(fmm_ker.ker_l2l);
  111. }
  112. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::SetComm(const Comm& comm) {
  113. comm_ = comm;
  114. #ifdef SCTL_HAVE_PVFMM
  115. if (DIM == 3) {
  116. for (auto& it : s2t_map) {
  117. it.second.setup_ker = true;
  118. it.second.setup_tree = true;
  119. }
  120. }
  121. #endif
  122. }
  123. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::SetAccuracy(Integer digits) {
  124. #ifdef SCTL_HAVE_PVFMM
  125. if (DIM == 3 && digits != digits_) {
  126. for (auto& it : s2t_map) {
  127. it.second.setup_ker = true;
  128. it.second.setup_tree = true;
  129. }
  130. }
  131. #endif
  132. digits_ = digits;
  133. }
  134. template <class Real, Integer DIM> template <class KerM2M, class KerM2L, class KerL2L> void ParticleFMM<Real,DIM>::SetKernels(const KerM2M& ker_m2m, const KerM2L& ker_m2l, const KerL2L& ker_l2l) {
  135. if (fmm_ker.ker_m2m != NullIterator<char>()) fmm_ker.delete_ker_m2m(fmm_ker.ker_m2m);
  136. if (fmm_ker.ker_m2l != NullIterator<char>()) fmm_ker.delete_ker_m2l(fmm_ker.ker_m2l);
  137. if (fmm_ker.ker_l2l != NullIterator<char>()) fmm_ker.delete_ker_l2l(fmm_ker.ker_l2l);
  138. fmm_ker.ker_m2m = (Iterator<char>)aligned_new<KerM2M>(1);
  139. fmm_ker.ker_m2l = (Iterator<char>)aligned_new<KerM2L>(1);
  140. fmm_ker.ker_l2l = (Iterator<char>)aligned_new<KerL2L>(1);
  141. (*(Iterator<KerM2M>)fmm_ker.ker_m2m) = ker_m2m;
  142. (*(Iterator<KerM2L>)fmm_ker.ker_m2l) = ker_m2l;
  143. (*(Iterator<KerL2L>)fmm_ker.ker_l2l) = ker_l2l;
  144. fmm_ker.dim_mul_eq = ker_m2m.SrcDim();
  145. fmm_ker.dim_mul_ch = ker_m2m.TrgDim();
  146. fmm_ker.dim_loc_eq = ker_l2l.SrcDim();
  147. fmm_ker.dim_loc_ch = ker_l2l.TrgDim();
  148. SCTL_ASSERT(ker_m2m.CoordDim() == DIM);
  149. SCTL_ASSERT(ker_m2l.CoordDim() == DIM);
  150. SCTL_ASSERT(ker_l2l.CoordDim() == DIM);
  151. SCTL_ASSERT(ker_m2l.SrcDim() == fmm_ker.dim_mul_eq);
  152. SCTL_ASSERT(ker_m2l.TrgDim() == fmm_ker.dim_loc_ch);
  153. fmm_ker.ker_m2m_eval = KerM2M::template Eval<Real,false>;
  154. fmm_ker.ker_m2l_eval = KerM2L::template Eval<Real,false>;
  155. fmm_ker.ker_l2l_eval = KerL2L::template Eval<Real,false>;
  156. fmm_ker.delete_ker_m2m = DeleteKer<KerM2M>;
  157. fmm_ker.delete_ker_m2l = DeleteKer<KerM2L>;
  158. fmm_ker.delete_ker_l2l = DeleteKer<KerL2L>;
  159. #ifdef SCTL_HAVE_PVFMM
  160. if (DIM == 3) {
  161. fmm_ker.pvfmm_ker_m2m = pvfmm::BuildKernel<Real, PVFMMKernelFn<KerM2M>::template Eval<Real>>(ker_m2m.Name().c_str(), DIM, std::pair<int,int>(ker_m2m.SrcDim(), ker_m2m.TrgDim()));
  162. fmm_ker.pvfmm_ker_m2l = pvfmm::BuildKernel<Real, PVFMMKernelFn<KerM2L>::template Eval<Real>>(ker_m2l.Name().c_str(), DIM, std::pair<int,int>(ker_m2l.SrcDim(), ker_m2l.TrgDim()));
  163. fmm_ker.pvfmm_ker_l2l = pvfmm::BuildKernel<Real, PVFMMKernelFn<KerL2L>::template Eval<Real>>(ker_l2l.Name().c_str(), DIM, std::pair<int,int>(ker_l2l.SrcDim(), ker_l2l.TrgDim()));
  164. for (auto& it : s2t_map) {
  165. it.second.setup_ker = true;
  166. it.second.setup_tree = true;
  167. }
  168. }
  169. #endif
  170. }
  171. template <class Real, Integer DIM> template <class KerS2M, class KerS2L> void ParticleFMM<Real,DIM>::AddSrc(const std::string& name, const KerS2M& ker_s2m, const KerS2L& ker_s2l) {
  172. SCTL_ASSERT_MSG(src_map.find(name) == src_map.end(), "Source name already exists.");
  173. src_map[name] = SrcData();
  174. auto& data = src_map[name];
  175. data.ker_s2m = (Iterator<char>)aligned_new<KerS2M>(1);
  176. data.ker_s2l = (Iterator<char>)aligned_new<KerS2L>(1);
  177. (*(Iterator<KerS2M>)data.ker_s2m) = ker_s2m;
  178. (*(Iterator<KerS2L>)data.ker_s2l) = ker_s2l;
  179. data.dim_src = ker_s2m.SrcDim();
  180. data.dim_mul_ch = ker_s2m.TrgDim();
  181. data.dim_loc_ch = ker_s2l.TrgDim();
  182. data.dim_normal = ker_s2m.NormalDim();
  183. SCTL_ASSERT(ker_s2m.CoordDim() == DIM);
  184. SCTL_ASSERT(ker_s2l.CoordDim() == DIM);
  185. SCTL_ASSERT(ker_s2l.SrcDim() == data.dim_src);
  186. SCTL_ASSERT(ker_s2l.NormalDim() == data.dim_normal);
  187. data.ker_s2m_eval = KerS2M::template Eval<Real,false>;
  188. data.ker_s2l_eval = KerS2L::template Eval<Real,false>;
  189. data.delete_ker_s2m = DeleteKer<KerS2M>;
  190. data.delete_ker_s2l = DeleteKer<KerS2L>;
  191. #ifdef SCTL_HAVE_PVFMM
  192. if (DIM == 3) {
  193. if (data.dim_normal) {
  194. data.pvfmm_ker_s2m = pvfmm::BuildKernel<Real, PVFMMKernelFn<KerS2M,true>::template Eval<Real>, PVFMMKernelFn<KerS2M>::template Eval<Real>>(ker_s2m.Name().c_str(), DIM, std::pair<int,int>(ker_s2m.SrcDim(), ker_s2m.TrgDim()));
  195. data.pvfmm_ker_s2l = pvfmm::BuildKernel<Real, PVFMMKernelFn<KerS2L,true>::template Eval<Real>, PVFMMKernelFn<KerS2L>::template Eval<Real>>(ker_s2l.Name().c_str(), DIM, std::pair<int,int>(ker_s2l.SrcDim(), ker_s2l.TrgDim()));
  196. } else {
  197. data.pvfmm_ker_s2m = pvfmm::BuildKernel<Real, PVFMMKernelFn<KerS2M>::template Eval<Real>>(ker_s2m.Name().c_str(), DIM, std::pair<int,int>(ker_s2m.SrcDim(), ker_s2m.TrgDim()));
  198. data.pvfmm_ker_s2l = pvfmm::BuildKernel<Real, PVFMMKernelFn<KerS2L>::template Eval<Real>>(ker_s2l.Name().c_str(), DIM, std::pair<int,int>(ker_s2l.SrcDim(), ker_s2l.TrgDim()));
  199. }
  200. for (auto& it : s2t_map) {
  201. if (it.first.first != name) continue;
  202. it.second.setup_ker = true;
  203. it.second.setup_tree = true;
  204. }
  205. }
  206. #endif
  207. }
  208. template <class Real, Integer DIM> template <class KerM2T, class KerL2T> void ParticleFMM<Real,DIM>::AddTrg(const std::string& name, const KerM2T& ker_m2t, const KerL2T& ker_l2t) {
  209. SCTL_ASSERT_MSG(trg_map.find(name) == trg_map.end(), "Target name already exists.");
  210. trg_map[name] = TrgData();
  211. auto& data = trg_map[name];
  212. data.ker_m2t = (Iterator<char>)aligned_new<KerM2T>(1);
  213. data.ker_l2t = (Iterator<char>)aligned_new<KerL2T>(1);
  214. (*(Iterator<KerM2T>)data.ker_m2t) = ker_m2t;
  215. (*(Iterator<KerL2T>)data.ker_l2t) = ker_l2t;
  216. data.dim_trg = ker_l2t.TrgDim();
  217. data.dim_mul_eq = ker_m2t.SrcDim();
  218. data.dim_loc_eq = ker_l2t.SrcDim();
  219. SCTL_ASSERT(ker_m2t.CoordDim() == DIM);
  220. SCTL_ASSERT(ker_l2t.CoordDim() == DIM);
  221. SCTL_ASSERT(ker_m2t.TrgDim() == data.dim_trg);
  222. data.ker_m2t_eval = KerM2T::template Eval<Real,false>;
  223. data.ker_l2t_eval = KerL2T::template Eval<Real,false>;
  224. data.delete_ker_m2t = DeleteKer<KerM2T>;
  225. data.delete_ker_l2t = DeleteKer<KerL2T>;
  226. #ifdef SCTL_HAVE_PVFMM
  227. if (DIM == 3) {
  228. data.pvfmm_ker_m2t = pvfmm::BuildKernel<Real, PVFMMKernelFn<KerM2T>::template Eval<Real>>(ker_m2t.Name().c_str(), DIM, std::pair<int,int>(ker_m2t.SrcDim(), ker_m2t.TrgDim()));
  229. data.pvfmm_ker_l2t = pvfmm::BuildKernel<Real, PVFMMKernelFn<KerL2T>::template Eval<Real>>(ker_l2t.Name().c_str(), DIM, std::pair<int,int>(ker_l2t.SrcDim(), ker_l2t.TrgDim()));
  230. for (auto& it : s2t_map) {
  231. if (it.first.second != name) continue;
  232. it.second.setup_ker = true;
  233. it.second.setup_tree = true;
  234. }
  235. }
  236. #endif
  237. }
  238. template <class Real, Integer DIM> template <class KerS2T> void ParticleFMM<Real,DIM>::SetKernelS2T(const std::string& src_name, const std::string& trg_name, const KerS2T& ker_s2t) {
  239. SCTL_ASSERT_MSG(src_map.find(src_name) != src_map.end(), "Source name does not exists.");
  240. SCTL_ASSERT_MSG(trg_map.find(trg_name) != trg_map.end(), "Target name does not exists.");
  241. const auto name = std::make_pair(src_name, trg_name);
  242. if (s2t_map.find(name) != s2t_map.end()) DeleteS2T(src_name, trg_name);
  243. s2t_map[name] = S2TData();
  244. auto& data = s2t_map[name];
  245. data.ker_s2t = (Iterator<char>)aligned_new<KerS2T>(1);
  246. (*(Iterator<KerS2T>)data.ker_s2t) = ker_s2t;
  247. data.dim_src = ker_s2t.SrcDim();
  248. data.dim_trg = ker_s2t.TrgDim();
  249. data.dim_normal = ker_s2t.NormalDim();
  250. SCTL_ASSERT(ker_s2t.CoordDim() == DIM);
  251. data.ker_s2t_eval = KerS2T::template Eval<Real,false>;
  252. data.ker_s2t_eval_omp = KerS2T::template Eval<Real,true>;
  253. data.delete_ker_s2t = DeleteKer<KerS2T>;
  254. #ifdef SCTL_HAVE_PVFMM
  255. if (DIM == 3) {
  256. BuildSrcTrgScal(data, !comm_.Rank());
  257. if (data.dim_normal) {
  258. data.pvfmm_ker_s2t = pvfmm::BuildKernel<Real, PVFMMKernelFn<KerS2T,true>::template Eval<Real>, PVFMMKernelFn<KerS2T>::template Eval<Real>>(ker_s2t.Name().c_str(), DIM, std::pair<int,int>(ker_s2t.SrcDim(), ker_s2t.TrgDim()));
  259. } else {
  260. data.pvfmm_ker_s2t = pvfmm::BuildKernel<Real, PVFMMKernelFn<KerS2T>::template Eval<Real>>(ker_s2t.Name().c_str(), DIM, std::pair<int,int>(ker_s2t.SrcDim(), ker_s2t.TrgDim()));
  261. }
  262. }
  263. data.tree_ptr = nullptr;
  264. data.setup_ker = true;
  265. data.setup_tree = true;
  266. #endif
  267. }
  268. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::DeleteSrc(const std::string& name) {
  269. SCTL_ASSERT_MSG(src_map.find(name) != src_map.end(), "Source name does not exist.");
  270. auto& data = src_map[name];
  271. data.delete_ker_s2m(data.ker_s2m);
  272. data.delete_ker_s2l(data.ker_s2l);
  273. src_map.erase(name);
  274. Vector<std::pair<std::string,std::string>> s2t_lst;
  275. for (auto& it : s2t_map) if (it.first.first == name) s2t_lst.PushBack(it.first);
  276. for (const auto& name : s2t_lst) DeleteS2T(name.first, name.second);
  277. }
  278. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::DeleteTrg(const std::string& name) {
  279. SCTL_ASSERT_MSG(trg_map.find(name) != trg_map.end(), "Target name does not exist.");
  280. auto& data = trg_map[name];
  281. data.delete_ker_m2t(data.ker_m2t);
  282. data.delete_ker_l2t(data.ker_l2t);
  283. trg_map.erase(name);
  284. Vector<std::pair<std::string,std::string>> s2t_lst;
  285. for (auto& it : s2t_map) if (it.first.second == name) s2t_lst.PushBack(it.first);
  286. for (const auto& name : s2t_lst) DeleteS2T(name.first, name.second);
  287. }
  288. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::DeleteS2T(const std::string& src_name, const std::string& trg_name) {
  289. const auto name = std::make_pair(src_name, trg_name);
  290. SCTL_ASSERT_MSG(s2t_map.find(name) != s2t_map.end(), "S2T kernel does not exist.");
  291. auto& data = s2t_map[name];
  292. #ifdef SCTL_HAVE_PVFMM
  293. if (data.tree_ptr) delete data.tree_ptr;
  294. #endif
  295. data.delete_ker_s2t(data.ker_s2t);
  296. s2t_map.erase(name);
  297. }
  298. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::SetSrcCoord(const std::string& name, const Vector<Real>& src_coord, const Vector<Real>& src_normal) {
  299. SCTL_ASSERT_MSG(src_map.find(name) != src_map.end(), "Target name does not exist.");
  300. auto& data = src_map[name];
  301. data.X = src_coord;
  302. data.Xn = src_normal;
  303. #ifdef SCTL_HAVE_PVFMM
  304. if (DIM == 3) {
  305. BoundingBox<DIM>(data.bbox, src_coord, comm_);
  306. for (auto& it : s2t_map) {
  307. if (it.first.first != name) continue;
  308. it.second.setup_tree = true;
  309. }
  310. }
  311. #endif
  312. }
  313. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::SetSrcDensity(const std::string& name, const Vector<Real>& src_density) {
  314. SCTL_ASSERT_MSG(src_map.find(name) != src_map.end(), "Target name does not exist.");
  315. auto& data = src_map[name];
  316. data.F = src_density;
  317. }
  318. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::SetTrgCoord(const std::string& name, const Vector<Real>& trg_coord) {
  319. SCTL_ASSERT_MSG(trg_map.find(name) != trg_map.end(), "Target name does not exist.");
  320. auto& data = trg_map[name];
  321. data.X = trg_coord;
  322. #ifdef SCTL_HAVE_PVFMM
  323. if (DIM == 3) {
  324. BoundingBox<DIM>(data.bbox, trg_coord, comm_);
  325. for (auto& it : s2t_map) {
  326. if (it.first.second != name) continue;
  327. it.second.setup_tree = true;
  328. }
  329. }
  330. #endif
  331. }
  332. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::Eval(Vector<Real>& U, const std::string& trg_name) const {
  333. CheckKernelDims();
  334. #ifdef SCTL_HAVE_PVFMM
  335. EvalPVFMM(U, trg_name);
  336. #else
  337. EvalDirect(U, trg_name);
  338. #endif
  339. }
  340. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::EvalDirect(Vector<Real>& U, const std::string& trg_name) const {
  341. const Integer rank = comm_.Rank();
  342. const Integer np = comm_.Size();
  343. SCTL_ASSERT_MSG(trg_map.find(trg_name) != trg_map.end(), "Target name does not exist.");
  344. const auto& trg_data = trg_map.at(trg_name);
  345. const Integer TrgDim = trg_data.dim_trg;
  346. const auto& Xt = trg_data.X;
  347. const Long Nt = Xt.Dim() / DIM;
  348. SCTL_ASSERT(Xt.Dim() == Nt * DIM);
  349. if (U.Dim() != Nt * TrgDim) {
  350. U.ReInit(Nt * TrgDim);
  351. U.SetZero();
  352. }
  353. for (auto& it : s2t_map) {
  354. if (it.first.second != trg_name) continue;
  355. const std::string src_name = it.first.first;
  356. SCTL_ASSERT_MSG(src_map.find(src_name) != src_map.end(), "Source name does not exist.");
  357. const auto& src_data = src_map.at(src_name);
  358. const Integer SrcDim = src_data.dim_src;
  359. const Integer NorDim = src_data.dim_normal;
  360. const auto& Xs = src_data.X;
  361. const auto& F = src_data.F;
  362. const Vector<Real> Xn_dummy;
  363. const auto& Xn = (NorDim ? src_data.Xn : Xn_dummy);
  364. const Long Ns = Xs.Dim() / DIM;
  365. SCTL_ASSERT(Xs.Dim() == Ns * DIM);
  366. SCTL_ASSERT(F.Dim() == Ns * SrcDim);
  367. SCTL_ASSERT(Xn.Dim() == Ns * NorDim);
  368. Vector<Real> Xs_, Xn_, F_;
  369. for (Long i = 0; i < np; i++) {
  370. auto send_recv_vec = [this,rank,np](Vector<Real>& X_, const Vector<Real>& X, Integer offset){
  371. Integer send_partner = (rank + offset) % np;
  372. Integer recv_partner = (rank + np - offset) % np;
  373. Long send_cnt = X.Dim(), recv_cnt = 0;
  374. void* recv_req = comm_.Irecv( Ptr2Itr<Long>(&recv_cnt,1), 1, recv_partner, offset);
  375. void* send_req = comm_.Isend(Ptr2ConstItr<Long>(&send_cnt,1), 1, send_partner, offset);
  376. comm_.Wait(recv_req);
  377. comm_.Wait(send_req);
  378. X_.ReInit(recv_cnt);
  379. recv_req = comm_.Irecv(X_.begin(), recv_cnt, recv_partner, offset);
  380. send_req = comm_.Isend(X .begin(), send_cnt, send_partner, offset);
  381. comm_.Wait(recv_req);
  382. comm_.Wait(send_req);
  383. };
  384. send_recv_vec(Xs_, Xs, i);
  385. send_recv_vec(Xn_, Xn, i);
  386. send_recv_vec(F_ , F , i);
  387. it.second.ker_s2t_eval_omp(U, Xt, Xs_, Xn_, F_, digits_, it.second.ker_s2t);
  388. }
  389. }
  390. }
  391. template <class Real, Integer DIM> template <class Ker> void ParticleFMM<Real,DIM>::DeleteKer(Iterator<char> ker) {
  392. aligned_delete((Iterator<Ker>)ker);
  393. }
  394. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::CheckKernelDims() const {
  395. SCTL_ASSERT(fmm_ker.ker_m2m != NullIterator<char>());
  396. SCTL_ASSERT(fmm_ker.ker_m2l != NullIterator<char>());
  397. SCTL_ASSERT(fmm_ker.ker_l2l != NullIterator<char>());
  398. const Integer DimMulEq = fmm_ker.dim_mul_eq;
  399. const Integer DimMulCh = fmm_ker.dim_mul_ch;
  400. const Integer DimLocEq = fmm_ker.dim_loc_eq;
  401. const Integer DimLocCh = fmm_ker.dim_loc_ch;
  402. for (auto& src_it : src_map) {
  403. for (auto& trg_it : trg_map) {
  404. const auto name = std::make_pair(src_it.first, trg_it.first);
  405. SCTL_ASSERT_MSG(s2t_map.find(name) != s2t_map.end(), "S2T kernel for " + name.first + "-" + name.second + " was not provided.");
  406. }
  407. }
  408. for (auto& it : s2t_map) {
  409. const auto& src_name = it.first.first;
  410. const auto& trg_name = it.first.second;
  411. const Integer SrcDim = it.second.dim_src;
  412. const Integer TrgDim = it.second.dim_trg;
  413. const Integer NormalDim = it.second.dim_normal;
  414. SCTL_ASSERT_MSG(src_map.find(src_name) != src_map.end(), "Source name does not exist.");
  415. SCTL_ASSERT_MSG(trg_map.find(trg_name) != trg_map.end(), "Source name does not exist.");
  416. const auto& src_data = src_map.at(src_name);
  417. const auto& trg_data = trg_map.at(trg_name);
  418. SCTL_ASSERT(trg_data.dim_trg == TrgDim);
  419. SCTL_ASSERT(src_data.dim_src == SrcDim);
  420. SCTL_ASSERT(src_data.dim_normal == NormalDim);
  421. SCTL_ASSERT(src_data.dim_mul_ch == DimMulCh);
  422. SCTL_ASSERT(src_data.dim_loc_ch == DimLocCh);
  423. SCTL_ASSERT(trg_data.dim_mul_eq == DimMulEq);
  424. SCTL_ASSERT(trg_data.dim_loc_eq == DimLocEq);
  425. }
  426. }
  427. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::BuildSrcTrgScal(const S2TData& data, bool verbose) {
  428. const StaticArray<Integer,2> kdim{data.dim_src, data.dim_trg};
  429. const Integer dim_normal = data.dim_normal;
  430. const Real eps=machine_eps<Real>();
  431. auto BuildMatrix = [&data,&kdim,&dim_normal](Matrix<Real>& M, const Vector<Real>& src_X, const Vector<Real>& src_Xn, const Vector<Real>& trg_X) {
  432. const Long N = trg_X.Dim() / DIM;
  433. SCTL_ASSERT(trg_X.Dim() == N * DIM);
  434. SCTL_ASSERT(src_X.Dim() == DIM);
  435. SCTL_ASSERT(src_Xn.Dim() == dim_normal);
  436. M.ReInit(N, kdim[0]*kdim[1]);
  437. Vector<Real> F(kdim[0]), U(N*kdim[1]); F = 0;
  438. for (Integer k0 = 0; k0 < kdim[0]; k0++) {
  439. U = 0;
  440. F[k0] = 1;
  441. data.ker_s2t_eval(U, trg_X, src_X, src_Xn, F, -1, data.ker_s2t);
  442. for (Long i = 0; i < N; i++) {
  443. for (Integer k1 = 0; k1 < kdim[1]; k1++) {
  444. M[i][k0*kdim[1]+k1] = U[i*kdim[1]+k1];
  445. }
  446. }
  447. F[k0] = 0;
  448. }
  449. };
  450. Vector<Real>& src_scal_exp = data.src_scal_exp;
  451. Vector<Real>& trg_scal_exp = data.trg_scal_exp;
  452. src_scal_exp.ReInit(kdim[0]); src_scal_exp.SetZero();
  453. trg_scal_exp.ReInit(kdim[1]); trg_scal_exp.SetZero();
  454. Real scal=1.0;
  455. bool scale_invar = true;
  456. if (kdim[0]*kdim[1] > 0) { // Determine scaling
  457. Long N = 1024;
  458. Real eps_ = N * eps;
  459. Matrix<Real> M1(N, kdim[0]*kdim[1]);
  460. Vector<Real> src_coord(DIM), src_normal(dim_normal), trg_coord1(N*DIM);
  461. for (Integer i = 0; i < dim_normal; i++) src_normal[i] = (Real)(drand48() - 0.5);
  462. for (Integer i = 0; i < DIM; i++) src_coord[i] = 0;
  463. while (true) {
  464. Real inv_scal = 1/scal, abs_sum = 0;
  465. for (Long i = 0; i < N/2; i++) {
  466. for (Integer j = 0; j < DIM; j++) {
  467. trg_coord1[i*DIM+j] = (Real)(drand48()-0.5) * scal;
  468. }
  469. }
  470. for (Long i = N/2; i < N; i++) {
  471. for (Integer j = 0; j < DIM; j++) {
  472. trg_coord1[i*DIM+j] = (Real)(drand48()-0.5) * inv_scal;
  473. }
  474. }
  475. BuildMatrix(M1, src_coord, src_normal, trg_coord1);
  476. for (const auto& a : M1) abs_sum += fabs<Real>(a);
  477. if (abs_sum > sqrt<Real>(eps) || scal < eps) break;
  478. scal = scal * (Real)0.5;
  479. }
  480. Vector<Real> trg_coord2 = trg_coord1*(Real)0.5;
  481. Matrix<Real> M2(N,kdim[0]*kdim[1]);
  482. BuildMatrix(M2, src_coord, src_normal, trg_coord2);
  483. Real max_val = 0;
  484. Matrix<Real> M_scal(kdim[0], kdim[1]);
  485. for (Integer i = 0; i < kdim[0]*kdim[1]; i++) {
  486. Real dot11 = 0, dot22 = 0;
  487. for(Long j = 0; j < N; j++) {
  488. dot11 += M1[j][i] * M1[j][i];
  489. dot22 += M2[j][i] * M2[j][i];
  490. }
  491. max_val = std::max<Real>(max_val, dot11);
  492. max_val = std::max<Real>(max_val, dot22);
  493. }
  494. for (Integer i = 0; i < kdim[0]*kdim[1]; i++) {
  495. Real dot11 = 0, dot12 = 0, dot22 = 0;
  496. for (Long j = 0; j < N; j++) {
  497. dot11 += M1[j][i] * M1[j][i];
  498. dot12 += M1[j][i] * M2[j][i];
  499. dot22 += M2[j][i] * M2[j][i];
  500. }
  501. if (dot11>max_val*eps && dot22>max_val*eps) {
  502. Real s = dot12 / dot11;
  503. M_scal[0][i] = log<Real>(s) / log<Real>(2.0);
  504. Real err = sqrt<Real>((Real)0.5*(dot22/dot11)/(s*s) - (Real)0.5);
  505. if (err > eps_) {
  506. scale_invar = false;
  507. M_scal[0][i] = 0.0;
  508. }
  509. //assert(M_scal[0][i]>=0.0); // Kernel function must decay
  510. }else if(dot11>max_val*eps || dot22>max_val*eps) {
  511. scale_invar = false;
  512. M_scal[0][i] = 0.0;
  513. }else{
  514. M_scal[0][i] = -1;
  515. }
  516. }
  517. if (scale_invar) {
  518. Matrix<Real> b(kdim[0]*kdim[1]+1,1); b.SetZero();
  519. for (Integer i = 0; i < kdim[0]*kdim[1]; i++) b[i][0] = M_scal[0][i];
  520. Matrix<Real> M(kdim[0]*kdim[1]+1, kdim[0]+kdim[1]); M.SetZero();
  521. M[kdim[0]*kdim[1]][0] = 1;
  522. for (Integer i0 = 0; i0 < kdim[0]; i0++) {
  523. for (Integer i1 = 0; i1 < kdim[1]; i1++) {
  524. Integer j = i0*kdim[1] + i1;
  525. if (b[j][0] > 0) {
  526. M[j][ 0+ i0] = 1;
  527. M[j][i1+kdim[0]] = 1;
  528. }
  529. }
  530. }
  531. Matrix<Real> x = M.pinv() * b;
  532. for (Integer i = 0; i < kdim[0]; i++) { // Set src_scal_exp
  533. src_scal_exp[i] = x[i][0];
  534. }
  535. for (Integer i = 0; i < kdim[1]; i++) { // Set src_scal_exp
  536. trg_scal_exp[i] = x[kdim[0]+i][0];
  537. }
  538. for (Integer i0 = 0; i0 < kdim[0]; i0++) { // Verify
  539. for (Integer i1 = 0; i1 < kdim[1]; i1++) {
  540. if (M_scal[i0][i1] >= 0) {
  541. if (fabs<Real>(src_scal_exp[i0] + trg_scal_exp[i1] - M_scal[i0][i1]) > eps_) {
  542. scale_invar = false;
  543. }
  544. }
  545. }
  546. }
  547. }
  548. if (!scale_invar) {
  549. src_scal_exp.SetZero();
  550. trg_scal_exp.SetZero();
  551. }
  552. }
  553. #ifdef SCTL_VERBOSE
  554. if (verbose && scale_invar && kdim[0]*kdim[1] > 0) {
  555. std::cout<<"Scaling Matrix :\n";
  556. Matrix<Real> Src(kdim[0],1);
  557. Matrix<Real> Trg(1,kdim[1]);
  558. for(Integer i=0;i<kdim[0];i++) Src[i][0]=pow<Real>(2.0,src_scal_exp[i]);
  559. for(Integer i=0;i<kdim[1];i++) Trg[0][i]=pow<Real>(2.0,trg_scal_exp[i]);
  560. std::cout<<Src*Trg<<'\n';
  561. }
  562. #endif
  563. }
  564. #ifdef SCTL_HAVE_PVFMM
  565. template <class SCTLKernel, bool use_dummy_normal> struct PVFMMKernelFn_ {
  566. static const int FLOPS = SCTLKernel::FLOPS() + 2*SCTLKernel::SrcDim()*SCTLKernel::TrgDim();;
  567. template <class ValueType> static ValueType ScaleFactor();
  568. template <class VecType, int digits> static void uKerEval(VecType (&u)[SCTLKernel::TrgDim()], const VecType (&r)[SCTLKernel::CoordDim()], const VecType (&f)[SCTLKernel::SrcDim()+(use_dummy_normal?0:SCTLKernel::NormalDim())], const void* ctx_ptr);
  569. };
  570. template <class Real, Integer DIM> template <class SCTLKernel, bool use_dummy_normal> struct ParticleFMM<Real,DIM>::PVFMMKernelFn : public pvfmm::GenericKernel<PVFMMKernelFn_<SCTLKernel,use_dummy_normal>> {};
  571. template <class SCTLKernel, bool use_dummy_normal> template <class ValueType> ValueType PVFMMKernelFn_<SCTLKernel,use_dummy_normal>::ScaleFactor() {
  572. return SCTLKernel::template uKerScaleFactor<ValueType>();
  573. }
  574. template <class SCTLKernel, bool use_dummy_normal> template <class VecType, int digits> void PVFMMKernelFn_<SCTLKernel,use_dummy_normal>::uKerEval(VecType (&u)[SCTLKernel::TrgDim()], const VecType (&r)[SCTLKernel::CoordDim()], const VecType (&f)[SCTLKernel::SrcDim()+(use_dummy_normal?0:SCTLKernel::NormalDim())], const void* ctx_ptr) {
  575. constexpr Integer KDIM0 = SCTLKernel::SrcDim();
  576. constexpr Integer KDIM1 = SCTLKernel::TrgDim();
  577. constexpr Integer N_DIM = SCTLKernel::NormalDim();
  578. constexpr Integer N_DIM_ = (N_DIM?N_DIM:1);
  579. VecType Xn[N_DIM_], K[KDIM0][KDIM1];
  580. for (Integer i = 0; i < N_DIM; i++) { // Set Xn
  581. Xn[i] = (use_dummy_normal ? VecType((typename VecType::ScalarType)0) : f[KDIM0+i]);
  582. }
  583. SCTLKernel::template uKerMatrix<digits>(K, r, Xn, ctx_ptr);
  584. for (Integer k0 = 0; k0 < KDIM0; k0++) { // u <-- K * f
  585. for (Integer k1 = 0; k1 < KDIM1; k1++) {
  586. u[k1] = FMA(K[k0][k1], f[k0], u[k1]);
  587. }
  588. }
  589. }
  590. template <class Real, Integer DIM> void ParticleFMM<Real,DIM>::EvalPVFMM(Vector<Real>& U, const std::string& trg_name) const {
  591. if (DIM != 3) return EvalDirect(U, trg_name); // PVFMM only supports 3D
  592. SCTL_ASSERT_MSG(trg_map.find(trg_name) != trg_map.end(), "Target name does not exist.");
  593. const auto& trg_data = trg_map.at(trg_name);
  594. const Integer TrgDim = trg_data.dim_trg;
  595. const auto& Xt = trg_data.X;
  596. const Long Nt = Xt.Dim() / DIM;
  597. SCTL_ASSERT(Xt.Dim() == Nt * DIM);
  598. { // User EvalDirect for small problems
  599. StaticArray<Long,2> cnt{Nt,0};
  600. comm_.Allreduce<Long>(cnt+0, cnt+1, 1, Comm::CommOp::SUM);
  601. if (cnt[1] < 40000) return EvalDirect(U, trg_name);
  602. }
  603. if (U.Dim() != Nt * TrgDim) {
  604. U.ReInit(Nt * TrgDim);
  605. U.SetZero();
  606. }
  607. for (auto& it : s2t_map) {
  608. if (it.first.second != trg_name) continue;
  609. const std::string src_name = it.first.first;
  610. const auto& s2t_data = it.second;
  611. SCTL_ASSERT_MSG(src_map.find(src_name) != src_map.end(), "Source name does not exist.");
  612. const auto& src_data = src_map.at(src_name);
  613. const Integer SrcDim = src_data.dim_src;
  614. const Integer NorDim = src_data.dim_normal;
  615. const auto& Xs = src_data.X;
  616. const auto& F = src_data.F;
  617. const Vector<Real> Xn_dummy;
  618. const auto& Xn = (NorDim ? src_data.Xn : Xn_dummy);
  619. const Long Ns = Xs.Dim() / DIM;
  620. SCTL_ASSERT(Xs.Dim() == Ns * DIM);
  621. SCTL_ASSERT(F.Dim() == Ns * SrcDim);
  622. SCTL_ASSERT(Xn.Dim() == Ns * NorDim);
  623. if (!Ns) continue;
  624. { // Run FMM
  625. const Integer max_pts=3000, mult_order = ((Integer)(digits_*0.55))*2; // TODO: use better estimates
  626. pvfmm::PtFMM<Real>& fmm_ctx = s2t_data.fmm_ctx;
  627. if (s2t_data.setup_ker) { // Setup fmm_ctx
  628. pvfmm::Kernel<Real>& pvfmm_ker_s2t = s2t_data.pvfmm_ker_s2t;
  629. pvfmm_ker_s2t.k_m2m = &fmm_ker.pvfmm_ker_m2m;
  630. pvfmm_ker_s2t.k_m2l = &fmm_ker.pvfmm_ker_m2l;
  631. pvfmm_ker_s2t.k_l2l = &fmm_ker.pvfmm_ker_l2l;
  632. pvfmm_ker_s2t.k_m2t = &trg_data.pvfmm_ker_m2t;
  633. pvfmm_ker_s2t.k_l2t = &trg_data.pvfmm_ker_l2t;
  634. pvfmm_ker_s2t.k_s2m = &src_data.pvfmm_ker_s2m;
  635. pvfmm_ker_s2t.k_s2l = &src_data.pvfmm_ker_s2l;
  636. fmm_ctx.Initialize(mult_order, comm_.GetMPI_Comm(), &pvfmm_ker_s2t);
  637. s2t_data.setup_ker = false;
  638. }
  639. std::vector<Real> sl_den_, dl_den_;
  640. Vector<Real>& src_scal = s2t_data.src_scal;
  641. Vector<Real>& trg_scal = s2t_data.trg_scal;
  642. pvfmm::PtFMM_Tree<Real>*& tree_ptr = s2t_data.tree_ptr;
  643. if (s2t_data.setup_tree) { // Setup tree_ptr, src_scal, trg_scal
  644. auto& bbox_scale = s2t_data.bbox_scale;
  645. auto& bbox_offset = s2t_data.bbox_offset;
  646. { // Set bbox_scale, bbox_offset
  647. StaticArray<Real,DIM*2> bbox;
  648. for (Integer k = 0; k < DIM; k++) {
  649. bbox[k*2+0] = std::min<Real>(src_data.bbox[k*2+0], trg_data.bbox[k*2+0]);
  650. bbox[k*2+1] = std::max<Real>(src_data.bbox[k*2+1], trg_data.bbox[k*2+1]);
  651. }
  652. Real bbox_len = 0;
  653. for (Integer k = 0; k < DIM; k++) {
  654. bbox_offset[k] = (bbox[k*2+0] + bbox[k*2+1])/2;
  655. bbox_len = std::max<Real>(bbox_len, bbox[k*2+1]-bbox[k*2+0]);
  656. }
  657. bbox_scale = 1/(bbox_len*(Real)1.1); // extra 5% padding so that points are not on boundary
  658. for (Integer k = 0; k < DIM; k++) {
  659. bbox_offset[k] -= 1/(2*bbox_scale);
  660. }
  661. }
  662. { // Set src_scal, trg_scal
  663. src_scal.ReInit(SrcDim);
  664. trg_scal.ReInit(TrgDim);
  665. const Vector<Real>& src_scal_exp = s2t_data.src_scal_exp;
  666. const Vector<Real>& trg_scal_exp = s2t_data.trg_scal_exp;
  667. for (Integer i = 0; i < SrcDim; i++) src_scal[i] = pow<Real>(bbox_scale, src_scal_exp[i]);
  668. for (Integer i = 0; i < TrgDim; i++) trg_scal[i] = pow<Real>(bbox_scale, trg_scal_exp[i]);
  669. }
  670. std::vector<Real> sl_coord_, dl_coord_, trg_coord_(Nt*DIM);
  671. auto& src_coord = (NorDim ? dl_coord_ : sl_coord_);
  672. src_coord.resize(Ns * DIM);
  673. for (Long i = 0; i < Ns; i++) {
  674. for (Integer k = 0; k < DIM; k++) {
  675. src_coord[i*DIM+k] = (Xs[i*DIM+k] - bbox_offset[k]) * bbox_scale;
  676. }
  677. }
  678. for (Long i = 0; i < Nt; i++) {
  679. for (Integer k = 0; k < DIM; k++) {
  680. trg_coord_[i*DIM+k] = (Xt[i*DIM+k] - bbox_offset[k]) * bbox_scale;
  681. }
  682. }
  683. if (tree_ptr) delete tree_ptr;
  684. sl_den_.resize(NorDim ? 0 : Ns*SrcDim);
  685. dl_den_.resize(NorDim ? Ns*(SrcDim+NorDim) : 0);
  686. tree_ptr = PtFMM_CreateTree(sl_coord_, sl_den_, dl_coord_, dl_den_, trg_coord_, comm_.GetMPI_Comm(), max_pts, pvfmm::FreeSpace);
  687. tree_ptr->SetupFMM(&fmm_ctx);
  688. s2t_data.setup_tree = false;
  689. } else {
  690. tree_ptr->ClearFMMData();
  691. }
  692. if (NorDim) { // Set sl_den_, dl_den_
  693. dl_den_.resize(Ns*(SrcDim+NorDim));
  694. for (Long i = 0; i < Ns; i++) {
  695. for (Long j = 0; j < SrcDim; j++) {
  696. dl_den_[i*(SrcDim+NorDim) + j] = F[i*SrcDim+j] * src_scal[j];
  697. }
  698. for (Long j = 0; j < NorDim; j++) {
  699. dl_den_[i*(SrcDim+NorDim) + SrcDim+j] = Xn[i*NorDim+j];
  700. }
  701. }
  702. } else {
  703. sl_den_.resize(Ns*SrcDim);
  704. for (Long i = 0; i < Ns; i++) {
  705. for (Long j = 0; j < SrcDim; j++) {
  706. sl_den_[i*SrcDim+j] = F[i*SrcDim+j] * src_scal[j];
  707. }
  708. }
  709. }
  710. std::vector<Real> trg_value;
  711. PtFMM_Evaluate(tree_ptr, trg_value, Nt, &sl_den_, &dl_den_);
  712. //pvfmm::Profile::print(&comm_.GetMPI_Comm());
  713. SCTL_ASSERT(trg_value.size() == (size_t)(Nt*TrgDim));
  714. for (Long i = 0; i < Nt; i++) {
  715. for (Long j = 0; j < TrgDim; j++) {
  716. U[i*TrgDim+j] += trg_value[i*TrgDim+j] * trg_scal[j];
  717. }
  718. }
  719. }
  720. }
  721. }
  722. #endif
  723. } // end namespace