boundary_integ_op.hpp 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307
  1. #ifndef _BOUNDARY_INTEG_OP_HPP_
  2. #define _BOUNDARY_INTEG_OP_HPP_
  3. #include <biest/surface.hpp>
  4. #include <biest/singular_correction.hpp>
  5. #include <sctl.hpp>
  6. namespace biest {
  7. template <class Real, sctl::Integer KDIM0, sctl::Integer KDIM1, sctl::Integer UPSAMPLE = 1, sctl::Integer PATCH_DIM0 = 25, sctl::Integer RAD_DIM = 18> class BoundaryIntegralOp {
  8. static constexpr sctl::Integer COORD_DIM = 3;
  9. public:
  10. BoundaryIntegralOp(const sctl::Comm& comm = sctl::Comm::Self()) : comm_(comm) {
  11. dim[0] = 0;
  12. dim[1] = 0;
  13. ker_ptr = nullptr;
  14. }
  15. sctl::Long Dim(sctl::Integer i) const { return dim[i]; }
  16. template <class Surface, class Kernel> void Setup(const sctl::Vector<Surface>& Svec, const Kernel& ker) {
  17. dim[0] = 0;
  18. dim[1] = 0;
  19. if (!Svec.Dim()) return;
  20. ker_ptr = &ker;
  21. sctl::Long Nsurf = Svec.Dim();
  22. Nt0 .ReInit(Nsurf);
  23. Np0 .ReInit(Nsurf);
  24. Op .ReInit(Nsurf);
  25. Xtrg .ReInit(Nsurf);
  26. Xsrc .ReInit(Nsurf);
  27. dXsrc .ReInit(Nsurf);
  28. Xn_src.ReInit(Nsurf);
  29. Xa_src.ReInit(Nsurf);
  30. normal_scal.ReInit(Nsurf);
  31. for (sctl::Long i = 0; i < Nsurf; i++) {
  32. const auto& S = Svec[i];
  33. Nt0[i] = S.NTor();
  34. Np0[i] = S.NPol();
  35. Op[i] = SurfaceOp<Real>(comm_, Nt0[i]*UPSAMPLE, Np0[i]*UPSAMPLE);
  36. Xtrg[i] = S.Coord();
  37. Upsample(Xtrg[i], Xsrc[i], Nt0[i], Np0[i]);
  38. Op[i].Grad2D(dXsrc[i], Xsrc[i]);
  39. normal_scal = Op[i].SurfNormalAreaElem(&Xn_src[i], &Xa_src[i], dXsrc[i], &Xsrc[i]);
  40. dim[0] += ker.Dim(0) * (Xtrg[i].Dim() / COORD_DIM);
  41. dim[1] += ker.Dim(1) * (Xtrg[i].Dim() / COORD_DIM);
  42. }
  43. }
  44. template <class Surface, class Kernel> void SetupSingular(const sctl::Vector<Surface>& Svec, const Kernel& ker) {
  45. Setup(Svec, ker);
  46. sctl::Long Nsurf = Svec.Dim();
  47. singular_correction.ReInit(Nsurf);
  48. for (sctl::Long i = 0; i < Nsurf; i++) {
  49. Op[i].SetupSingularCorrection(singular_correction[i], UPSAMPLE, Xsrc[i], dXsrc[i], ker, normal_scal[i]);
  50. }
  51. }
  52. void operator()(sctl::Vector<Real>& U, const sctl::Vector<Real>& F) const {
  53. SCTL_ASSERT(ker_ptr);
  54. sctl::Long dof = F.Dim() / Dim(0);
  55. SCTL_ASSERT(F.Dim() == dof * Dim(0));
  56. if (U.Dim() != dof * Dim(1)) {
  57. U.ReInit(dof * Dim(1));
  58. U = 0;
  59. }
  60. sctl::Vector<Real> Fsrc;
  61. sctl::Long SrcOffset = 0;
  62. for (sctl::Long s = 0; s < Xsrc.Dim(); s++) {
  63. const sctl::Vector<Real> F_(dof * ker_ptr->Dim(0) * Nt0[s] * Np0[s], (sctl::Iterator<Real>)F.begin() + dof * ker_ptr->Dim(0) * SrcOffset, false);
  64. Upsample(F_, Fsrc, Nt0[s], Np0[s]);
  65. sctl::Long TrgOffset = 0;
  66. for (sctl::Long t = 0; t < Xtrg.Dim(); t++) {
  67. sctl::Vector<Real> U_(dof * ker_ptr->Dim(1) * Nt0[t] * Np0[t], U.begin() + dof * ker_ptr->Dim(1) * TrgOffset, false);
  68. Op[s].EvalSurfInteg(U_, Xtrg[t], Xsrc[s], Xn_src[s], Xa_src[s], Fsrc, *ker_ptr);
  69. TrgOffset += Nt0[t] * Np0[t];
  70. }
  71. sctl::Vector<Real> U_(dof * ker_ptr->Dim(1) * Nt0[s] * Np0[s], U.begin() + dof * ker_ptr->Dim(1) * SrcOffset, false);
  72. Op[s].EvalSingularCorrection(U_, singular_correction[s], ker_ptr->Dim(0), ker_ptr->Dim(1), Fsrc);
  73. SrcOffset += Nt0[s] * Np0[s];
  74. }
  75. };
  76. void EvalOffSurface(sctl::Vector<Real>& U, const sctl::Vector<Real> Xt, const sctl::Vector<Real>& F) const {
  77. SCTL_ASSERT(ker_ptr);
  78. SCTL_ASSERT(F.Dim() == Dim(0));
  79. sctl::Long Nt = Xt.Dim() / COORD_DIM;
  80. SCTL_ASSERT(Xt.Dim() == COORD_DIM * Nt);
  81. if (U.Dim() != Nt * ker_ptr->Dim(1)) {
  82. U.ReInit(Nt * ker_ptr->Dim(1));
  83. U = 0;
  84. }
  85. sctl::Vector<Real> Fsrc;
  86. sctl::Long SrcOffset = 0;
  87. for (sctl::Long s = 0; s < Xsrc.Dim(); s++) {
  88. const sctl::Vector<Real> F_(ker_ptr->Dim(0) * Nt0[s] * Np0[s], (sctl::Iterator<Real>)F.begin() + ker_ptr->Dim(0) * SrcOffset, false);
  89. Upsample(F_, Fsrc, Nt0[s], Np0[s]);
  90. Op[s].EvalSurfInteg(U, Xt, Xsrc[s], Xn_src[s], Xa_src[s], Fsrc, *ker_ptr);
  91. SrcOffset += Nt0[s] * Np0[s];
  92. }
  93. };
  94. template <class Kernel> static sctl::Vector<Real> test(sctl::Long Nt, sctl::Long Np, SurfType surf_type, const Kernel& ker, const sctl::Comm& comm) {
  95. // Parameters
  96. // UPSAMPLE Upsample factor
  97. // PATCH_DIM Patch dimension (has optimal value, too small/large will increase error)
  98. // RADIAL_DIM Radial quadrature order
  99. // INTERP_ORDER Interpolation order
  100. // POU-function Partition-of-unity function
  101. sctl::Vector<Surface<Real>> Svec;
  102. Svec.PushBack(Surface<Real>(Nt, Np, surf_type));
  103. BoundaryIntegralOp integ_op(comm);
  104. sctl::Profile::Tic("Setup", &comm);
  105. integ_op.SetupSingular(Svec, ker);
  106. sctl::Profile::Toc();
  107. sctl::Vector<Real> U, Fsrc(ker.Dim(0) * Nt * Np);
  108. Fsrc = 1;
  109. sctl::Profile::Tic("Eval", &comm);
  110. integ_op(U, Fsrc);
  111. sctl::Profile::Toc();
  112. if (!comm.Rank()) {
  113. Real max_err = 0;
  114. for (sctl::Long i = 0; i < U.Dim(); i++) max_err = std::max<Real>(max_err, fabs(U[i]-(Real)0.5));
  115. std::cout<<(double)max_err<<'\n';
  116. }
  117. return U;
  118. }
  119. template <class Kernel, class GradKernel> static void test_GreensIdentity(sctl::Long Nt, sctl::Long Np, SurfType surf_type, const Kernel& sl_ker, const Kernel& dl_ker, const GradKernel& sl_grad_ker, const sctl::Comm& comm) {
  120. sctl::Vector<Surface<Real>> Svec;
  121. Svec.PushBack(Surface<Real>(Nt, Np, surf_type));
  122. BoundaryIntegralOp<Real, Kernel::Dim(0), Kernel::Dim(1), UPSAMPLE, PATCH_DIM0, RAD_DIM> SL(comm);
  123. BoundaryIntegralOp<Real, Kernel::Dim(0), Kernel::Dim(1), UPSAMPLE, PATCH_DIM0, RAD_DIM> DL(comm);
  124. sctl::Vector<Real> Fs, Fd;
  125. { // Set Fs, Fd
  126. const auto& S = Svec[0];
  127. const auto& Xt = S.Coord();
  128. sctl::Long Nt = S.NTor(), Np = S.NPol();
  129. sctl::Vector<Real> dX, Xn, Xa;
  130. SurfaceOp<Real> Op(comm, Nt, Np);
  131. Op.Grad2D(dX, Xt);
  132. Op.SurfNormalAreaElem(&Xn, &Xa, dX, &Xt);
  133. sctl::Vector<Real> Xsrc;
  134. sctl::Vector<Real> Fsrc;
  135. Xsrc.PushBack(1);
  136. Xsrc.PushBack(2);
  137. Xsrc.PushBack(3);
  138. for (sctl::Long i = 0; i < sl_ker.Dim(0); i++) Fsrc.PushBack(drand48());
  139. Fsrc /= max_norm(Fsrc);
  140. sctl::Vector<Real> U, dU;
  141. sl_ker(Xsrc, Xsrc, Fsrc, Xt, U);
  142. sl_grad_ker(Xsrc, Xsrc, Fsrc, Xt, dU);
  143. Fd = U;
  144. Fs = U * 0;
  145. for (sctl::Long j = 0; j < COORD_DIM; j++) {
  146. for (sctl::Long k = 0; k < sl_ker.Dim(0); k++) {
  147. for (sctl::Long i = 0; i < Nt*Np; i++) {
  148. Fs[k * Nt*Np + i] += Xn[j * Nt*Np + i] * dU[(j*sl_ker.Dim(0)+k) * Nt*Np + i];
  149. }
  150. }
  151. }
  152. }
  153. sctl::Profile::Tic("Setup", &comm);
  154. SL.SetupSingular(Svec, sl_ker);
  155. DL.SetupSingular(Svec, dl_ker);
  156. sctl::Profile::Toc();
  157. sctl::Profile::Tic("Eval", &comm);
  158. sctl::Vector<Real> Us, Ud;
  159. SL(Us, Fs);
  160. DL(Ud, Fd);
  161. sctl::Profile::Toc();
  162. std::cout<<"Error: "<<max_norm(Us+Ud-0.5*Fd)/max_norm(Fd)<<'\n';
  163. WriteVTK("U", Svec, Us+Ud+0.5*Fd, comm);
  164. }
  165. static void test_Precond(sctl::Long Nt, sctl::Long Np, SurfType surf_type, Real gmres_tol, sctl::Long gmres_iter, const sctl::Comm& comm) {
  166. sctl::Profile::Tic("SLayerPrecond", &comm);
  167. sctl::Profile::Tic("Setup", &comm);
  168. Surface<Real> S(Nt, Np, surf_type);
  169. BoundaryIntegralOp integ_op(comm);
  170. const auto& ker = Laplace3D<Real>::FxU();
  171. sctl::Vector<Surface<Real>> Svec; Svec.PushBack(S);
  172. integ_op.SetupSingular(Svec, ker);
  173. std::function<void(sctl::Vector<Real>&, const sctl::Vector<Real>&)> SPrecond = [&integ_op](sctl::Vector<Real>& Sx, const sctl::Vector<Real>& x) { integ_op(Sx, x); };
  174. sctl::Profile::Toc();
  175. SurfaceOp<Real>::test_InvSurfLap(Nt, Np, surf_type, gmres_tol, gmres_iter, &SPrecond, &SPrecond, comm);
  176. sctl::Profile::Toc();
  177. }
  178. private:
  179. static void Upsample(const sctl::Vector<Real>& X0_, sctl::Vector<Real>& X_, sctl::Long Nt0, sctl::Long Np0) { // TODO: Replace by upsample in SurfaceOp
  180. auto FFT_Helper = [](const sctl::FFT<Real>& fft, const sctl::Vector<Real>& in, sctl::Vector<Real>& out) {
  181. sctl::Long dof = in.Dim() / fft.Dim(0);
  182. if (out.Dim() != dof * fft.Dim(1)) {
  183. out.ReInit(dof * fft.Dim(1));
  184. }
  185. for (sctl::Long i = 0; i < dof; i++) {
  186. const sctl::Vector<Real> in_(fft.Dim(0), (sctl::Iterator<Real>)in.begin() + i * fft.Dim(0), false);
  187. sctl::Vector<Real> out_(fft.Dim(1), out.begin() + i * fft.Dim(1), false);
  188. fft.Execute(in_, out_);
  189. }
  190. };
  191. assert(X0_.Dim() % (Nt0 * Np0) == 0);
  192. sctl::Long Nt1 = UPSAMPLE * Nt0;
  193. sctl::Long Np1 = UPSAMPLE * Np0;
  194. sctl::Long Nt0_ = Nt0;
  195. sctl::Long Np0_ = Np0 / 2 + 1;
  196. sctl::Long Nt1_ = Nt1;
  197. sctl::Long Np1_ = Np1 / 2 + 1;
  198. sctl::FFT<Real> fft_r2c0, fft_c2r_;
  199. { // Initialize fft_r2c0, fft_c2r_
  200. sctl::StaticArray<sctl::Long, 2> fft_dim0 = {Nt0, Np0};
  201. sctl::StaticArray<sctl::Long, 2> fft_dim_ = {Nt1, Np1};
  202. fft_r2c0.Setup(sctl::FFT_Type::R2C, 1, sctl::Vector<sctl::Long>(2, fft_dim0, false), omp_get_max_threads());
  203. fft_c2r_.Setup(sctl::FFT_Type::C2R, 1, sctl::Vector<sctl::Long>(2, fft_dim_, false), omp_get_max_threads());
  204. }
  205. sctl::Vector<Real> tmp0, tmp_;
  206. auto X0__ = X0_; // TODO: make unaligned plans and remove this workaround
  207. FFT_Helper(fft_r2c0, X0__, tmp0);
  208. sctl::Long dof = tmp0.Dim() / (Nt0_*Np0_*2);
  209. SCTL_ASSERT(tmp0.Dim() == dof * Nt0_ * Np0_ * 2);
  210. if (tmp_.Dim() != dof * Nt1_ * Np1_ * 2) tmp_.ReInit(dof * Nt1_ * Np1_ * 2);
  211. tmp_.SetZero();
  212. Real scale = sctl::sqrt<Real>(Nt1 * Np1) / sctl::sqrt<Real>(Nt0 * Np0);
  213. sctl::Long Ntt = std::min(Nt0_, Nt1_);
  214. sctl::Long Npp = std::min(Np0_, Np1_);
  215. for (sctl::Long k = 0; k < dof; k++) {
  216. for (sctl::Long t = 0; t < (Ntt + 1) / 2; t++) {
  217. for (sctl::Long p = 0; p < Npp; p++) {
  218. tmp_[((k * Nt1_ + t) * Np1_ + p) * 2 + 0] = scale * tmp0[((k * Nt0_ + t) * Np0_ + p) * 2 + 0];
  219. tmp_[((k * Nt1_ + t) * Np1_ + p) * 2 + 1] = scale * tmp0[((k * Nt0_ + t) * Np0_ + p) * 2 + 1];
  220. }
  221. }
  222. for (sctl::Long t = 0; t < Ntt / 2; t++) {
  223. for (sctl::Long p = 0; p < Npp; p++) {
  224. tmp_[((k * Nt1_ + (Nt1_ - t - 1)) * Np1_ + p) * 2 + 0] = scale * tmp0[((k * Nt0_ + (Nt0_ - t - 1)) * Np0_ + p) * 2 + 0];
  225. tmp_[((k * Nt1_ + (Nt1_ - t - 1)) * Np1_ + p) * 2 + 1] = scale * tmp0[((k * Nt0_ + (Nt0_ - t - 1)) * Np0_ + p) * 2 + 1];
  226. }
  227. }
  228. }
  229. if (X_.Dim() != dof * Nt1 * Np1) X_.ReInit(dof * Nt1 * Np1);
  230. FFT_Helper(fft_c2r_, tmp_, X_);
  231. { // Floating-point correction
  232. sctl::Long Ut = Nt1 / Nt0;
  233. sctl::Long Up = Np1 / Np0;
  234. if (Nt1 == Nt0 * Ut && Np1 == Np0 * Up) {
  235. for (sctl::Long k = 0; k < dof; k++) {
  236. for (sctl::Long t = 0; t < Nt0; t++) {
  237. for (sctl::Long p = 0; p < Np0; p++) {
  238. X_[(k * Nt1 + t * Ut) * Np1 + p * Up] = X0_[(k * Nt0 + t) * Np0 + p];
  239. }
  240. }
  241. }
  242. }
  243. }
  244. }
  245. static Real max_norm(const sctl::Vector<Real>& x) {
  246. Real err = 0;
  247. for (const auto& a : x) err = std::max(err, sctl::fabs<Real>(a));
  248. return err;
  249. }
  250. sctl::Comm comm_;
  251. sctl::Vector<sctl::Long> Nt0, Np0;
  252. sctl::Vector<SurfaceOp<Real>> Op;
  253. const KernelFunction<Real,COORD_DIM,KDIM0,KDIM1>* ker_ptr;
  254. sctl::StaticArray<sctl::Long,2> dim;
  255. sctl::Vector<Real> normal_scal;
  256. sctl::Vector<sctl::Vector<Real>> Xtrg, Xsrc, dXsrc, Xn_src, Xa_src;
  257. sctl::Vector<sctl::Vector<SingularCorrection<Real,PATCH_DIM0,RAD_DIM,KDIM0,KDIM1>>> singular_correction;
  258. };
  259. }
  260. #endif //_BOUNDARY_INTEG_OP_HPP_