slender_element.txx 130 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857
  1. #include SCTL_INCLUDE(kernel_functions.hpp)
  2. #include SCTL_INCLUDE(tensor.hpp)
  3. #include SCTL_INCLUDE(quadrule.hpp)
  4. #include SCTL_INCLUDE(ompUtils.hpp)
  5. #include SCTL_INCLUDE(profile.hpp)
  6. #include SCTL_INCLUDE(legendre_rule.hpp)
  7. #include SCTL_INCLUDE(fft_wrapper.hpp)
  8. #include SCTL_INCLUDE(vtudata.hpp)
  9. #include SCTL_INCLUDE(lagrange-interp.hpp)
  10. #include <functional>
  11. namespace SCTL_NAMESPACE {
  12. template <class Real, Integer Nm, Integer Nr, Integer Nt> template <class Kernel> void ToroidalGreensFn<Real,Nm,Nr,Nt>::Setup(const Kernel& ker, Real R0) {
  13. #ifdef SCTL_QUAD_T
  14. using ValueType = QuadReal;
  15. #else
  16. using ValueType = long double;
  17. #endif
  18. PrecompToroidalGreensFn<ValueType>(ker, R0);
  19. }
  20. template <class Real, Integer Nm, Integer Nr, Integer Nt> template <class Kernel> void ToroidalGreensFn<Real,Nm,Nr,Nt>::BuildOperatorModal(Matrix<Real>& M, const Real x0, const Real x1, const Real x2, const Kernel& ker) const {
  21. constexpr Integer KDIM0 = Kernel::SrcDim();
  22. constexpr Integer KDIM1 = Kernel::TrgDim();
  23. constexpr Integer Nmm = (Nm/2+1)*2;
  24. constexpr Integer Ntt = (Nt/2+1)*2;
  25. StaticArray<Real,2*Nr> buff0;
  26. StaticArray<Real,Ntt> buff1;
  27. Vector<Real> r_basis(Nr,buff0,false);
  28. Vector<Real> interp_r(Nr,buff0+Nr,false);
  29. Vector<Real> interp_Ntt(Ntt,buff1,false);
  30. if (M.Dim(0) != KDIM0*Nmm || M.Dim(1) != KDIM1) M.ReInit(KDIM0*Nmm,KDIM1);
  31. { // Set M
  32. const Real r = sqrt<Real>(x0*x0 + x1*x1);
  33. const Real rho = sqrt<Real>((r-R0_)*(r-R0_) + x2*x2);
  34. if (rho < max_dist*R0_) {
  35. const Real r_inv = 1/r;
  36. const Real rho_inv = 1/rho;
  37. const Real cos_theta = x0*r_inv;
  38. const Real sin_theta = x1*r_inv;
  39. const Real cos_phi = x2*rho_inv;
  40. const Real sin_phi = (r-R0_)*rho_inv;
  41. { // Set interp_r
  42. interp_r = 0;
  43. const Real rho0 = (rho/R0_-min_dist)/(max_dist-min_dist);
  44. BasisFn<Real>::EvalBasis(r_basis, rho0);
  45. for (Long i = 0; i < Nr; i++) {
  46. Real fn_val = 0;
  47. for (Long j = 0; j < Nr; j++) {
  48. fn_val += Mnds2coeff1[0][i*Nr+j] * r_basis[j];
  49. }
  50. for (Long j = 0; j < Nr; j++) {
  51. interp_r[j] += Mnds2coeff0[0][i*Nr+j] * fn_val;
  52. }
  53. }
  54. }
  55. { // Set interp_Ntt
  56. interp_Ntt[0] = 0.5;
  57. interp_Ntt[1] = 0.0;
  58. Complex<Real> exp_t(cos_phi, sin_phi);
  59. Complex<Real> exp_jt(cos_phi, sin_phi);
  60. for (Long j = 1; j < Ntt/2; j++) {
  61. interp_Ntt[j*2+0] = exp_jt.real;
  62. interp_Ntt[j*2+1] =-exp_jt.imag;
  63. exp_jt *= exp_t;
  64. }
  65. }
  66. M = 0;
  67. for (Long j = 0; j < Nr; j++) {
  68. for (Long k = 0; k < Ntt; k++) {
  69. Real interp_wt = interp_r[j] * interp_Ntt[k];
  70. ConstIterator<Real> Ut_ = Ut.begin() + (j*Ntt+k)*KDIM0*Nmm*KDIM1;
  71. for (Long i = 0; i < KDIM0*Nmm*KDIM1; i++) { // Set M
  72. M[0][i] += Ut_[i] * interp_wt;
  73. }
  74. }
  75. }
  76. { // Rotate by theta
  77. Complex<Real> exp_iktheta(1,0), exp_itheta(cos_theta, -sin_theta);
  78. for (Long k = 0; k < Nmm/2; k++) {
  79. for (Long i = 0; i < KDIM0; i++) {
  80. for (Long j = 0; j < KDIM1; j++) {
  81. Complex<Real> c(M[i*Nmm+2*k+0][j],M[i*Nmm+2*k+1][j]);
  82. c *= exp_iktheta;
  83. M[i*Nmm+2*k+0][j] = c.real;
  84. M[i*Nmm+2*k+1][j] = c.imag;
  85. }
  86. }
  87. exp_iktheta *= exp_itheta;
  88. }
  89. }
  90. } else if (rho < max_dist*R0_*1.25) {
  91. BuildOperatorModalDirect<110>(M, x0, x1, x2, ker);
  92. } else if (rho < max_dist*R0_*1.67) {
  93. BuildOperatorModalDirect<88>(M, x0, x1, x2, ker);
  94. } else if (rho < max_dist*R0_*2.5) {
  95. BuildOperatorModalDirect<76>(M, x0, x1, x2, ker);
  96. } else if (rho < max_dist*R0_*5) {
  97. BuildOperatorModalDirect<50>(M, x0, x1, x2, ker);
  98. } else if (rho < max_dist*R0_*10) {
  99. BuildOperatorModalDirect<25>(M, x0, x1, x2, ker);
  100. } else if (rho < max_dist*R0_*20) {
  101. BuildOperatorModalDirect<14>(M, x0, x1, x2, ker);
  102. } else {
  103. BuildOperatorModalDirect<Nm>(M, x0, x1, x2, ker);
  104. }
  105. }
  106. }
  107. template <class Real, Integer Nm, Integer Nr, Integer Nt> template <class ValueType> ValueType ToroidalGreensFn<Real,Nm,Nr,Nt>::BasisFn<ValueType>::Eval(const Vector<ValueType>& coeff, ValueType x) {
  108. if (1) {
  109. ValueType sum = 0;
  110. ValueType log_x = log(x);
  111. Long Nsplit = std::max<Long>(0,(coeff.Dim()-1)/2);
  112. ValueType x_i = 1;
  113. for (Long i = 0; i < Nsplit; i++) {
  114. sum += coeff[i] * x_i;
  115. x_i *= x;
  116. }
  117. x_i = 1;
  118. for (Long i = coeff.Dim()-2; i >= Nsplit; i--) {
  119. sum += coeff[i] * log_x * x_i;
  120. x_i *= x;
  121. }
  122. if (coeff.Dim()-1 >= 0) sum += coeff[coeff.Dim()-1] / x;
  123. return sum;
  124. }
  125. if (0) {
  126. ValueType sum = 0;
  127. Long Nsplit = coeff.Dim()/2;
  128. for (Long i = 0; i < Nsplit; i++) {
  129. sum += coeff[i] * sctl::pow<ValueType,Long>(x,i);
  130. }
  131. for (Long i = Nsplit; i < coeff.Dim(); i++) {
  132. sum += coeff[i] * log(x) * sctl::pow<ValueType,Long>(x,coeff.Dim()-1-i);
  133. }
  134. return sum;
  135. }
  136. }
  137. template <class Real, Integer Nm, Integer Nr, Integer Nt> template <class ValueType> void ToroidalGreensFn<Real,Nm,Nr,Nt>::BasisFn<ValueType>::EvalBasis(Vector<ValueType>& f, ValueType x) {
  138. const Long N = f.Dim();
  139. const Long Nsplit = std::max<Long>(0,(N-1)/2);
  140. ValueType xi = 1;
  141. for (Long i = 0; i < Nsplit; i++) {
  142. f[i] = xi;
  143. xi *= x;
  144. }
  145. ValueType xi_logx = log(x);
  146. for (Long i = N-2; i >= Nsplit; i--) {
  147. f[i] = xi_logx;
  148. xi_logx *= x;
  149. }
  150. if (N-1 >= 0) f[N-1] = 1/x;
  151. }
  152. template <class Real, Integer Nm, Integer Nr, Integer Nt> template <class ValueType> const Vector<ValueType>& ToroidalGreensFn<Real,Nm,Nr,Nt>::BasisFn<ValueType>::nds(Integer ORDER) {
  153. ValueType fn_start = 1e-7, fn_end = 1.0;
  154. auto compute_nds = [&ORDER,&fn_start,&fn_end]() {
  155. Vector<ValueType> nds, wts;
  156. auto integrands = [&ORDER,&fn_start,&fn_end](const Vector<ValueType>& nds) {
  157. const Integer K = ORDER;
  158. const Long N = nds.Dim();
  159. Matrix<ValueType> M(N,K);
  160. for (Long j = 0; j < N; j++) {
  161. Vector<ValueType> f(K,M[j],false);
  162. EvalBasis(f, nds[j]*(fn_end-fn_start)+fn_start);
  163. }
  164. return M;
  165. };
  166. InterpQuadRule<ValueType>::Build(nds, wts, integrands, sqrt(machine_eps<ValueType>()), ORDER);
  167. return nds*(fn_end-fn_start)+fn_start;
  168. };
  169. static Vector<ValueType> nds = compute_nds();
  170. return nds;
  171. }
  172. template <class Real, Integer Nm, Integer Nr, Integer Nt> template <class ValueType, class Kernel> void ToroidalGreensFn<Real,Nm,Nr,Nt>::PrecompToroidalGreensFn(const Kernel& ker, ValueType R0) {
  173. SCTL_ASSERT(ker.CoordDim() == COORD_DIM);
  174. constexpr Integer KDIM0 = Kernel::SrcDim();
  175. constexpr Integer KDIM1 = Kernel::TrgDim();
  176. constexpr Long Nmm = (Nm/2+1)*2;
  177. constexpr Long Ntt = (Nt/2+1)*2;
  178. R0_ = (Real)R0;
  179. const auto& nds = BasisFn<ValueType>::nds(Nr);
  180. { // Set Mnds2coeff0, Mnds2coeff1
  181. Matrix<ValueType> M(Nr,Nr);
  182. Vector<ValueType> coeff(Nr); coeff = 0;
  183. for (Long i = 0; i < Nr; i++) {
  184. coeff[i] = 1;
  185. for (Long j = 0; j < Nr; j++) {
  186. M[i][j] = BasisFn<ValueType>::Eval(coeff, nds[j]);
  187. }
  188. coeff[i] = 0;
  189. }
  190. Matrix<ValueType> U, S, Vt;
  191. M.SVD(U, S, Vt);
  192. for (Long i = 0; i < S.Dim(0); i++) {
  193. S[i][i] = 1/S[i][i];
  194. }
  195. auto Mnds2coeff0_ = S * Vt;
  196. auto Mnds2coeff1_ = U.Transpose();
  197. Mnds2coeff0.ReInit(Mnds2coeff0_.Dim(0), Mnds2coeff0_.Dim(1));
  198. Mnds2coeff1.ReInit(Mnds2coeff1_.Dim(0), Mnds2coeff1_.Dim(1));
  199. for (Long i = 0; i < Mnds2coeff0.Dim(0)*Mnds2coeff0.Dim(1); i++) Mnds2coeff0[0][i] = (Real)Mnds2coeff0_[0][i];
  200. for (Long i = 0; i < Mnds2coeff1.Dim(0)*Mnds2coeff1.Dim(1); i++) Mnds2coeff1[0][i] = (Real)Mnds2coeff1_[0][i];
  201. }
  202. { // Setup fft_Nm_R2C
  203. Vector<Long> dim_vec(1);
  204. dim_vec[0] = Nm;
  205. fft_Nm_R2C.Setup(FFT_Type::R2C, KDIM0, dim_vec);
  206. fft_Nm_C2R.Setup(FFT_Type::C2R, KDIM0*KDIM1, dim_vec);
  207. }
  208. Vector<ValueType> Xtrg(Nr*Nt*COORD_DIM);
  209. for (Long i = 0; i < Nr; i++) {
  210. for (Long j = 0; j < Nt; j++) {
  211. Xtrg[(i*Nt+j)*COORD_DIM+0] = R0 * (1.0 + (min_dist+(max_dist-min_dist)*nds[i]) * sin<ValueType>(j*2*const_pi<ValueType>()/Nt));
  212. Xtrg[(i*Nt+j)*COORD_DIM+1] = R0 * (0.0);
  213. Xtrg[(i*Nt+j)*COORD_DIM+2] = R0 * (0.0 + (min_dist+(max_dist-min_dist)*nds[i]) * cos<ValueType>(j*2*const_pi<ValueType>()/Nt));
  214. }
  215. }
  216. Vector<ValueType> U0(KDIM0*Nmm*Nr*KDIM1*Nt);
  217. { // Set U0
  218. FFT<ValueType> fft_Nm_C2R;
  219. { // Setup fft_Nm_C2R
  220. Vector<Long> dim_vec(1);
  221. dim_vec[0] = Nm;
  222. fft_Nm_C2R.Setup(FFT_Type::C2R, KDIM0, dim_vec);
  223. }
  224. Vector<ValueType> Fcoeff(KDIM0*Nmm), F, U_;
  225. for (Long i = 0; i < KDIM0*Nmm; i++) {
  226. Fcoeff = 0; Fcoeff[i] = 1;
  227. { // Set F
  228. fft_Nm_C2R.Execute(Fcoeff, F);
  229. Matrix<ValueType> FF(KDIM0,Nm,F.begin(), false);
  230. FF = FF.Transpose();
  231. }
  232. ComputePotential<ValueType>(U_, Xtrg, R0, F, ker);
  233. SCTL_ASSERT(U_.Dim() == Nr*Nt*KDIM1);
  234. for (Long j = 0; j < Nr; j++) {
  235. for (Long l = 0; l < Nt; l++) {
  236. for (Long k = 0; k < KDIM1; k++) {
  237. U0[((i*Nr+j)*KDIM1+k)*Nt+l] = U_[(j*Nt+l)*KDIM1+k];
  238. }
  239. }
  240. }
  241. }
  242. }
  243. Vector<ValueType> U1(KDIM0*Nmm*Nr*KDIM1*Ntt);
  244. { // U1 <-- fft_Nt(U0)
  245. FFT<ValueType> fft_Nt;
  246. Vector<Long> dim_vec(1); dim_vec = Nt;
  247. fft_Nt.Setup(FFT_Type::R2C, KDIM0*Nmm*Nr*KDIM1, dim_vec);
  248. fft_Nt.Execute(U0, U1);
  249. if (Nt%2==0 && Nt) {
  250. for (Long i = Ntt-2; i < U1.Dim(); i += Ntt) {
  251. U1[i] *= 0.5;
  252. }
  253. }
  254. U1 *= 1.0/sqrt<ValueType>(Nt);
  255. }
  256. U.ReInit(KDIM0*Nmm*KDIM1*Nr*Ntt);
  257. { // U <-- rearrange(U1)
  258. for (Long i0 = 0; i0 < KDIM0*Nmm; i0++) {
  259. for (Long i1 = 0; i1 < Nr; i1++) {
  260. for (Long i2 = 0; i2 < KDIM1; i2++) {
  261. for (Long i3 = 0; i3 < Ntt; i3++) {
  262. U[((i0*Nr+i1)*KDIM1+i2)*Ntt+i3] = (Real)U1[((i0*KDIM1+i2)*Nr+i1)*Ntt+i3];
  263. }
  264. }
  265. }
  266. }
  267. }
  268. Ut.ReInit(Nr*Ntt*KDIM0*Nmm*KDIM1);
  269. { // Set Ut
  270. Matrix<Real> Ut_(Nr*Ntt,KDIM0*Nmm*KDIM1, Ut.begin(), false);
  271. Matrix<Real> U_(KDIM0*Nmm*KDIM1,Nr*Ntt, U.begin(), false);
  272. Ut_ = U_.Transpose()*2.0;
  273. }
  274. }
  275. template <class Real, Integer Nm, Integer Nr, Integer Nt> template <class ValueType, class Kernel> void ToroidalGreensFn<Real,Nm,Nr,Nt>::ComputePotential(Vector<ValueType>& U, const Vector<ValueType>& Xtrg, ValueType R0, const Vector<ValueType>& F_, const Kernel& ker, ValueType tol) {
  276. constexpr Integer KDIM0 = Kernel::SrcDim();
  277. Vector<ValueType> F_fourier_coeff;
  278. const Long Nt_ = F_.Dim() / KDIM0; // number of Fourier modes
  279. SCTL_ASSERT(F_.Dim() == Nt_ * KDIM0);
  280. { // Transpose F_
  281. Matrix<ValueType> FF(Nt_,KDIM0,(Iterator<ValueType>)F_.begin(), false);
  282. FF = FF.Transpose();
  283. }
  284. { // Set F_fourier_coeff
  285. FFT<ValueType> fft_plan;
  286. Vector<Long> dim_vec(1); dim_vec[0] = Nt_;
  287. fft_plan.Setup(FFT_Type::R2C, KDIM0, dim_vec);
  288. fft_plan.Execute(F_, F_fourier_coeff);
  289. if (Nt_%2==0 && F_fourier_coeff.Dim()) {
  290. F_fourier_coeff[F_fourier_coeff.Dim()-2] *= 0.5;
  291. }
  292. }
  293. auto EvalFourierExp = [&Nt_](Vector<ValueType>& F, const Vector<ValueType>& F_fourier_coeff, Integer dof, const Vector<ValueType>& theta) {
  294. const Long N = F_fourier_coeff.Dim() / dof / 2;
  295. SCTL_ASSERT(F_fourier_coeff.Dim() == dof * N * 2);
  296. const Long Ntheta = theta.Dim();
  297. if (F.Dim() != Ntheta*dof) F.ReInit(Ntheta*dof);
  298. for (Integer k = 0; k < dof; k++) {
  299. for (Long j = 0; j < Ntheta; j++) {
  300. Complex<ValueType> F_(0,0);
  301. for (Long i = 0; i < N; i++) {
  302. Complex<ValueType> c(F_fourier_coeff[(k*N+i)*2+0],F_fourier_coeff[(k*N+i)*2+1]);
  303. Complex<ValueType> exp_t(cos<ValueType>(theta[j]*i), sin<ValueType>(theta[j]*i));
  304. F_ += exp_t * c * (i==0?1:2);
  305. }
  306. F[j*dof+k] = F_.real/sqrt<ValueType>(Nt_);
  307. }
  308. }
  309. };
  310. constexpr Integer QuadOrder = 18;
  311. std::function<Vector<ValueType>(ValueType,ValueType,ValueType)> compute_potential = [&](ValueType a, ValueType b, ValueType tol) -> Vector<ValueType> {
  312. auto GetGeomCircle = [&R0] (Vector<ValueType>& Xsrc, Vector<ValueType>& Nsrc, const Vector<ValueType>& nds) {
  313. Long N = nds.Dim();
  314. if (Xsrc.Dim() != N * COORD_DIM) Xsrc.ReInit(N*COORD_DIM);
  315. if (Nsrc.Dim() != N * COORD_DIM) Nsrc.ReInit(N*COORD_DIM);
  316. for (Long i = 0; i < N; i++) {
  317. Xsrc[i*COORD_DIM+0] = R0 * cos<ValueType>(nds[i]);
  318. Xsrc[i*COORD_DIM+1] = R0 * sin<ValueType>(nds[i]);
  319. Xsrc[i*COORD_DIM+2] = R0 * 0;
  320. Nsrc[i*COORD_DIM+0] = cos<ValueType>(nds[i]);
  321. Nsrc[i*COORD_DIM+1] = sin<ValueType>(nds[i]);
  322. Nsrc[i*COORD_DIM+2] = 0;
  323. }
  324. };
  325. const auto& nds0 = ChebQuadRule<ValueType>::nds(QuadOrder+1);
  326. const auto& wts0 = ChebQuadRule<ValueType>::wts(QuadOrder+1);
  327. const auto& nds1 = ChebQuadRule<ValueType>::nds(QuadOrder+0);
  328. const auto& wts1 = ChebQuadRule<ValueType>::wts(QuadOrder+0);
  329. Vector<ValueType> U0;
  330. Vector<ValueType> Xsrc, Nsrc, Fsrc;
  331. GetGeomCircle(Xsrc, Nsrc, a+(b-a)*nds0);
  332. EvalFourierExp(Fsrc, F_fourier_coeff, KDIM0, a+(b-a)*nds0);
  333. for (Long i = 0; i < nds0.Dim(); i++) {
  334. for (Long j = 0; j < KDIM0; j++) {
  335. Fsrc[i*KDIM0+j] *= ((b-a) * wts0[i]);
  336. }
  337. }
  338. ker.Eval(U0, Xtrg, Xsrc, Nsrc, Fsrc);
  339. Vector<ValueType> U1;
  340. GetGeomCircle(Xsrc, Nsrc, a+(b-a)*nds1);
  341. EvalFourierExp(Fsrc, F_fourier_coeff, KDIM0, a+(b-a)*nds1);
  342. for (Long i = 0; i < nds1.Dim(); i++) {
  343. for (Long j = 0; j < KDIM0; j++) {
  344. Fsrc[i*KDIM0+j] *= ((b-a) * wts1[i]);
  345. }
  346. }
  347. ker.Eval(U1, Xtrg, Xsrc, Nsrc, Fsrc);
  348. ValueType err = 0, max_val = 0;
  349. for (Long i = 0; i < U1.Dim(); i++) {
  350. err = std::max<ValueType>(err, fabs(U0[i]-U1[i]));
  351. max_val = std::max<ValueType>(max_val, fabs(U0[i]));
  352. }
  353. if (err < tol || (b-a)<tol) {
  354. //if ((a != 0 && b != 2*const_pi<ValueType>()) || (b-a)<tol) {
  355. std::cout<<a<<' '<<b-a<<' '<<err<<' '<<tol<<'\n';
  356. return U1;
  357. } else {
  358. U0 = compute_potential(a, (a+b)*0.5, tol);
  359. U1 = compute_potential((a+b)*0.5, b, tol);
  360. return U0 + U1;
  361. }
  362. };
  363. U = compute_potential(0, 2*const_pi<ValueType>(), tol);
  364. };
  365. template <class Real, Integer Nm, Integer Nr, Integer Nt> template <Integer Nnds, class Kernel> void ToroidalGreensFn<Real,Nm,Nr,Nt>::BuildOperatorModalDirect(Matrix<Real>& M, const Real x0, const Real x1, const Real x2, const Kernel& ker) const {
  366. constexpr Integer KDIM0 = Kernel::SrcDim();
  367. constexpr Integer KDIM1 = Kernel::TrgDim();
  368. constexpr Integer Nmm = (Nm/2+1)*2;
  369. auto get_sin_theta = [](Long N){
  370. Vector<Real> sin_theta(N);
  371. for (Long i = 0; i < N; i++) {
  372. sin_theta[i] = sin<Real>(2*const_pi<Real>()*i/N);
  373. }
  374. return sin_theta;
  375. };
  376. auto get_cos_theta = [](Long N){
  377. Vector<Real> cos_theta(N);
  378. for (Long i = 0; i < N; i++) {
  379. cos_theta[i] = cos<Real>(2*const_pi<Real>()*i/N);
  380. }
  381. return cos_theta;
  382. };
  383. auto get_circle_coord = [](Long N, Real R0){
  384. Vector<Real> X(N*COORD_DIM);
  385. for (Long i = 0; i < N; i++) {
  386. X[i*COORD_DIM+0] = R0*cos<Real>(2*const_pi<Real>()*i/N);
  387. X[i*COORD_DIM+1] = R0*sin<Real>(2*const_pi<Real>()*i/N);
  388. X[i*COORD_DIM+2] = 0;
  389. }
  390. return X;
  391. };
  392. constexpr Real scal = 2/sqrt<Real>(Nm);
  393. static const Vector<Real> sin_nds = get_sin_theta(Nnds);
  394. static const Vector<Real> cos_nds = get_cos_theta(Nnds);
  395. static const Vector<Real> Xn = get_circle_coord(Nnds,1);
  396. StaticArray<Real,Nnds*COORD_DIM> buff0;
  397. Vector<Real> Xs(Nnds*COORD_DIM,buff0,false);
  398. Xs = Xn * R0_;
  399. StaticArray<Real,COORD_DIM> Xt = {x0,x1,x2};
  400. StaticArray<Real,KDIM0*KDIM1*Nnds> mem_buff2;
  401. Matrix<Real> Mker(KDIM0*Nnds, KDIM1, mem_buff2, false);
  402. ker.KernelMatrix(Mker, Vector<Real>(COORD_DIM,(Iterator<Real>)Xt,false), Xs, Xn);
  403. StaticArray<Real,4*Nnds> mem_buff3;
  404. Vector<Complex<Real>> exp_itheta(Nnds, (Iterator<Complex<Real>>)(mem_buff3+0*Nnds), false);
  405. Vector<Complex<Real>> exp_iktheta_da(Nnds, (Iterator<Complex<Real>>)(mem_buff3+2*Nnds), false);
  406. for (Integer j = 0; j < Nnds; j++) {
  407. exp_itheta[j].real = cos_nds[j];
  408. exp_itheta[j].imag =-sin_nds[j];
  409. exp_iktheta_da[j].real = 2*const_pi<Real>()/Nnds*scal;
  410. exp_iktheta_da[j].imag = 0;
  411. }
  412. for (Integer k = 0; k < Nmm/2; k++) { // apply Mker to complex exponentials
  413. // TODO: FFT might be faster since points are uniform
  414. Tensor<Real,true,KDIM0,KDIM1> Mk0, Mk1;
  415. for (Integer i0 = 0; i0 < KDIM0; i0++) {
  416. for (Integer i1 = 0; i1 < KDIM1; i1++) {
  417. Mk0(i0,i1) = 0;
  418. Mk1(i0,i1) = 0;
  419. }
  420. }
  421. for (Integer j = 0; j < Nnds; j++) {
  422. Tensor<Real,false,KDIM0,KDIM1> Mker_(Mker[j*KDIM0]);
  423. Mk0 = Mk0 + Mker_ * exp_iktheta_da[j].real;
  424. Mk1 = Mk1 + Mker_ * exp_iktheta_da[j].imag;
  425. }
  426. for (Integer i0 = 0; i0 < KDIM0; i0++) {
  427. for (Integer i1 = 0; i1 < KDIM1; i1++) {
  428. M[i0*Nmm+(k*2+0)][i1] = Mk0(i0,i1);
  429. M[i0*Nmm+(k*2+1)][i1] = Mk1(i0,i1);
  430. }
  431. }
  432. exp_iktheta_da *= exp_itheta;
  433. }
  434. for (Integer i0 = 0; i0 < KDIM0; i0++) {
  435. for (Integer i1 = 0; i1 < KDIM1; i1++) {
  436. M[i0*Nmm+0][i1] *= 0.5;
  437. M[i0*Nmm+1][i1] *= 0.5;
  438. if (Nm%2 == 0) {
  439. M[(i0+1)*Nmm-2][i1] *= 0.5;
  440. M[(i0+1)*Nmm-1][i1] *= 0.5;
  441. }
  442. }
  443. }
  444. }
  445. template <class ValueType> static void ReadFile(Vector<Vector<ValueType>>& data, const std::string fname) {
  446. FILE* f = fopen(fname.c_str(), "r");
  447. if (f == nullptr) {
  448. std::cout << "Unable to open file for reading:" << fname << '\n';
  449. } else {
  450. uint64_t data_len;
  451. Long readlen = fread(&data_len, sizeof(uint64_t), 1, f);
  452. SCTL_ASSERT(readlen == 1);
  453. if (data_len) {
  454. data.ReInit(data_len);
  455. for (Long i = 0; i < data.Dim(); i++) {
  456. readlen = fread(&data_len, sizeof(uint64_t), 1, f);
  457. SCTL_ASSERT(readlen == 1);
  458. data[i].ReInit(data_len);
  459. if (data_len) {
  460. readlen = fread(&data[i][0], sizeof(ValueType), data_len, f);
  461. SCTL_ASSERT(readlen == (Long)data_len);
  462. }
  463. }
  464. }
  465. fclose(f);
  466. }
  467. }
  468. template <class ValueType> static void WriteFile(const Vector<Vector<ValueType>>& data, const std::string fname) {
  469. FILE* f = fopen(fname.c_str(), "wb+");
  470. if (f == nullptr) {
  471. std::cout << "Unable to open file for writing:" << fname << '\n';
  472. exit(0);
  473. }
  474. uint64_t data_len = data.Dim();
  475. fwrite(&data_len, sizeof(uint64_t), 1, f);
  476. for (Integer i = 0; i < data.Dim(); i++) {
  477. data_len = data[i].Dim();
  478. fwrite(&data_len, sizeof(uint64_t), 1, f);
  479. if (data_len) fwrite(&data[i][0], sizeof(ValueType), data_len, f);
  480. }
  481. fclose(f);
  482. }
  483. template <class ValueType> static ValueType dot_prod(const Tensor<ValueType,true,3,1>& u, const Tensor<ValueType,true,3,1>& v) {
  484. ValueType u_dot_v = 0;
  485. u_dot_v += u(0,0) * v(0,0);
  486. u_dot_v += u(1,0) * v(1,0);
  487. u_dot_v += u(2,0) * v(2,0);
  488. return u_dot_v;
  489. }
  490. template <class ValueType> static Tensor<ValueType,true,3,1> cross_prod(const Tensor<ValueType,true,3,1>& u, const Tensor<ValueType,true,3,1>& v) {
  491. Tensor<ValueType,true,3,1> uxv;
  492. uxv(0,0) = u(1,0) * v(2,0) - u(2,0) * v(1,0);
  493. uxv(1,0) = u(2,0) * v(0,0) - u(0,0) * v(2,0);
  494. uxv(2,0) = u(0,0) * v(1,0) - u(1,0) * v(0,0);
  495. return uxv;
  496. }
  497. template <class Real> static const Vector<Real>& sin_theta(const Integer ORDER) {
  498. constexpr Integer MaxOrder = 256;
  499. auto compute_sin_theta = [MaxOrder](){
  500. Vector<Vector<Real>> sin_theta_lst(MaxOrder);
  501. for (Long k = 0; k < MaxOrder; k++) {
  502. sin_theta_lst[k].ReInit(k);
  503. for (Long i = 0; i < k; i++) {
  504. sin_theta_lst[k][i] = sin<Real>(2*const_pi<Real>()*i/k);
  505. }
  506. }
  507. return sin_theta_lst;
  508. };
  509. static const auto sin_theta_lst = compute_sin_theta();
  510. SCTL_ASSERT(ORDER < MaxOrder);
  511. return sin_theta_lst[ORDER];
  512. }
  513. template <class Real> static const Vector<Real>& cos_theta(const Integer ORDER) {
  514. constexpr Integer MaxOrder = 256;
  515. auto compute_cos_theta = [MaxOrder](){
  516. Vector<Vector<Real>> cos_theta_lst(MaxOrder);
  517. for (Long k = 0; k < MaxOrder; k++) {
  518. cos_theta_lst[k].ReInit(k);
  519. for (Long i = 0; i < k; i++) {
  520. cos_theta_lst[k][i] = cos<Real>(2*const_pi<Real>()*i/k);
  521. }
  522. }
  523. return cos_theta_lst;
  524. };
  525. static const auto cos_theta_lst = compute_cos_theta();
  526. SCTL_ASSERT(ORDER < MaxOrder);
  527. return cos_theta_lst[ORDER];
  528. }
  529. template <class Real> static const Matrix<Real>& fourier_matrix(Integer Nmodes, Integer Nnodes) {
  530. constexpr Integer MaxOrder = 128;
  531. auto compute_fourier_matrix = [](Integer Nmodes, Integer Nnodes) {
  532. if (Nnodes == 0 || Nmodes == 0) return Matrix<Real>();
  533. Matrix<Real> M_fourier(2*Nmodes,Nnodes);
  534. for (Long i = 0; i < Nnodes; i++) {
  535. Real theta = 2*const_pi<Real>()*i/Nnodes;
  536. for (Long k = 0; k < Nmodes; k++) {
  537. M_fourier[k*2+0][i] = cos<Real>(k*theta);
  538. M_fourier[k*2+1][i] = sin<Real>(k*theta);
  539. }
  540. }
  541. return M_fourier;
  542. };
  543. auto compute_all = [&compute_fourier_matrix, MaxOrder]() {
  544. Matrix<Matrix<Real>> Mall(MaxOrder, MaxOrder);
  545. for (Long i = 0; i < MaxOrder; i++) {
  546. for (Long j = 0; j < MaxOrder; j++) {
  547. Mall[i][j] = compute_fourier_matrix(i,j);
  548. }
  549. }
  550. return Mall;
  551. };
  552. static const Matrix<Matrix<Real>> Mall = compute_all();
  553. SCTL_ASSERT(Nmodes < MaxOrder && Nnodes < MaxOrder);
  554. return Mall[Nmodes][Nnodes];
  555. }
  556. template <class Real> static const Matrix<Real>& fourier_matrix_inv(Integer Nnodes, Integer Nmodes) {
  557. constexpr Integer MaxOrder = 128;
  558. auto compute_fourier_matrix_inv = [](Integer Nnodes, Integer Nmodes) {
  559. if (Nmodes > Nnodes/2+1 || Nnodes == 0 || Nmodes == 0) return Matrix<Real>();
  560. const Real scal = 2/(Real)Nnodes;
  561. Matrix<Real> M_fourier_inv(Nnodes,2*Nmodes);
  562. for (Long i = 0; i < Nnodes; i++) {
  563. Real theta = 2*const_pi<Real>()*i/Nnodes;
  564. for (Long k = 0; k < Nmodes; k++) {
  565. M_fourier_inv[i][k*2+0] = cos<Real>(k*theta)*scal;
  566. M_fourier_inv[i][k*2+1] = sin<Real>(k*theta)*scal;
  567. }
  568. }
  569. for (Long i = 0; i < Nnodes; i++) {
  570. M_fourier_inv[i][0] *= 0.5;
  571. }
  572. if (Nnodes == (Nmodes-1)*2) {
  573. for (Long i = 0; i < Nnodes; i++) {
  574. M_fourier_inv[i][Nnodes] *= 0.5;
  575. }
  576. }
  577. return M_fourier_inv;
  578. };
  579. auto compute_all = [&compute_fourier_matrix_inv, MaxOrder]() {
  580. Matrix<Matrix<Real>> Mall(MaxOrder, MaxOrder);
  581. for (Long i = 0; i < MaxOrder; i++) {
  582. for (Long j = 0; j < MaxOrder; j++) {
  583. Mall[i][j] = compute_fourier_matrix_inv(i,j);
  584. }
  585. }
  586. return Mall;
  587. };
  588. static const Matrix<Matrix<Real>> Mall = compute_all();
  589. SCTL_ASSERT(Nnodes < MaxOrder && Nmodes < MaxOrder);
  590. return Mall[Nnodes][Nmodes];
  591. }
  592. template <class Real> static const Matrix<Real>& fourier_matrix_inv_transpose(Integer Nnodes, Integer Nmodes) {
  593. constexpr Integer MaxOrder = 128;
  594. auto compute_all = [MaxOrder]() {
  595. Matrix<Matrix<Real>> Mall(MaxOrder, MaxOrder);
  596. for (Long i = 0; i < MaxOrder; i++) {
  597. for (Long j = 0; j < MaxOrder; j++) {
  598. Mall[i][j] = fourier_matrix_inv<Real>(i,j).Transpose();
  599. }
  600. }
  601. return Mall;
  602. };
  603. static const Matrix<Matrix<Real>> Mall = compute_all();
  604. SCTL_ASSERT(Nnodes < MaxOrder && Nmodes < MaxOrder);
  605. return Mall[Nnodes][Nmodes];
  606. }
  607. template <class ValueType> static const std::pair<Vector<ValueType>,Vector<ValueType>>& LegendreQuadRule(Integer ORDER) {
  608. constexpr Integer max_order = 50;
  609. auto compute_nds_wts = [max_order]() {
  610. Vector<std::pair<Vector<ValueType>,Vector<ValueType>>> nds_wts(max_order);
  611. for (Integer order = 1; order < max_order; order++) {
  612. auto& x_ = nds_wts[order].first;
  613. auto& w_ = nds_wts[order].second;
  614. x_ = LegQuadRule<ValueType>::ComputeNds(order);
  615. w_ = LegQuadRule<ValueType>::ComputeWts(x_);
  616. }
  617. return nds_wts;
  618. };
  619. static const auto nds_wts = compute_nds_wts();
  620. SCTL_ASSERT(ORDER < max_order);
  621. return nds_wts[ORDER];
  622. }
  623. template <class ValueType> static const std::pair<Vector<ValueType>,Vector<ValueType>>& LogSingularityQuadRule(Integer ORDER) {
  624. constexpr Integer MaxOrder = 50;
  625. auto compute_nds_wts_lst = [MaxOrder]() {
  626. #ifdef SCTL_QUAD_T
  627. using RealType = QuadReal;
  628. #else
  629. using RealType = long double;
  630. #endif
  631. Vector<Vector<RealType>> data;
  632. ReadFile<RealType>(data, "data/log_quad");
  633. if (data.Dim() < MaxOrder*2) {
  634. data.ReInit(MaxOrder*2);
  635. #pragma omp parallel for
  636. for (Integer order = 1; order < MaxOrder; order++) {
  637. auto integrands = [order](const Vector<RealType>& nds) {
  638. const Integer K = order;
  639. const Long N = nds.Dim();
  640. Matrix<RealType> M(N,K);
  641. for (Long j = 0; j < N; j++) {
  642. for (Long i = 0; i < (K+1)/2; i++) {
  643. M[j][i] = pow<RealType,Long>(nds[j],i);
  644. }
  645. for (Long i = (K+1)/2; i < K; i++) {
  646. M[j][i] = pow<RealType,Long>(nds[j],i-(K+1)/2) * log<RealType>(nds[j]);
  647. }
  648. }
  649. return M;
  650. };
  651. InterpQuadRule<RealType, true>::Build(data[order*2+0], data[order*2+1], integrands, false, machine_eps<ValueType>(), order, 2e-4, 1.0);
  652. }
  653. WriteFile<RealType>(data, "data/log_quad");
  654. }
  655. Vector<std::pair<Vector<ValueType>,Vector<ValueType>>> nds_wts_lst(MaxOrder);
  656. #pragma omp parallel for
  657. for (Integer order = 1; order < MaxOrder; order++) {
  658. const auto& nds = data[order*2+0];
  659. const auto& wts = data[order*2+1];
  660. auto& nds_ = nds_wts_lst[order].first;
  661. auto& wts_ = nds_wts_lst[order].second;
  662. nds_.ReInit(nds.Dim());
  663. wts_.ReInit(wts.Dim());
  664. for (Long i = 0; i < nds.Dim(); i++) {
  665. nds_[i] = (ValueType)nds[i];
  666. wts_[i] = (ValueType)wts[i];
  667. }
  668. }
  669. return nds_wts_lst;
  670. };
  671. static const auto nds_wts_lst = compute_nds_wts_lst();
  672. SCTL_ASSERT(ORDER < MaxOrder);
  673. return nds_wts_lst[ORDER];
  674. }
  675. template <class RealType, class Kernel, Integer adap> static Vector<Vector<RealType>> BuildToroidalSpecialQuadRules(Integer Nmodes, Integer VecLen) {
  676. const std::string fname = std::string("data/toroidal_quad_rule_m") + std::to_string(Nmodes) + "_" + Kernel::Name();
  677. constexpr Integer COORD_DIM = 3;
  678. constexpr Integer max_adap_depth = 30; // build quadrature rules for points up to 2*pi*0.5^max_adap_depth from source loop
  679. constexpr Integer crossover_adap_depth = 2;
  680. constexpr Integer max_digits = 20;
  681. #ifdef SCTL_QUAD_T
  682. using ValueType = QuadReal;
  683. #else
  684. using ValueType = long double;
  685. #endif
  686. auto DyadicPanelQuadRule = [](Vector<ValueType>& nds, Vector<ValueType>& wts, const Integer depth, const Integer LegOrder, const Integer PanelRepeat) {
  687. Vector<ValueType> panel_nds, panel_wts;
  688. { // Set panel_nds, panel_wts
  689. auto leg_quad = LegendreQuadRule<ValueType>(LegOrder);
  690. const auto& leg_nds = leg_quad.first;
  691. const auto& leg_wts = leg_quad.second;
  692. const Long rep = PanelRepeat;
  693. const ValueType scal = 1/(ValueType)rep;
  694. for (Long i = 0; i < rep; i++) {
  695. for (Long j = 0; j < leg_nds.Dim(); j++) {
  696. panel_nds.PushBack(leg_nds[j]*scal + i*scal);
  697. panel_wts.PushBack(leg_wts[j]*scal);
  698. }
  699. }
  700. }
  701. SCTL_ASSERT(depth);
  702. Long N = 2*depth;
  703. ValueType l = 0.5;
  704. nds.ReInit(N*panel_nds.Dim());
  705. wts.ReInit(N*panel_nds.Dim());
  706. for (Integer idx = 0; idx < depth; idx++) {
  707. l *= (idx<depth-1 ? 0.5 : 1.0);
  708. Vector<ValueType> nds0(panel_nds.Dim(), nds.begin()+( idx )*panel_nds.Dim(), false);
  709. Vector<ValueType> nds1(panel_nds.Dim(), nds.begin()+(N-idx-1)*panel_nds.Dim(), false);
  710. Vector<ValueType> wts0(panel_wts.Dim(), wts.begin()+( idx )*panel_wts.Dim(), false);
  711. Vector<ValueType> wts1(panel_wts.Dim(), wts.begin()+(N-idx-1)*panel_wts.Dim(), false);
  712. for (Long i = 0; i < panel_nds.Dim(); i++) {
  713. ValueType s = panel_nds[i]*l + (idx<depth-1 ? l : 0);
  714. nds0[ i] = s;
  715. nds1[panel_nds.Dim()-1-i] =-s;
  716. wts0[ i] = panel_wts[i]*l;
  717. wts1[panel_nds.Dim()-1-i] = wts0[i];
  718. }
  719. }
  720. };
  721. Vector<Vector<ValueType>> data;
  722. if (!adap) { // read from file
  723. ReadFile(data, fname);
  724. } else { // use dyadically refined panel quadrature rules
  725. data.ReInit(max_adap_depth * max_digits);
  726. for (Integer idx = 0; idx < max_adap_depth; idx++) {
  727. const ValueType dist = 4*const_pi<ValueType>()*pow<ValueType,Long>(0.5,idx);
  728. const Integer DyadicRefDepth = std::max<Integer>(1,(Integer)(log(dist/2/const_pi<ValueType>())/log(0.5)+0.5));
  729. Vector<ValueType> quad_nds, quad_wts;
  730. for (Integer digits = 0; digits < max_digits; digits++) {
  731. const Integer LegOrder = (Integer)(digits*1.5);
  732. DyadicPanelQuadRule(quad_nds, quad_wts, DyadicRefDepth, LegOrder, adap);
  733. const Long N = quad_nds.Dim();
  734. data[idx*max_digits+digits].ReInit(3*N);
  735. for (Long i = 0; i < N; i++) {
  736. data[idx*max_digits+digits][i*3+0] = cos<ValueType>(2*const_pi<ValueType>()*quad_nds[i]);
  737. data[idx*max_digits+digits][i*3+1] = sin<ValueType>(2*const_pi<ValueType>()*quad_nds[i]);
  738. data[idx*max_digits+digits][i*3+2] = (2*const_pi<ValueType>()*quad_wts[i]);
  739. }
  740. }
  741. }
  742. }
  743. if (!adap && data.Dim() != max_adap_depth*max_digits) { // If file is not-found then compute quadrature rule and write to file
  744. data.ReInit(max_adap_depth * max_digits);
  745. for (Integer idx = 0; idx < max_adap_depth; idx++) {
  746. Vector<Vector<ValueType>> quad_nds, quad_wts;
  747. { // generate special quadrature rule
  748. Vector<ValueType> nds, wts;
  749. Matrix<ValueType> Mintegrands;
  750. auto discretize_basis_functions = [Nmodes,&DyadicPanelQuadRule](Matrix<ValueType>& Mintegrands, Vector<ValueType>& nds, Vector<ValueType>& wts, const ValueType dist, const Integer LegOrder) {
  751. auto trg_coord = [](ValueType dist, Long M) {
  752. Vector<ValueType> Xtrg; //(M*M*COORD_DIM);
  753. for (Long i = 0; i < M; i++) {
  754. for (Long j = 0; j < M; j++) {
  755. ValueType theta = i*2*const_pi<ValueType>()/(M);
  756. ValueType r = (0.5 + i*0.5/(M)) * dist;
  757. ValueType x0 = r*cos<ValueType>(theta);
  758. ValueType x1 = 0;
  759. ValueType x2 = r*sin<ValueType>(theta);
  760. if (x0 > 0) {
  761. Xtrg.PushBack(x0);
  762. Xtrg.PushBack(x1);
  763. Xtrg.PushBack(x2);
  764. }
  765. }
  766. }
  767. return Xtrg;
  768. };
  769. const Vector<ValueType> Xtrg = trg_coord(dist, 25); // TODO: determine optimal sample count
  770. const Long Ntrg = Xtrg.Dim()/COORD_DIM;
  771. const Integer DyadicRefDepth = std::max<Integer>(1,(Integer)(log(dist/2/const_pi<ValueType>())/log(0.5)+0.5));
  772. DyadicPanelQuadRule(nds, wts, DyadicRefDepth, LegOrder, 1);
  773. const Long Nnds = nds.Dim();
  774. Vector<Complex<ValueType>> exp_itheta(Nnds), exp_iktheta(Nnds);
  775. Vector<ValueType> Xsrc(Nnds*COORD_DIM), Xn(Nnds*COORD_DIM);
  776. for (Long i = 0; i < Nnds; i++) {
  777. const ValueType cos_t = cos<ValueType>(2*const_pi<ValueType>()*nds[i]);
  778. const ValueType sin_t = sin<ValueType>(2*const_pi<ValueType>()*nds[i]);
  779. exp_iktheta[i].real = 1;
  780. exp_iktheta[i].imag = 0;
  781. exp_itheta[i].real = cos_t;
  782. exp_itheta[i].imag = sin_t;
  783. Xsrc[i*COORD_DIM+0] = -2*sin<ValueType>(const_pi<ValueType>()*nds[i])*sin<ValueType>(const_pi<ValueType>()*nds[i]); // == cos_t - 1
  784. Xsrc[i*COORD_DIM+1] = sin_t;
  785. Xsrc[i*COORD_DIM+2] = 0;
  786. Xn[i*COORD_DIM+0] = cos_t;
  787. Xn[i*COORD_DIM+1] = sin_t;
  788. Xn[i*COORD_DIM+2] = 0;
  789. }
  790. Kernel ker;
  791. Matrix<ValueType> Mker;
  792. ker.KernelMatrix(Mker, Xtrg, Xsrc, Xn);
  793. SCTL_ASSERT(Mker.Dim(0) == Nnds * Kernel::SrcDim());
  794. SCTL_ASSERT(Mker.Dim(1) == Ntrg * Kernel::TrgDim());
  795. Mintegrands.ReInit(Nnds, (Nmodes*2)*Kernel::SrcDim() * Ntrg*Kernel::TrgDim());
  796. for (Long k = 0; k < Nmodes; k++) {
  797. for (Long i = 0; i < Nnds; i++) {
  798. for (Long j = 0; j < Ntrg; j++) {
  799. for (Long k0 = 0; k0 < Kernel::SrcDim(); k0++) {
  800. for (Long k1 = 0; k1 < Kernel::TrgDim(); k1++) {
  801. Mintegrands[i][(((k*2+0)*Kernel::SrcDim()+k0) *Ntrg+j)*Kernel::TrgDim()+k1] = Mker[i*Kernel::SrcDim()+k0][j*Kernel::TrgDim()+k1] * exp_iktheta[i].real;
  802. Mintegrands[i][(((k*2+1)*Kernel::SrcDim()+k0) *Ntrg+j)*Kernel::TrgDim()+k1] = Mker[i*Kernel::SrcDim()+k0][j*Kernel::TrgDim()+k1] * exp_iktheta[i].imag;
  803. }
  804. }
  805. }
  806. }
  807. for (Long i = 0; i < Nnds; i++) {
  808. exp_iktheta[i] *= exp_itheta[i];
  809. }
  810. }
  811. };
  812. const ValueType dist = 4*const_pi<ValueType>()*pow<ValueType,Long>(0.5,idx); // distance of target points from the source loop (which is a unit circle)
  813. discretize_basis_functions(Mintegrands, nds, wts, dist, 35); // TODO: adaptively select Legendre order
  814. Vector<ValueType> eps_vec;
  815. for (Long k = 0; k < max_digits; k++) eps_vec.PushBack(pow<ValueType,Long>(0.1,k));
  816. std::cout<<"Level = "<<idx<<" of "<<max_adap_depth<<'\n';
  817. auto cond_num_vec = InterpQuadRule<ValueType>::Build(quad_nds, quad_wts, Mintegrands, nds, wts, true, eps_vec);
  818. }
  819. for (Integer digits = 0; digits < max_digits; digits++) {
  820. Long N = quad_nds[digits].Dim();
  821. data[idx*max_digits+digits].ReInit(3*N);
  822. for (Long i = 0; i < N; i++) {
  823. data[idx*max_digits+digits][i*3+0] = cos<ValueType>(2*const_pi<ValueType>()*quad_nds[digits][i]);
  824. data[idx*max_digits+digits][i*3+1] = sin<ValueType>(2*const_pi<ValueType>()*quad_nds[digits][i]);
  825. data[idx*max_digits+digits][i*3+2] = (2*const_pi<ValueType>()*quad_wts[digits][i]);
  826. }
  827. }
  828. }
  829. WriteFile(data, fname);
  830. }
  831. for (Integer idx = 0; idx < crossover_adap_depth; idx++) { // Use trapezoidal rule up to crossover_adap_depth
  832. for (Integer digits = 0; digits < max_digits; digits++) {
  833. Long N = std::max<Long>(digits*pow<Long,Long>(2,idx), Nmodes); // TODO: determine optimal order by testing error or adaptively
  834. data[idx*max_digits+digits].ReInit(3*N);
  835. for (Long i = 0; i < N; i++) {
  836. ValueType quad_nds = i/(ValueType)N;
  837. ValueType quad_wts = 1/(ValueType)N;
  838. data[idx*max_digits+digits][i*3+0] = cos<ValueType>(2*const_pi<ValueType>()*quad_nds);
  839. data[idx*max_digits+digits][i*3+1] = sin<ValueType>(2*const_pi<ValueType>()*quad_nds);
  840. data[idx*max_digits+digits][i*3+2] = (2*const_pi<ValueType>()*quad_wts);
  841. }
  842. }
  843. }
  844. Vector<Vector<RealType>> quad_rule_lst;
  845. quad_rule_lst.ReInit(data.Dim()*4);
  846. for (Integer i = 0; i < data.Dim(); i++) {
  847. const Long Nnds_ = data[i].Dim()/3;
  848. const Integer Nnds = ((Nnds_+VecLen-1)/VecLen)*VecLen;
  849. quad_rule_lst[i*4+0].ReInit(Nnds); quad_rule_lst[i*4+0].SetZero();
  850. quad_rule_lst[i*4+1].ReInit(Nnds); quad_rule_lst[i*4+1].SetZero();
  851. quad_rule_lst[i*4+2].ReInit(Nnds); quad_rule_lst[i*4+2].SetZero();
  852. quad_rule_lst[i*4+3].ReInit(Nmodes*2*Nnds); quad_rule_lst[i*4+3].SetZero();
  853. for (Long j = 0; j < Nnds_; j++) {
  854. Complex<ValueType> exp_itheta(data[i][j*3+0], data[i][j*3+1]);
  855. quad_rule_lst[i*4+0][j] = (RealType)(exp_itheta.real-1);
  856. quad_rule_lst[i*4+1][j] = (RealType)(exp_itheta.imag);
  857. quad_rule_lst[i*4+2][j] = (RealType)data[i][j*3+2];
  858. Complex<ValueType> exp_iktheta(1,0);
  859. for (Long k = 0; k < Nmodes; k++) {
  860. quad_rule_lst[i*4+3][(k*2+0)*Nnds+j] = (RealType)exp_iktheta.real;
  861. quad_rule_lst[i*4+3][(k*2+1)*Nnds+j] = (RealType)exp_iktheta.imag;
  862. exp_iktheta *= exp_itheta;
  863. }
  864. }
  865. }
  866. return quad_rule_lst;
  867. }
  868. template <class RealType, Integer VecLen, Integer ModalUpsample, class Kernel, Integer adap> static bool ToroidalSpecialQuadRule(Matrix<RealType>& Mfourier, Vector<RealType>& nds_cos_theta, Vector<RealType>& nds_sin_theta, Vector<RealType>& wts, const Integer Nmodes, RealType r_R0, Integer digits) {
  869. static constexpr Integer max_adap_depth = 30; // build quadrature rules for points up to 2*pi*0.5^max_adap_depth from source loop
  870. static constexpr Integer crossover_adap_depth = 2;
  871. static constexpr Integer max_digits = 20;
  872. if (digits >= max_digits) digits = max_digits-1;
  873. //SCTL_ASSERT(digits<max_digits);
  874. Long adap_depth = 0;
  875. for (RealType s = r_R0; s<2*const_pi<RealType>(); s*=2) adap_depth++;
  876. if (adap_depth >= max_adap_depth) {
  877. SCTL_WARN("Toroidal quadrature evaluation is outside of the range of precomputed quadratures; accuracy may be sverely degraded.");
  878. adap_depth = max_adap_depth-1;
  879. }
  880. SCTL_ASSERT(Nmodes < 100);
  881. static Vector<Vector<Matrix<RealType>>> all_fourier_basis(100);
  882. static Vector<Vector<Vector<RealType>>> all_quad_nds_cos_theta(100);
  883. static Vector<Vector<Vector<RealType>>> all_quad_nds_sin_theta(100);
  884. static Vector<Vector<Vector<RealType>>> all_quad_wts(100);
  885. #pragma omp critical(SCTL_ToroidalSpecialQuadRule)
  886. if (all_quad_wts[Nmodes].Dim() == 0) {
  887. auto quad_rules = BuildToroidalSpecialQuadRules<RealType,Kernel,adap>(Nmodes, VecLen);
  888. const Long Nrules = quad_rules.Dim()/4;
  889. Vector<Matrix<RealType>> fourier_basis(Nrules);
  890. Vector<Vector<RealType>> quad_nds_cos_theta(Nrules);
  891. Vector<Vector<RealType>> quad_nds_sin_theta(Nrules);
  892. Vector<Vector<RealType>> quad_wts(Nrules);
  893. for (Long i = 0; i < Nrules; i++) { // Set quad_nds_cos_theta, quad_nds_sin_theta, quad_wts, fourier_basis
  894. const Integer Nnds = quad_rules[i*4+0].Dim();
  895. SCTL_ASSERT(Nnds%VecLen == 0);
  896. quad_wts[i] = quad_rules[i*4+2];
  897. quad_nds_cos_theta[i] = quad_rules[i*4+0];
  898. quad_nds_sin_theta[i] = quad_rules[i*4+1];
  899. fourier_basis[i] = Matrix<RealType>((Nmodes-ModalUpsample)*2, Nnds, quad_rules[i*4+3].begin()).Transpose();
  900. }
  901. all_fourier_basis[Nmodes].Swap(fourier_basis);
  902. all_quad_nds_cos_theta[Nmodes].Swap(quad_nds_cos_theta);
  903. all_quad_nds_sin_theta[Nmodes].Swap(quad_nds_sin_theta);
  904. all_quad_wts[Nmodes].Swap(quad_wts);
  905. }
  906. { // Set Mfourier, nds_cos_theta, nds_sin_theta, wts
  907. const Long quad_idx = adap_depth*max_digits+digits;
  908. const auto& Mfourier0 = all_fourier_basis[Nmodes][quad_idx];
  909. const auto& nds0_cos_theta = all_quad_nds_cos_theta[Nmodes][quad_idx];
  910. const auto& nds0_sin_theta = all_quad_nds_sin_theta[Nmodes][quad_idx];
  911. const auto& wts0 = all_quad_wts[Nmodes][quad_idx];
  912. const Long N = wts0.Dim();
  913. Mfourier.ReInit(Mfourier0.Dim(0), Mfourier0.Dim(1), (Iterator<RealType>)Mfourier0.begin(), false);
  914. nds_cos_theta.ReInit(N, (Iterator<RealType>)nds0_cos_theta.begin(), false);
  915. nds_sin_theta.ReInit(N, (Iterator<RealType>)nds0_sin_theta.begin(), false);
  916. wts.ReInit(N, (Iterator<RealType>)wts0.begin(), false);
  917. }
  918. // return whether an adaptive quadrature rule has been used
  919. return (adap_depth >= crossover_adap_depth);
  920. }
  921. template <Integer digits, Integer ModalUpsample, bool trg_dot_prod, class RealType, class Kernel, Integer adap=false> static void toroidal_greens_fn_batched(Matrix<RealType>& M, const Tensor<RealType,true,3,1>& x_trg, const Tensor<RealType,true,3,1>& e_trg, const RealType r_trg, const Tensor<RealType,true,3,1>& n_trg, const Matrix<RealType>& x_src, const Matrix<RealType>& dx_src, const Matrix<RealType>& d2x_src, const Matrix<RealType>& r_src, const Matrix<RealType>& dr_src, const Matrix<RealType>& e1_src, const Kernel& ker, const Integer FourierModes) {
  922. static constexpr Integer VecLen = DefaultVecLen<RealType>();
  923. using VecType = Vec<RealType, VecLen>;
  924. constexpr Integer COORD_DIM = 3;
  925. using Vec3 = Tensor<RealType,true,COORD_DIM,1>;
  926. static constexpr Integer KDIM0 = Kernel::SrcDim();
  927. static constexpr Integer KDIM1 = Kernel::TrgDim()/(trg_dot_prod?COORD_DIM:1);
  928. static constexpr Integer Nbuff = 10000; // TODO
  929. const Long BatchSize = M.Dim(0);
  930. SCTL_ASSERT(M.Dim(1) == KDIM0*KDIM1*FourierModes*2);
  931. SCTL_ASSERT( x_src.Dim(1) == BatchSize && x_src.Dim(0) == COORD_DIM);
  932. SCTL_ASSERT( dx_src.Dim(1) == BatchSize && dx_src.Dim(0) == COORD_DIM);
  933. SCTL_ASSERT(d2x_src.Dim(1) == BatchSize && d2x_src.Dim(0) == COORD_DIM);
  934. SCTL_ASSERT( r_src.Dim(1) == BatchSize && r_src.Dim(0) == 1);
  935. SCTL_ASSERT( dr_src.Dim(1) == BatchSize && dr_src.Dim(0) == 1);
  936. SCTL_ASSERT( e1_src.Dim(1) == BatchSize && e1_src.Dim(0) == COORD_DIM);
  937. const VecType n_trg_[COORD_DIM] = {n_trg(0,0),n_trg(1,0),n_trg(2,0)};
  938. const Vec3 y_trg = x_trg + e_trg*r_trg;
  939. for (Long ii = 0; ii < BatchSize; ii++) {
  940. RealType r = r_src[0][ii], dr = dr_src[0][ii];
  941. Vec3 x, dx, d2x, e1;
  942. for (Integer k = 0; k < COORD_DIM; k++) { // Set x, dx, d2x, e1
  943. x (k,0) = x_src[k][ii];
  944. dx (k,0) = dx_src[k][ii];
  945. d2x(k,0) = d2x_src[k][ii];
  946. e1 (k,0) = e1_src[k][ii];
  947. }
  948. auto toroidal_greens_fn = [&ker,&n_trg_](Matrix<RealType>& M, const Vec3& Xt, const Vec3& x, const Vec3& dx, const Vec3& d2x, const Vec3& e1_, const RealType r, const RealType dr, const Integer FourierModes) {
  949. SCTL_ASSERT(M.Dim(0) == KDIM0*KDIM1);
  950. SCTL_ASSERT(M.Dim(1) == FourierModes*2);
  951. const auto Xt_X0 = Xt-x;
  952. RealType dist;
  953. Vec3 e1, e2, e3;
  954. { // Set dist, e1, e2, e3
  955. e3 = dx*(-1/sqrt<RealType>(dot_prod(dx,dx)));
  956. e1 = Xt_X0 - e3 * dot_prod(Xt_X0,e3);
  957. if (dot_prod(e1,e1) == 0) e1 = e1_;
  958. e1 = e1 * (1/sqrt<RealType>(dot_prod(e1,e1)));
  959. e2 = cross_prod(e3, e1);
  960. e2 = e2 * (1/sqrt<RealType>(dot_prod(e2,e2)));
  961. RealType dist0 = dot_prod(Xt_X0, e1) - r;
  962. RealType dist1 = dot_prod(Xt_X0, e3);
  963. dist = sqrt<RealType>(dist0*dist0 + dist1*dist1);
  964. }
  965. const auto exp_theta = Complex<RealType>(dot_prod(e1,e1_), -dot_prod(e2,e1_));
  966. Matrix<RealType> Mexp_iktheta;
  967. Vector<RealType> nds_cos_theta, nds_sin_theta, wts;
  968. ToroidalSpecialQuadRule<RealType,VecLen,ModalUpsample,Kernel,adap>(Mexp_iktheta, nds_cos_theta, nds_sin_theta, wts, FourierModes+ModalUpsample, dist/r, digits);
  969. const Long Nnds = wts.Dim();
  970. SCTL_ASSERT(Nnds < Nbuff);
  971. { // Set M
  972. const RealType d2x_dot_e1 = e1(0,0)*d2x(0,0) + e1(1,0)*d2x(1,0) + e1(2,0)*d2x(2,0);
  973. const RealType d2x_dot_e2 = e2(0,0)*d2x(0,0) + e2(1,0)*d2x(1,0) + e2(2,0)*d2x(2,0);
  974. const RealType norm_dx_ = sqrt<RealType>(dot_prod(dx,dx));
  975. const RealType inv_norm_dx = 1/norm_dx_;
  976. const VecType norm_dx(norm_dx_);
  977. const VecType vec_dx[3] = {dx(0,0), dx(1,0), dx(2,0)};
  978. const VecType vec_dy0[3] = {Xt(0,0)-x(0,0), Xt(1,0)-x(1,0), Xt(2,0)-x(2,0)};
  979. alignas(sizeof(VecType)) StaticArray<RealType,KDIM0*KDIM1*Nbuff> mem_buff;
  980. Matrix<RealType> Mker_da(KDIM0*KDIM1, Nnds, mem_buff, false);
  981. for (Integer j = 0; j < Nnds; j+=VecLen) { // Set Mker_da
  982. VecType dy[3], n[3], da;
  983. { // Set dy, n, da
  984. VecType cost = VecType::LoadAligned(&nds_cos_theta[j])+(RealType)1;
  985. VecType sint = VecType::LoadAligned(&nds_sin_theta[j]);
  986. dy[0] = vec_dy0[0] - cost*(r*e1(0,0)) - sint*(r*e2(0,0));
  987. dy[1] = vec_dy0[1] - cost*(r*e1(1,0)) - sint*(r*e2(1,0));
  988. dy[2] = vec_dy0[2] - cost*(r*e1(2,0)) - sint*(r*e2(2,0));
  989. VecType norm_dy = norm_dx - (cost*d2x_dot_e1 + sint*d2x_dot_e2) * (r*inv_norm_dx);
  990. n[0] = cost*e1(0,0)*norm_dy + sint*e2(0,0)*norm_dy - vec_dx[0]*(dr*inv_norm_dx);
  991. n[1] = cost*e1(1,0)*norm_dy + sint*e2(1,0)*norm_dy - vec_dx[1]*(dr*inv_norm_dx);
  992. n[2] = cost*e1(2,0)*norm_dy + sint*e2(2,0)*norm_dy - vec_dx[2]*(dr*inv_norm_dx);
  993. VecType da2 = n[0]*n[0] + n[1]*n[1] + n[2]*n[2];
  994. VecType inv_da = approx_rsqrt<digits>(da2);
  995. da = da2 * inv_da * r;
  996. n[0] = n[0] * inv_da;
  997. n[1] = n[1] * inv_da;
  998. n[2] = n[2] * inv_da;
  999. //da = norm_dx*r - (n[0]*vec_d2x[0]+n[1]*vec_d2x[1]+n[2]*vec_d2x[2])*(r*r*inv_norm_dx); // dr == 0
  1000. }
  1001. VecType Mker[KDIM0][Kernel::TrgDim()];
  1002. ker.template uKerMatrix<digits, VecType>(Mker, dy, n, ker.GetCtxPtr());
  1003. VecType da_wts = VecType::LoadAligned(&wts[j]) * da;
  1004. for (Integer k0 = 0; k0 < KDIM0; k0++) {
  1005. for (Integer k1 = 0; k1 < KDIM1; k1++) {
  1006. if (trg_dot_prod) {
  1007. VecType Mker_dot_n = FMA(Mker[k0][k1*COORD_DIM+0],n_trg_[0],
  1008. FMA(Mker[k0][k1*COORD_DIM+1],n_trg_[1],
  1009. Mker[k0][k1*COORD_DIM+2]*n_trg_[2]));
  1010. (Mker_dot_n*da_wts).StoreAligned(&Mker_da[k0*KDIM1+k1][j]);
  1011. } else {
  1012. (Mker[k0][k1]*da_wts).StoreAligned(&Mker_da[k0*KDIM1+k1][j]);
  1013. }
  1014. }
  1015. }
  1016. }
  1017. Matrix<RealType>::GEMM(M, Mker_da, Mexp_iktheta);
  1018. Complex<RealType> exp_iktheta(1,0);
  1019. for (Integer j = 0; j < FourierModes; j++) {
  1020. for (Integer k = 0; k < KDIM0*KDIM1; k++) {
  1021. Complex<RealType> Mjk(M[k][j*2+0],M[k][j*2+1]);
  1022. Mjk *= exp_iktheta;
  1023. M[k][j*2+0] = Mjk.real;
  1024. M[k][j*2+1] = Mjk.imag;
  1025. }
  1026. exp_iktheta *= exp_theta;
  1027. }
  1028. }
  1029. };
  1030. Matrix<RealType> M_toroidal_greens_fn(KDIM0*KDIM1, FourierModes*2, M[ii], false);
  1031. toroidal_greens_fn(M_toroidal_greens_fn, y_trg, x, dx, d2x, e1, r, dr, FourierModes);
  1032. }
  1033. return;
  1034. if (adap==0) { // Print toroidal quadrature error
  1035. using ValueType = RealType;
  1036. auto copy_matrix = [](Matrix<ValueType>& M_, const Matrix<RealType>& M) {
  1037. M_.ReInit(M.Dim(0), M.Dim(1));
  1038. for (Long i = 0; i < M.Dim(0)*M.Dim(1); i++) {
  1039. M_[0][i] = (ValueType)M[0][i];
  1040. }
  1041. };
  1042. Matrix<ValueType> M_;
  1043. Tensor<ValueType,true,3,1> x_trg_;
  1044. Tensor<ValueType,true,3,1> e_trg_;
  1045. Tensor<ValueType,true,3,1> n_trg_;
  1046. for (Long i = 0; i < 3; i++) {
  1047. x_trg_(i,0) = (ValueType)x_trg(i,0);
  1048. e_trg_(i,0) = (ValueType)e_trg(i,0);
  1049. n_trg_(i,0) = (ValueType)n_trg(i,0);
  1050. }
  1051. Matrix<ValueType> x_src_ ;
  1052. Matrix<ValueType> dx_src_ ;
  1053. Matrix<ValueType> d2x_src_;
  1054. Matrix<ValueType> r_src_ ;
  1055. Matrix<ValueType> dr_src_ ;
  1056. Matrix<ValueType> e1_src_ ;
  1057. copy_matrix(M_ , M);
  1058. copy_matrix(x_src_ , x_src);
  1059. copy_matrix(dx_src_ , dx_src);
  1060. copy_matrix(d2x_src_, d2x_src);
  1061. copy_matrix(r_src_ , r_src);
  1062. copy_matrix(dr_src_ , dr_src);
  1063. copy_matrix(e1_src_ , e1_src);
  1064. toroidal_greens_fn_batched<(digits<32?digits+1:digits), ModalUpsample, trg_dot_prod, ValueType, Kernel, 2>(M_, x_trg_, e_trg_/sqrt<ValueType>(dot_prod(e_trg_,e_trg_)), (ValueType)r_trg, n_trg_, x_src_, dx_src_, d2x_src_, r_src_, dr_src_, e1_src_, ker, FourierModes);
  1065. static RealType max_rel_err = 0;
  1066. RealType max_err = 0, max_val = 0;
  1067. for (Long i = 0; i < BatchSize*KDIM0*KDIM1; i++) {
  1068. RealType err = fabs(M[0][i*FourierModes*2] - (RealType)M_[0][i*FourierModes*2]);
  1069. RealType val = fabs(M[0][i*FourierModes*2]);
  1070. if (err > max_err) max_err = err;
  1071. if (val > max_val) max_val = val;
  1072. }
  1073. if (max_val>0 && max_err/max_val > max_rel_err) {
  1074. max_rel_err = max_err/max_val;
  1075. std::cout<<max_rel_err<<' '<<max_err<<'\n';
  1076. }
  1077. for (Long i = 0; i < M.Dim(0)*M.Dim(1); i++) {
  1078. M[0][i] = (RealType)M_[0][i];
  1079. }
  1080. }
  1081. }
  1082. template <class ValueType> static void DyadicQuad_s(Vector<ValueType>& nds, Vector<ValueType>& wts, const Integer LegQuadOrder, const Integer LogQuadOrder, const ValueType s, const Integer levels, bool sort) {
  1083. const auto& log_quad_nds = LogSingularityQuadRule<ValueType>(LogQuadOrder).first;
  1084. const auto& log_quad_wts = LogSingularityQuadRule<ValueType>(LogQuadOrder).second;
  1085. const auto& leg_nds = LegendreQuadRule<ValueType>(LegQuadOrder).first;
  1086. const auto& leg_wts = LegendreQuadRule<ValueType>(LegQuadOrder).second;
  1087. ValueType len0 = std::min(pow<ValueType>(0.5,levels), std::min(s, (1-s)));
  1088. ValueType len1 = std::min<ValueType>(s, 1-s);
  1089. ValueType len2 = std::max<ValueType>(s, 1-s);
  1090. for (Long i = 0; i < log_quad_nds.Dim(); i++) {
  1091. nds.PushBack( len0*log_quad_nds[i]);
  1092. nds.PushBack(-len0*log_quad_nds[i]);
  1093. wts.PushBack(len0*log_quad_wts[i]);
  1094. wts.PushBack(len0*log_quad_wts[i]);
  1095. }
  1096. for (ValueType start = len0; start < len1; start*=2) {
  1097. ValueType step_ = std::min(start, len1-start);
  1098. for (Long i = 0; i < leg_nds.Dim(); i++) {
  1099. nds.PushBack( start + step_*leg_nds[i]);
  1100. nds.PushBack(-start - step_*leg_nds[i]);
  1101. wts.PushBack(step_*leg_wts[i]);
  1102. wts.PushBack(step_*leg_wts[i]);
  1103. }
  1104. }
  1105. for (ValueType start = len1; start < len2; start*=2) {
  1106. ValueType step_ = std::min(start, len2-start);
  1107. for (Long i = 0; i < leg_nds.Dim(); i++) {
  1108. if (s + start + step_*leg_nds[i] <= 1.0) {
  1109. nds.PushBack( start + step_*leg_nds[i]);
  1110. wts.PushBack(step_*leg_wts[i]);
  1111. }
  1112. if (s - start - step_*leg_nds[i] >= 0.0) {
  1113. nds.PushBack(-start - step_*leg_nds[i]);
  1114. wts.PushBack(step_*leg_wts[i]);
  1115. }
  1116. }
  1117. }
  1118. if (!sort) return;
  1119. Vector<ValueType> nds_(nds.Dim());
  1120. Vector<ValueType> wts_(wts.Dim());
  1121. Vector<std::pair<ValueType,Long>> sort_pair;
  1122. for (Long i = 0; i < nds.Dim(); i++) {
  1123. sort_pair.PushBack(std::pair<ValueType,Long>{nds[i], i});
  1124. }
  1125. std::sort(sort_pair.begin(), sort_pair.end());
  1126. for (Long i = 0; i < nds.Dim(); i++) {
  1127. const Long idx = sort_pair[i].second;
  1128. nds_[i] = nds[idx];
  1129. wts_[i] = wts[idx];
  1130. }
  1131. nds = nds_;
  1132. wts = wts_;
  1133. };
  1134. template <Integer ModalUpsample, class ValueType, class Kernel, bool trg_dot_prod> static void SpecialQuadBuildBasisMatrix(Matrix<ValueType>& M, Vector<ValueType>& quad_nds, Vector<ValueType>& quad_wts, const Integer Ncheb, const Integer FourierModes, const ValueType s_trg, const Integer max_digits, const ValueType elem_length, const Integer RefLevels, const Kernel& ker) {
  1135. // TODO: cleanup
  1136. constexpr Integer COORD_DIM = 3;
  1137. using Vec3 = Tensor<ValueType,true,COORD_DIM,1>;
  1138. const Long LegQuadOrder = 2*max_digits;
  1139. constexpr Long LogQuadOrder = 16; // this has non-negative weights
  1140. constexpr Integer KDIM0 = Kernel::SrcDim();
  1141. constexpr Integer KDIM1 = Kernel::TrgDim() / (trg_dot_prod ? COORD_DIM : 1);
  1142. // Adaptive quadrature rule
  1143. DyadicQuad_s(quad_nds, quad_wts, LegQuadOrder, LogQuadOrder, s_trg, RefLevels, true);
  1144. quad_nds += s_trg; // TODO: remove this
  1145. Matrix<ValueType> Minterp_quad_nds;
  1146. { // Set Minterp_quad_nds
  1147. Minterp_quad_nds.ReInit(Ncheb, quad_nds.Dim());
  1148. Vector<ValueType> Vinterp_quad_nds(Ncheb*quad_nds.Dim(), Minterp_quad_nds.begin(), false);
  1149. LagrangeInterp<ValueType>::Interpolate(Vinterp_quad_nds, SlenderElemList<ValueType>::CenterlineNodes(Ncheb), quad_nds);
  1150. }
  1151. Vec3 x_trg, e_trg, n_trg;
  1152. x_trg(0,0) = 0;
  1153. x_trg(1,0) = 0;
  1154. x_trg(2,0) = 0;
  1155. e_trg(0,0) = 1;
  1156. e_trg(1,0) = 0;
  1157. e_trg(2,0) = 0;
  1158. n_trg(0,0) = 1;
  1159. n_trg(1,0) = 0;
  1160. n_trg(2,0) = 0;
  1161. Vector<ValueType> radius( Ncheb);
  1162. Vector<ValueType> coord (COORD_DIM*Ncheb);
  1163. Vector<ValueType> dr ( Ncheb);
  1164. Vector<ValueType> dx (COORD_DIM*Ncheb);
  1165. Vector<ValueType> d2x (COORD_DIM*Ncheb);
  1166. Vector<ValueType> e1 (COORD_DIM*Ncheb);
  1167. for (Long i = 0; i < Ncheb; i++) {
  1168. radius[i] = 1;
  1169. dr[i] = 0;
  1170. coord[0*Ncheb+i] = 0;
  1171. coord[1*Ncheb+i] = 0;
  1172. coord[2*Ncheb+i] = SlenderElemList<ValueType>::CenterlineNodes(Ncheb)[i] * elem_length - s_trg * elem_length;
  1173. dx[0*Ncheb+i] = 0;
  1174. dx[1*Ncheb+i] = 0;
  1175. dx[2*Ncheb+i] = elem_length;
  1176. d2x[0*Ncheb+i] = 0;
  1177. d2x[1*Ncheb+i] = 0;
  1178. d2x[2*Ncheb+i] = 0;
  1179. e1[0*Ncheb+i] = 1;
  1180. e1[1*Ncheb+i] = 0;
  1181. e1[2*Ncheb+i] = 0;
  1182. }
  1183. Matrix<ValueType> r_src, dr_src, x_src, dx_src, d2x_src, e1_src;
  1184. r_src .ReInit( 1,quad_nds.Dim());
  1185. dr_src .ReInit( 1,quad_nds.Dim());
  1186. x_src .ReInit(COORD_DIM,quad_nds.Dim());
  1187. dx_src .ReInit(COORD_DIM,quad_nds.Dim());
  1188. d2x_src.ReInit(COORD_DIM,quad_nds.Dim());
  1189. e1_src .ReInit(COORD_DIM,quad_nds.Dim());
  1190. Matrix<ValueType>::GEMM( x_src, Matrix<ValueType>(COORD_DIM,Ncheb, coord.begin(),false), Minterp_quad_nds);
  1191. Matrix<ValueType>::GEMM( dx_src, Matrix<ValueType>(COORD_DIM,Ncheb, dx.begin(),false), Minterp_quad_nds);
  1192. Matrix<ValueType>::GEMM(d2x_src, Matrix<ValueType>(COORD_DIM,Ncheb, d2x.begin(),false), Minterp_quad_nds);
  1193. Matrix<ValueType>::GEMM( r_src, Matrix<ValueType>( 1,Ncheb,radius.begin(),false), Minterp_quad_nds);
  1194. Matrix<ValueType>::GEMM( dr_src, Matrix<ValueType>( 1,Ncheb, dr.begin(),false), Minterp_quad_nds);
  1195. Matrix<ValueType>::GEMM( e1_src, Matrix<ValueType>(COORD_DIM,Ncheb, e1.begin(),false), Minterp_quad_nds);
  1196. for (Long j = 0; j < quad_nds.Dim(); j++) { // Set e2_src
  1197. Vec3 e1, dx;
  1198. for (Integer k = 0; k < COORD_DIM; k++) {
  1199. e1(k,0) = e1_src[k][j];
  1200. dx(k,0) = dx_src[k][j];
  1201. }
  1202. e1 = e1 - dx * dot_prod(e1, dx) * (1/dot_prod(dx,dx));
  1203. e1 = e1 * (1/sqrt<ValueType>(dot_prod(e1,e1)));
  1204. for (Integer k = 0; k < COORD_DIM; k++) {
  1205. e1_src[k][j] = e1(k,0);
  1206. }
  1207. }
  1208. Matrix<ValueType> M_tor(quad_nds.Dim(), KDIM0*KDIM1*FourierModes*2);
  1209. constexpr Integer TorGreensFnDigits = (Integer)(TypeTraits<ValueType>::SigBits*0.3010299957);
  1210. toroidal_greens_fn_batched<TorGreensFnDigits,ModalUpsample,trg_dot_prod,ValueType,Kernel,1>(M_tor, x_trg, e_trg, (ValueType)1, n_trg, x_src, dx_src, d2x_src, r_src, dr_src, e1_src, ker, FourierModes);
  1211. M.ReInit(quad_nds.Dim(), Ncheb*FourierModes*2*KDIM0*KDIM1);
  1212. for (Long i = 0; i < quad_nds.Dim(); i++) {
  1213. for (Long j = 0; j < Ncheb; j++) {
  1214. for (Long k = 0; k < KDIM0*KDIM1*FourierModes*2; k++) {
  1215. M[i][j*KDIM0*KDIM1*FourierModes*2+k] = Minterp_quad_nds[j][i] * M_tor[i][k];
  1216. }
  1217. }
  1218. }
  1219. }
  1220. template <Integer ModalUpsample, class ValueType, class Kernel, bool trg_dot_prod, bool symmetric=true/*must be set true for hypersingular kernels*/> static Vector<Vector<ValueType>> BuildSpecialQuadRules(const Integer Ncheb, const Integer FourierModes, const Integer trg_node_idx, const ValueType elem_length) {
  1221. constexpr Integer Nlen = 20; // number of length samples in [elem_length/sqrt(2), elem_length*sqrt(2)]
  1222. constexpr Integer max_digits = 19;
  1223. const ValueType s_trg = SlenderElemList<ValueType>::CenterlineNodes(Ncheb)[trg_node_idx];
  1224. const Integer adap_depth = (Integer)(log<ValueType>(elem_length)/log<ValueType>(2)+4);
  1225. const ValueType eps_buffer = std::min<ValueType>(3e-2/elem_length, 3e-4); // distance of closest node points to s_trg
  1226. const ValueType eps = 8*machine_eps<ValueType>();
  1227. Kernel ker;
  1228. Vector<ValueType> nds, wts;
  1229. Matrix<ValueType> Mintegrands;
  1230. { // Set nds, wts, Mintegrands
  1231. Vector<Matrix<ValueType>> Mker(Nlen);
  1232. Vector<Vector<ValueType>> nds_(Nlen), wts_(Nlen);
  1233. //#pragma omp parallel for schedule(static) // TODO: prevents parallelization of precomputation of toroidal quadrature rule
  1234. for (Long k = 0; k < Nlen; k++) {
  1235. ValueType length = elem_length/sqrt<ValueType>(2.0)*k/(Nlen-1) + elem_length*sqrt<ValueType>(2.0)*(Nlen-k-1)/(Nlen-1);
  1236. SpecialQuadBuildBasisMatrix<ModalUpsample,ValueType,Kernel,trg_dot_prod>(Mker[k], nds_[k], wts_[k], Ncheb, FourierModes, s_trg, max_digits, length, adap_depth, ker);
  1237. }
  1238. const Long N0 = nds_[0].Dim();
  1239. Vector<Long> cnt(Nlen), dsp(Nlen); dsp[0] = 0;
  1240. for (Long k = 0; k < Nlen; k++) {
  1241. cnt[k] = Mker[k].Dim(1);
  1242. }
  1243. omp_par::scan(cnt.begin(), dsp.begin(), cnt.Dim());
  1244. const Long Nsplit = (symmetric ? std::lower_bound(nds_[0].begin(), nds_[0].end(), s_trg) - nds_[0].begin() : N0);
  1245. const Long N = std::max<Long>(N0 - Nsplit, Nsplit);
  1246. nds.ReInit(N);
  1247. wts.ReInit(N);
  1248. Mintegrands.ReInit(N, dsp[Nlen-1] + cnt[Nlen-1]);
  1249. if (N == Nsplit) {
  1250. #pragma omp parallel for schedule(static)
  1251. for (Long k = 0; k < Nlen; k++) {
  1252. for (Long i = 0; i < Nsplit; i++) {
  1253. for (Long j = 0; j < cnt[k]; j++) {
  1254. Mintegrands[i][dsp[k]+j] = Mker[k][i][j];
  1255. }
  1256. }
  1257. for (Long i = Nsplit; i < N0; i++) {
  1258. for (Long j = 0; j < cnt[k]; j++) {
  1259. Mintegrands[2*Nsplit-i-1][dsp[k]+j] += Mker[k][i][j];
  1260. }
  1261. }
  1262. }
  1263. for (Long i = 0; i < Nsplit; i++) {
  1264. nds[i] = nds_[0][i];
  1265. wts[i] = wts_[0][i];
  1266. }
  1267. for (Long i = Nsplit; i < N0; i++) {
  1268. SCTL_ASSERT(fabs(nds[2*Nsplit-i-1] + nds_[0][i] - 2*s_trg) < eps);
  1269. SCTL_ASSERT(fabs(wts[2*Nsplit-i-1] - wts_[0][i]) < eps);
  1270. }
  1271. } else {
  1272. #pragma omp parallel for schedule(static)
  1273. for (Long k = 0; k < Nlen; k++) {
  1274. for (Long i = Nsplit; i < N0; i++) {
  1275. for (Long j = 0; j < cnt[k]; j++) {
  1276. Mintegrands[i-Nsplit][dsp[k]+j] = Mker[k][i][j];
  1277. }
  1278. }
  1279. for (Long i = 0; i < Nsplit; i++) {
  1280. for (Long j = 0; j < cnt[k]; j++) {
  1281. Mintegrands[Nsplit-i-1][dsp[k]+j] += Mker[k][i][j];
  1282. }
  1283. }
  1284. }
  1285. for (Long i = Nsplit; i < N0; i++) {
  1286. nds[i-Nsplit] = nds_[0][i];
  1287. wts[i-Nsplit] = wts_[0][i];
  1288. }
  1289. for (Long i = 0; i < Nsplit; i++) {
  1290. SCTL_ASSERT(fabs(nds[Nsplit-i-1] + nds_[0][i] - 2*s_trg) < eps);
  1291. SCTL_ASSERT(fabs(wts[Nsplit-i-1] - wts_[0][i]) < eps);
  1292. }
  1293. }
  1294. }
  1295. Vector<Vector<ValueType>> nds_wts(max_digits*2);
  1296. { // Set nds_wts
  1297. Vector<ValueType> eps_vec;
  1298. Vector<Vector<ValueType>> quad_nds, quad_wts;
  1299. for (Long k = 0; k < max_digits; k++) eps_vec.PushBack(pow<ValueType,Long>(0.1,k));
  1300. ValueType range0 = s_trg>=0.5 ? 0 : s_trg+eps_buffer;
  1301. ValueType range1 = s_trg>=0.5 ? s_trg-eps_buffer : 1;
  1302. InterpQuadRule<ValueType>::Build(quad_nds, quad_wts, Mintegrands, nds, wts, false, eps_vec, Vector<Long>(), range0, range1);
  1303. SCTL_ASSERT(quad_nds.Dim() == max_digits);
  1304. SCTL_ASSERT(quad_wts.Dim() == max_digits);
  1305. for (Long k = 0; k < max_digits; k++) {
  1306. for (Long i = 0; i < quad_nds[k].Dim(); i++) {
  1307. const ValueType qx0 = quad_nds[k][i];
  1308. const ValueType qx1 = 2*s_trg - qx0;
  1309. const ValueType qw = quad_wts[k][i];
  1310. nds_wts[k*2+0].PushBack(qx0);
  1311. nds_wts[k*2+1].PushBack(qw);
  1312. if (symmetric && 0 <= qx1 && qx1 <= (ValueType)1) {
  1313. nds_wts[k*2+0].PushBack(qx1);
  1314. nds_wts[k*2+1].PushBack(qw);
  1315. }
  1316. }
  1317. }
  1318. }
  1319. return nds_wts;
  1320. }
  1321. template <Integer ModalUpsample, class Real, class Kernel, bool trg_dot_prod, bool adap_quad=false> static void SpecialQuadRule(Vector<Real>& nds, Vector<Real>& wts, const Integer ChebOrder, const Integer trg_node_idx, const Real elem_radius, const Real elem_length, const Integer digits) {
  1322. static constexpr Integer max_adap_depth = 30+7; // TODO
  1323. constexpr Integer MaxFourierModes = 8; // TODO
  1324. constexpr Integer MaxChebOrder = 100;
  1325. constexpr Integer max_digits = 19;
  1326. auto LogSingularQuadOrder = [](Integer digits) { return 2*digits; }; // TODO: determine optimal order
  1327. auto LegQuadOrder = [](Integer digits) { return digits; }; // TODO: determine optimal order
  1328. #ifdef SCTL_QUAD_T
  1329. using ValueType = QuadReal;
  1330. #else
  1331. using ValueType = long double;
  1332. #endif
  1333. if (0) { // Compute quadratures on-the-fly
  1334. static ValueType aspect_ratio = 0;
  1335. static Vector<Vector<Vector<ValueType>>> nds_wts;
  1336. #pragma omp critical(mytest)
  1337. if (elem_length/elem_radius < aspect_ratio/1.42 || aspect_ratio*1.42 < elem_length/elem_radius) {
  1338. nds_wts.ReInit(ChebOrder);
  1339. for (Long i = 0; i < ChebOrder; i++) {
  1340. nds_wts[i] = BuildSpecialQuadRules<ModalUpsample,ValueType,Kernel,trg_dot_prod>(ChebOrder, MaxFourierModes, i, elem_length/elem_radius);
  1341. }
  1342. aspect_ratio = elem_length/elem_radius;
  1343. }
  1344. const Long Nnds = nds_wts[trg_node_idx][digits*2+0].Dim();
  1345. nds.ReInit(Nnds);
  1346. wts.ReInit(Nnds);
  1347. for (Long i = 0; i < Nnds; i++) {
  1348. static const auto cheb_nds_ = SlenderElemList<ValueType>::CenterlineNodes(ChebOrder);
  1349. nds[i] = (Real)(nds_wts[trg_node_idx][digits*2+0][i] - cheb_nds_[trg_node_idx]);
  1350. wts[i] = (Real)nds_wts[trg_node_idx][digits*2+1][i];
  1351. }
  1352. return;
  1353. }
  1354. if (!adap_quad) {
  1355. auto load_special_quad_rule = [](Vector<Vector<Real>>& nds_lst, Vector<Vector<Real>>& wts_lst, const Integer ChebOrder){
  1356. const std::string fname = std::string("data/special_quad_q") + std::to_string(ChebOrder) + "_" + Kernel::Name() + (trg_dot_prod ? "_dotXn" : "");
  1357. const auto cheb_nds_ = SlenderElemList<ValueType>::CenterlineNodes(ChebOrder);
  1358. Vector<Vector<ValueType>> data;
  1359. ReadFile(data, fname);
  1360. if (data.Dim() != max_adap_depth*ChebOrder*max_digits*2) { // build quadrature rules
  1361. data.ReInit(max_adap_depth*ChebOrder*max_digits*2);
  1362. ValueType length = pow<max_adap_depth-7,ValueType>((ValueType)2);
  1363. for (Integer i = 0; i < max_adap_depth; i++) {
  1364. std::cout<<"length = "<<length<<'\n';
  1365. for (Integer trg_node_idx = 0; trg_node_idx < ChebOrder; trg_node_idx++) {
  1366. auto nds_wts = BuildSpecialQuadRules<ModalUpsample,ValueType,Kernel,trg_dot_prod>(ChebOrder, MaxFourierModes, trg_node_idx, length);
  1367. for (Long j = 0; j < max_digits; j++) {
  1368. data[((i*ChebOrder+trg_node_idx) * max_digits+j)*2+0] = nds_wts[j*2+0];
  1369. data[((i*ChebOrder+trg_node_idx) * max_digits+j)*2+1] = nds_wts[j*2+1];
  1370. }
  1371. }
  1372. length *= (ValueType)0.5;
  1373. }
  1374. WriteFile(data, fname);
  1375. }
  1376. nds_lst.ReInit(max_adap_depth*ChebOrder*max_digits);
  1377. wts_lst.ReInit(max_adap_depth*ChebOrder*max_digits);
  1378. for (Long i = 0; i < max_adap_depth*ChebOrder*max_digits; i++) { // Set nds_wts_lst
  1379. const Long trg_node_idx = (i/max_digits)%ChebOrder;
  1380. const auto& nds_ = data[i*2+0];
  1381. const auto& wts_ = data[i*2+1];
  1382. const Long Nnds = wts_.Dim();
  1383. nds_lst[i].ReInit(Nnds);
  1384. wts_lst[i].ReInit(Nnds);
  1385. for (Long j = 0; j < Nnds; j++) {
  1386. nds_lst[i][j] = (Real)(nds_[j] - cheb_nds_[trg_node_idx]);
  1387. wts_lst[i][j] = (Real)wts_[j];
  1388. }
  1389. }
  1390. };
  1391. static Vector<Vector<Vector<Real>>> nds_lst(MaxChebOrder);
  1392. static Vector<Vector<Vector<Real>>> wts_lst(MaxChebOrder);
  1393. SCTL_ASSERT(ChebOrder < MaxChebOrder);
  1394. #pragma omp critical(SCTL_SpecialQuadRule)
  1395. if (!wts_lst[ChebOrder].Dim()) {
  1396. load_special_quad_rule(nds_lst[ChebOrder], wts_lst[ChebOrder], ChebOrder);
  1397. }
  1398. Long quad_idx = (Long)((max_adap_depth-7) - log2((double)(elem_length/elem_radius*sqrt<Real>(0.5))));
  1399. if (quad_idx < 0 || quad_idx > max_adap_depth-1) {
  1400. SCTL_WARN("Slender element aspect-ratio is outside of the range of precomputed quadratures; accuracy may be sverely degraded.");
  1401. }
  1402. quad_idx = std::max<Integer>(0, std::min<Integer>(max_adap_depth-1, quad_idx));
  1403. wts = wts_lst[ChebOrder][(quad_idx*ChebOrder+trg_node_idx) * max_digits+digits];
  1404. nds = nds_lst[ChebOrder][(quad_idx*ChebOrder+trg_node_idx) * max_digits+digits];
  1405. } else {
  1406. const Integer RefLevels = (Integer)(log<Real>(elem_length/elem_radius)/log<Real>(2)-1);
  1407. const auto& cheb_nds = SlenderElemList<Real>::CenterlineNodes(ChebOrder);
  1408. const Real s_trg = cheb_nds[trg_node_idx];
  1409. DyadicQuad_s(nds, wts, LegQuadOrder(digits), LogSingularQuadOrder(digits), s_trg, RefLevels, false);
  1410. }
  1411. }
  1412. template <class Real> SlenderElemList<Real>::SlenderElemList(const Vector<Long>& cheb_order0, const Vector<Long>& fourier_order0, const Vector<Real>& coord0, const Vector<Real>& radius0, const Vector<Real>& orientation0) {
  1413. Init(cheb_order0, fourier_order0, coord0, radius0, orientation0);
  1414. }
  1415. template <class Real> void SlenderElemList<Real>::Init(const Vector<Long>& cheb_order0, const Vector<Long>& fourier_order0, const Vector<Real>& coord0, const Vector<Real>& radius0, const Vector<Real>& orientation0) {
  1416. using Vec3 = Tensor<Real,true,COORD_DIM,1>;
  1417. const Long Nelem = cheb_order0.Dim();
  1418. SCTL_ASSERT(fourier_order0.Dim() == Nelem);
  1419. cheb_order = cheb_order0;
  1420. fourier_order = fourier_order0;
  1421. elem_dsp.ReInit(Nelem);
  1422. if (Nelem) elem_dsp[0] = 0;
  1423. omp_par::scan(cheb_order.begin(), elem_dsp.begin(), Nelem);
  1424. const Long Nnodes = (Nelem ? cheb_order[Nelem-1]+elem_dsp[Nelem-1] : 0);
  1425. SCTL_ASSERT_MSG(coord0.Dim() == Nnodes * COORD_DIM, "Length of the coordinate vector does not match the number of nodes.");
  1426. SCTL_ASSERT_MSG(radius0.Dim() == Nnodes, "Length of the radius vector does not match the number of nodes.");
  1427. radius = radius0;
  1428. coord.ReInit(COORD_DIM*Nnodes);
  1429. e1 .ReInit(COORD_DIM*Nnodes);
  1430. dr .ReInit( Nnodes);
  1431. dx .ReInit(COORD_DIM*Nnodes);
  1432. d2x .ReInit(COORD_DIM*Nnodes);
  1433. for (Long i = 0; i < Nelem; i++) { // Set coord, radius, dr, ds, d2s
  1434. const Long Ncheb = cheb_order[i];
  1435. Vector<Real> radius_( Ncheb, radius.begin()+ elem_dsp[i], false);
  1436. Vector<Real> coord_(COORD_DIM*Ncheb, coord.begin()+COORD_DIM*elem_dsp[i], false);
  1437. Vector<Real> e1_(COORD_DIM*Ncheb, e1.begin()+COORD_DIM*elem_dsp[i], false);
  1438. Vector<Real> dr_( Ncheb, dr.begin()+ elem_dsp[i], false);
  1439. Vector<Real> dx_(COORD_DIM*Ncheb, dx.begin()+COORD_DIM*elem_dsp[i], false);
  1440. Vector<Real> d2x_(COORD_DIM*Ncheb, d2x.begin()+COORD_DIM*elem_dsp[i], false);
  1441. const Vector<Real> coord__(COORD_DIM*Ncheb, (Iterator<Real>)coord0.begin()+elem_dsp[i]*COORD_DIM, false);
  1442. for (Long j = 0; j < Ncheb; j++) { // Set coord_
  1443. for (Long k = 0; k < COORD_DIM; k++) {
  1444. coord_[k*Ncheb+j] = coord__[j*COORD_DIM+k];
  1445. }
  1446. }
  1447. LagrangeInterp<Real>::Derivative( dr_, radius_, CenterlineNodes(Ncheb));
  1448. LagrangeInterp<Real>::Derivative( dx_, coord_, CenterlineNodes(Ncheb));
  1449. LagrangeInterp<Real>::Derivative(d2x_, dx_, CenterlineNodes(Ncheb));
  1450. if (orientation0.Dim()) { // Set e1_
  1451. SCTL_ASSERT(orientation0.Dim() == Nnodes*COORD_DIM);
  1452. const Vector<Real> orientation__(COORD_DIM*Ncheb, (Iterator<Real>)orientation0.begin()+elem_dsp[i]*COORD_DIM, false);
  1453. for (Long j = 0; j < Ncheb; j++) {
  1454. for (Integer k = 0; k < COORD_DIM; k++) {
  1455. e1_[k*Ncheb+j] = orientation__[j*COORD_DIM+k];
  1456. }
  1457. }
  1458. } else {
  1459. Integer orient_dir = 0;
  1460. for (Integer k = 0; k < COORD_DIM; k++) {
  1461. e1_[k*Ncheb+0] = 0;
  1462. if (fabs(dx_[k*Ncheb+0]) < fabs(dx_[orient_dir*Ncheb+0])) orient_dir = k;
  1463. }
  1464. e1_[orient_dir*Ncheb+0] = 1;
  1465. for (Long j = 0; j < Ncheb; j++) {
  1466. Vec3 e1_vec, dx_vec;
  1467. for (Integer k = 0; k < COORD_DIM; k++) {
  1468. e1_vec(k,0) = (j==0 ? e1_[k*Ncheb] : e1_[k*Ncheb+j-1]);
  1469. dx_vec(k,0) = dx_[k*Ncheb+j];
  1470. }
  1471. e1_vec = e1_vec - dx_vec*(dot_prod(dx_vec,e1_vec)/dot_prod(dx_vec,dx_vec));
  1472. Real scal = (1.0/sqrt<Real>(dot_prod(e1_vec,e1_vec)));
  1473. for (Integer k = 0; k < COORD_DIM; k++) {
  1474. e1_[k*Ncheb+j] = e1_vec(k,0) * scal;
  1475. }
  1476. }
  1477. }
  1478. }
  1479. }
  1480. template <class Real> Long SlenderElemList<Real>::Size() const {
  1481. return cheb_order.Dim();
  1482. }
  1483. template <class Real> void SlenderElemList<Real>::GetNodeCoord(Vector<Real>* X, Vector<Real>* Xn, Vector<Long>* element_wise_node_cnt) const {
  1484. const Long Nelem = cheb_order.Dim();
  1485. Vector<Long> node_cnt(Nelem), node_dsp(Nelem);
  1486. { // Set node_cnt, node_dsp
  1487. for (Long i = 0; i < Nelem; i++) {
  1488. node_cnt[i] = cheb_order[i] * fourier_order[i];
  1489. }
  1490. if (Nelem) node_dsp[0] = 0;
  1491. omp_par::scan(node_cnt.begin(), node_dsp.begin(), Nelem);
  1492. }
  1493. const Long Nnodes = (Nelem ? node_dsp[Nelem-1]+node_cnt[Nelem-1] : 0);
  1494. if (element_wise_node_cnt) (*element_wise_node_cnt) = node_cnt;
  1495. if (X != nullptr && X ->Dim() != Nnodes*COORD_DIM) X ->ReInit(Nnodes*COORD_DIM);
  1496. if (Xn != nullptr && Xn->Dim() != Nnodes*COORD_DIM) Xn->ReInit(Nnodes*COORD_DIM);
  1497. for (Long i = 0; i < Nelem; i++) {
  1498. Vector<Real> X_, Xn_;
  1499. if (X != nullptr) X_ .ReInit(node_cnt[i]*COORD_DIM, X ->begin()+node_dsp[i]*COORD_DIM, false);
  1500. if (Xn != nullptr) Xn_.ReInit(node_cnt[i]*COORD_DIM, Xn->begin()+node_dsp[i]*COORD_DIM, false);
  1501. GetGeom((X==nullptr?nullptr:&X_), (Xn==nullptr?nullptr:&Xn_), nullptr,nullptr,nullptr, CenterlineNodes(cheb_order[i]), sin_theta<Real>(fourier_order[i]), cos_theta<Real>(fourier_order[i]), i);
  1502. }
  1503. }
  1504. template <class Real> void SlenderElemList<Real>::GetFarFieldNodes(Vector<Real>& X, Vector<Real>& Xn, Vector<Real>& wts, Vector<Real>& dist_far, Vector<Long>& element_wise_node_cnt, const Real tol) const {
  1505. const Long Nelem = cheb_order.Dim();
  1506. Vector<Long> node_cnt(Nelem), node_dsp(Nelem);
  1507. { // Set node_cnt, node_dsp
  1508. for (Long i = 0; i < Nelem; i++) {
  1509. node_cnt[i] = cheb_order[i]*FARFIELD_UPSAMPLE * fourier_order[i]*FARFIELD_UPSAMPLE;
  1510. }
  1511. if (Nelem) node_dsp[0] = 0;
  1512. omp_par::scan(node_cnt.begin(), node_dsp.begin(), Nelem);
  1513. }
  1514. element_wise_node_cnt = node_cnt;
  1515. const Long Nnodes = (Nelem ? node_dsp[Nelem-1]+node_cnt[Nelem-1] : 0);
  1516. if (X .Dim() != Nnodes*COORD_DIM) X .ReInit(Nnodes*COORD_DIM);
  1517. if (Xn .Dim() != Nnodes*COORD_DIM) Xn .ReInit(Nnodes*COORD_DIM);
  1518. if (wts .Dim() != Nnodes ) wts .ReInit(Nnodes );
  1519. if (dist_far.Dim() != Nnodes ) dist_far.ReInit(Nnodes );
  1520. for (Long elem_idx = 0; elem_idx < Nelem; elem_idx++) {
  1521. Vector<Real> X_(node_cnt[elem_idx]*COORD_DIM, X.begin()+node_dsp[elem_idx]*COORD_DIM, false);
  1522. Vector<Real> Xn_(node_cnt[elem_idx]*COORD_DIM, Xn.begin()+node_dsp[elem_idx]*COORD_DIM, false);
  1523. Vector<Real> wts_(node_cnt[elem_idx] , wts.begin()+node_dsp[elem_idx] , false);
  1524. Vector<Real> dist_far_(node_cnt[elem_idx] , dist_far.begin()+node_dsp[elem_idx] , false);
  1525. Vector<Real> dX_ds, dX_dt; // TODO: pre-allocate
  1526. const Long ChebOrder = cheb_order[elem_idx];
  1527. const Long FourierOrder = fourier_order[elem_idx];
  1528. const auto& leg_nds = LegendreQuadRule<Real>(ChebOrder*FARFIELD_UPSAMPLE).first;
  1529. const auto& leg_wts = LegendreQuadRule<Real>(ChebOrder*FARFIELD_UPSAMPLE).second;
  1530. GetGeom(&X_, &Xn_, &wts_, &dX_ds, &dX_dt, leg_nds, sin_theta<Real>(FourierOrder*FARFIELD_UPSAMPLE), cos_theta<Real>(FourierOrder*FARFIELD_UPSAMPLE), elem_idx);
  1531. const Real theta_quad_wt = 2*const_pi<Real>()/(FourierOrder*FARFIELD_UPSAMPLE);
  1532. for (Long i = 0; i < ChebOrder*FARFIELD_UPSAMPLE; i++) { // Set wts *= leg_wts * theta_quad_wt
  1533. Real quad_wt = leg_wts[i] * theta_quad_wt;
  1534. for (Long j = 0; j < FourierOrder*FARFIELD_UPSAMPLE; j++) {
  1535. wts_[i*FourierOrder*FARFIELD_UPSAMPLE+j] *= quad_wt;
  1536. }
  1537. }
  1538. for (Long i = 0; i < node_cnt[elem_idx]; i++) { // Set dist_far
  1539. Real dxds = sqrt<Real>(dX_ds[i*COORD_DIM+0]*dX_ds[i*COORD_DIM+0] + dX_ds[i*COORD_DIM+1]*dX_ds[i*COORD_DIM+1] + dX_ds[i*COORD_DIM+2]*dX_ds[i*COORD_DIM+2])*const_pi<Real>()/2;
  1540. Real dxdt = sqrt<Real>(dX_dt[i*COORD_DIM+0]*dX_dt[i*COORD_DIM+0] + dX_dt[i*COORD_DIM+1]*dX_dt[i*COORD_DIM+1] + dX_dt[i*COORD_DIM+2]*dX_dt[i*COORD_DIM+2])*const_pi<Real>()*2;
  1541. Real h_s = dxds/(ChebOrder*FARFIELD_UPSAMPLE-2);
  1542. Real h_t = dxdt/(FourierOrder*FARFIELD_UPSAMPLE-2);
  1543. dist_far_[i] = -log(tol) * std::max(0.15*h_s, 0.30*h_t); // TODO: use better estimate
  1544. }
  1545. }
  1546. }
  1547. template <class Real> void SlenderElemList<Real>::GetFarFieldDensity(Vector<Real>& Fout, const Vector<Real>& Fin) const {
  1548. constexpr Integer MaxOrderFourier = 128/FARFIELD_UPSAMPLE;
  1549. constexpr Integer MaxOrderCheb = 50/FARFIELD_UPSAMPLE;
  1550. auto compute_Mfourier_upsample_transpose = [MaxOrderFourier]() {
  1551. Vector<Matrix<Real>> M_lst(MaxOrderFourier);
  1552. for (Long k = 1; k < MaxOrderFourier; k++) {
  1553. const Integer FourierOrder = k;
  1554. const Integer FourierModes = FourierOrder/2+1;
  1555. const Matrix<Real>& Mfourier_inv = fourier_matrix_inv<Real>(FourierOrder,FourierModes);
  1556. const Matrix<Real>& Mfourier = fourier_matrix<Real>(FourierModes,FourierOrder*FARFIELD_UPSAMPLE);
  1557. M_lst[k] = (Mfourier_inv * Mfourier).Transpose();
  1558. }
  1559. return M_lst;
  1560. };
  1561. auto compute_Mcheb_upsample_transpose = [MaxOrderCheb]() {
  1562. Vector<Matrix<Real>> M_lst(MaxOrderCheb);
  1563. for (Long k = 0; k < MaxOrderCheb; k++) {
  1564. const Integer ChebOrder = k;
  1565. Matrix<Real> Minterp(ChebOrder, ChebOrder*FARFIELD_UPSAMPLE);
  1566. Vector<Real> Vinterp(ChebOrder*ChebOrder*FARFIELD_UPSAMPLE, Minterp.begin(), false);
  1567. LagrangeInterp<Real>::Interpolate(Vinterp, CenterlineNodes(ChebOrder), LegendreQuadRule<Real>(ChebOrder*FARFIELD_UPSAMPLE).first);
  1568. M_lst[k] = Minterp.Transpose();
  1569. }
  1570. return M_lst;
  1571. };
  1572. static const Vector<Matrix<Real>> Mfourier_transpose = compute_Mfourier_upsample_transpose();
  1573. static const Vector<Matrix<Real>> Mcheb_transpose = compute_Mcheb_upsample_transpose();
  1574. const Long Nelem = cheb_order.Dim();
  1575. Vector<Long> node_cnt(Nelem), node_dsp(Nelem);
  1576. { // Set node_cnt, node_dsp
  1577. for (Long i = 0; i < Nelem; i++) {
  1578. node_cnt[i] = cheb_order[i] * fourier_order[i];
  1579. }
  1580. if (Nelem) node_dsp[0] = 0;
  1581. omp_par::scan(node_cnt.begin(), node_dsp.begin(), Nelem);
  1582. }
  1583. const Long Nnodes = (Nelem ? node_dsp[Nelem-1]+node_cnt[Nelem-1] : 0);
  1584. const Long density_dof = (Nnodes ? Fin.Dim() / Nnodes : 0);
  1585. SCTL_ASSERT(Fin.Dim() == Nnodes * density_dof);
  1586. if (Fout.Dim() != Nnodes*(FARFIELD_UPSAMPLE*FARFIELD_UPSAMPLE) * density_dof) {
  1587. Fout.ReInit(Nnodes*(FARFIELD_UPSAMPLE*FARFIELD_UPSAMPLE) * density_dof);
  1588. }
  1589. for (Long i = 0; i < Nelem; i++) {
  1590. const Integer ChebOrder = cheb_order[i];
  1591. const Integer FourierOrder = fourier_order[i];
  1592. const auto& Mfourier_ = Mfourier_transpose[FourierOrder];
  1593. const Matrix<Real> Fin_(ChebOrder, FourierOrder*density_dof, (Iterator<Real>)Fin.begin()+node_dsp[i]*density_dof, false);
  1594. Matrix<Real> F0_(ChebOrder, FourierOrder*FARFIELD_UPSAMPLE*density_dof);
  1595. for (Long l = 0; l < ChebOrder; l++) { // Set F0
  1596. for (Long j0 = 0; j0 < FourierOrder*FARFIELD_UPSAMPLE; j0++) {
  1597. for (Long k = 0; k < density_dof; k++) {
  1598. Real f = 0;
  1599. for (Long j1 = 0; j1 < FourierOrder; j1++) {
  1600. f += Fin_[l][j1*density_dof+k] * Mfourier_[j0][j1];
  1601. }
  1602. F0_[l][j0*density_dof+k] = f;
  1603. }
  1604. }
  1605. }
  1606. Matrix<Real> Fout_(ChebOrder*FARFIELD_UPSAMPLE, FourierOrder*FARFIELD_UPSAMPLE*density_dof, Fout.begin()+node_dsp[i]*FARFIELD_UPSAMPLE*FARFIELD_UPSAMPLE*density_dof, false);
  1607. Matrix<Real>::GEMM(Fout_, Mcheb_transpose[ChebOrder], F0_);
  1608. }
  1609. }
  1610. template <class Real> void SlenderElemList<Real>::FarFieldDensityOperatorTranspose(Matrix<Real>& Mout, const Matrix<Real>& Min, const Long elem_idx) const {
  1611. constexpr Integer MaxOrderFourier = 128/FARFIELD_UPSAMPLE;
  1612. constexpr Integer MaxOrderCheb = 50/FARFIELD_UPSAMPLE;
  1613. auto compute_Mfourier_upsample = [MaxOrderFourier]() {
  1614. Vector<Matrix<Real>> M_lst(MaxOrderFourier);
  1615. for (Long k = 1; k < MaxOrderFourier; k++) {
  1616. const Integer FourierOrder = k;
  1617. const Integer FourierModes = FourierOrder/2+1;
  1618. const Matrix<Real>& Mfourier_inv = fourier_matrix_inv<Real>(FourierOrder,FourierModes);
  1619. const Matrix<Real>& Mfourier = fourier_matrix<Real>(FourierModes,FourierOrder*FARFIELD_UPSAMPLE);
  1620. M_lst[k] = Mfourier_inv * Mfourier;
  1621. }
  1622. return M_lst;
  1623. };
  1624. auto compute_Mcheb_upsample = [MaxOrderCheb]() {
  1625. Vector<Matrix<Real>> M_lst(MaxOrderCheb);
  1626. for (Long k = 0; k < MaxOrderCheb; k++) {
  1627. const Integer ChebOrder = k;
  1628. Matrix<Real> Minterp(ChebOrder, ChebOrder*FARFIELD_UPSAMPLE);
  1629. Vector<Real> Vinterp(ChebOrder*ChebOrder*FARFIELD_UPSAMPLE, Minterp.begin(), false);
  1630. LagrangeInterp<Real>::Interpolate(Vinterp, CenterlineNodes(ChebOrder), LegendreQuadRule<Real>(ChebOrder*FARFIELD_UPSAMPLE).first);
  1631. M_lst[k] = Minterp;
  1632. }
  1633. return M_lst;
  1634. };
  1635. static const Vector<Matrix<Real>> Mfourier = compute_Mfourier_upsample();
  1636. static const Vector<Matrix<Real>> Mcheb = compute_Mcheb_upsample();
  1637. const Integer ChebOrder = cheb_order[elem_idx];
  1638. const Integer FourierOrder = fourier_order[elem_idx];
  1639. const Long N = Min.Dim(1);
  1640. const Long density_dof = Min.Dim(0) / (ChebOrder*FARFIELD_UPSAMPLE*FourierOrder*FARFIELD_UPSAMPLE);
  1641. SCTL_ASSERT(Min.Dim(0) == ChebOrder*FARFIELD_UPSAMPLE*FourierOrder*FARFIELD_UPSAMPLE*density_dof);
  1642. if (Mout.Dim(0) != ChebOrder*FourierOrder*density_dof || Mout.Dim(1) != N) {
  1643. Mout.ReInit(ChebOrder*FourierOrder*density_dof,N);
  1644. Mout.SetZero();
  1645. }
  1646. Matrix<Real> Mtmp(ChebOrder*FARFIELD_UPSAMPLE, FourierOrder*density_dof*N);
  1647. const Matrix<Real> Min_(ChebOrder*FARFIELD_UPSAMPLE, FourierOrder*FARFIELD_UPSAMPLE*density_dof*N, (Iterator<Real>)Min.begin(), false);
  1648. if (FARFIELD_UPSAMPLE != 1) { // Appyl Mfourier // TODO: optimize
  1649. const auto& Mfourier_ = Mfourier[FourierOrder];
  1650. for (Long l = 0; l < ChebOrder*FARFIELD_UPSAMPLE; l++) {
  1651. for (Long j0 = 0; j0 < FourierOrder; j0++) {
  1652. for (Long k = 0; k < density_dof*N; k++) {
  1653. Real f_tmp = 0;
  1654. for (Long j1 = 0; j1 < FourierOrder*FARFIELD_UPSAMPLE; j1++) {
  1655. f_tmp += Min_[l][j1*density_dof*N+k] * Mfourier_[j0][j1];
  1656. }
  1657. Mtmp[l][j0*density_dof*N+k] = f_tmp;
  1658. }
  1659. }
  1660. }
  1661. }else{
  1662. Mtmp.ReInit(ChebOrder*FARFIELD_UPSAMPLE, FourierOrder*density_dof*N, (Iterator<Real>)Min.begin(), false);
  1663. }
  1664. Matrix<Real> Mout_(ChebOrder, FourierOrder*density_dof*N, Mout.begin(), false);
  1665. Matrix<Real>::GEMM(Mout_, Mcheb[ChebOrder], Mtmp);
  1666. }
  1667. template <class Real> template <class Kernel> void SlenderElemList<Real>::SelfInterac(Vector<Matrix<Real>>& M_lst, const Kernel& ker, Real tol, bool trg_dot_prod, const ElementListBase<Real>* self) {
  1668. const auto& elem_lst = *dynamic_cast<const SlenderElemList*>(self);
  1669. const Long Nelem = elem_lst.cheb_order.Dim();
  1670. for (Long elem_idx = 0; elem_idx < Nelem; elem_idx++) { // Initialize quadrature tables
  1671. const Integer ChebOrder = elem_lst.cheb_order[elem_idx];
  1672. const Integer FourierOrder = elem_lst.fourier_order[elem_idx];
  1673. const Integer FourierModes = FourierOrder/2+1;
  1674. Matrix<Real> Mfourier;
  1675. Vector<Real> nds_cos, nds_sin, wts;
  1676. ToroidalSpecialQuadRule<Real,DefaultVecLen<Real>(),ModalUpsample,Kernel,false>(Mfourier, nds_cos, nds_sin, wts, FourierModes+ModalUpsample, (Real)1, 1);
  1677. Vector<Real> quad_nds, quad_wts;
  1678. Matrix<Real> Minterp_quad_nds;
  1679. if (trg_dot_prod) {
  1680. SpecialQuadRule<ModalUpsample,Real,Kernel,true>(quad_nds, quad_wts, ChebOrder, 0, (Real)1, (Real)1, 1);
  1681. } else {
  1682. SpecialQuadRule<ModalUpsample,Real,Kernel,false>(quad_nds, quad_wts, ChebOrder, 0, (Real)1, (Real)1, 1);
  1683. }
  1684. }
  1685. if (M_lst.Dim() != Nelem) M_lst.ReInit(Nelem);
  1686. if (trg_dot_prod) {
  1687. //#pragma omp parallel for schedule(static)
  1688. for (Long elem_idx = 0; elem_idx < Nelem; elem_idx++) {
  1689. if (tol <= pow<15,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<15,true,Kernel>(ker, elem_idx);
  1690. else if (tol <= pow<14,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<14,true,Kernel>(ker, elem_idx);
  1691. else if (tol <= pow<13,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<13,true,Kernel>(ker, elem_idx);
  1692. else if (tol <= pow<12,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<12,true,Kernel>(ker, elem_idx);
  1693. else if (tol <= pow<11,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<11,true,Kernel>(ker, elem_idx);
  1694. else if (tol <= pow<10,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<10,true,Kernel>(ker, elem_idx);
  1695. else if (tol <= pow< 9,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 9,true,Kernel>(ker, elem_idx);
  1696. else if (tol <= pow< 8,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 8,true,Kernel>(ker, elem_idx);
  1697. else if (tol <= pow< 7,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 7,true,Kernel>(ker, elem_idx);
  1698. else if (tol <= pow< 6,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 6,true,Kernel>(ker, elem_idx);
  1699. else if (tol <= pow< 5,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 5,true,Kernel>(ker, elem_idx);
  1700. else if (tol <= pow< 4,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 4,true,Kernel>(ker, elem_idx);
  1701. else if (tol <= pow< 3,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 3,true,Kernel>(ker, elem_idx);
  1702. else if (tol <= pow< 2,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 2,true,Kernel>(ker, elem_idx);
  1703. else if (tol <= pow< 1,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 1,true,Kernel>(ker, elem_idx);
  1704. else M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 0,true,Kernel>(ker, elem_idx);
  1705. }
  1706. } else {
  1707. //#pragma omp parallel for schedule(static)
  1708. for (Long elem_idx = 0; elem_idx < Nelem; elem_idx++) {
  1709. if (tol <= pow<15,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<15,false,Kernel>(ker, elem_idx);
  1710. else if (tol <= pow<14,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<14,false,Kernel>(ker, elem_idx);
  1711. else if (tol <= pow<13,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<13,false,Kernel>(ker, elem_idx);
  1712. else if (tol <= pow<12,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<12,false,Kernel>(ker, elem_idx);
  1713. else if (tol <= pow<11,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<11,false,Kernel>(ker, elem_idx);
  1714. else if (tol <= pow<10,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper<10,false,Kernel>(ker, elem_idx);
  1715. else if (tol <= pow< 9,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 9,false,Kernel>(ker, elem_idx);
  1716. else if (tol <= pow< 8,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 8,false,Kernel>(ker, elem_idx);
  1717. else if (tol <= pow< 7,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 7,false,Kernel>(ker, elem_idx);
  1718. else if (tol <= pow< 6,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 6,false,Kernel>(ker, elem_idx);
  1719. else if (tol <= pow< 5,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 5,false,Kernel>(ker, elem_idx);
  1720. else if (tol <= pow< 4,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 4,false,Kernel>(ker, elem_idx);
  1721. else if (tol <= pow< 3,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 3,false,Kernel>(ker, elem_idx);
  1722. else if (tol <= pow< 2,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 2,false,Kernel>(ker, elem_idx);
  1723. else if (tol <= pow< 1,Real>((Real)0.1)) M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 1,false,Kernel>(ker, elem_idx);
  1724. else M_lst[elem_idx] = elem_lst.template SelfInteracHelper< 0,false,Kernel>(ker, elem_idx);
  1725. }
  1726. }
  1727. }
  1728. template <class Real> template <class Kernel> void SlenderElemList<Real>::NearInterac(Matrix<Real>& M, const Vector<Real>& Xtrg, const Vector<Real>& normal_trg, const Kernel& ker, Real tol, const Long elem_idx, const ElementListBase<Real>* self) {
  1729. const auto& elem_lst = *dynamic_cast<const SlenderElemList*>(self);
  1730. if (normal_trg.Dim()) {
  1731. if (tol <= pow<15,Real>((Real)0.1)) elem_lst.template NearInteracHelper<15,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1732. else if (tol <= pow<14,Real>((Real)0.1)) elem_lst.template NearInteracHelper<14,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1733. else if (tol <= pow<13,Real>((Real)0.1)) elem_lst.template NearInteracHelper<13,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1734. else if (tol <= pow<12,Real>((Real)0.1)) elem_lst.template NearInteracHelper<12,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1735. else if (tol <= pow<11,Real>((Real)0.1)) elem_lst.template NearInteracHelper<11,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1736. else if (tol <= pow<10,Real>((Real)0.1)) elem_lst.template NearInteracHelper<10,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1737. else if (tol <= pow< 9,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 9,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1738. else if (tol <= pow< 8,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 8,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1739. else if (tol <= pow< 7,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 7,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1740. else if (tol <= pow< 6,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 6,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1741. else if (tol <= pow< 5,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 5,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1742. else if (tol <= pow< 4,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 4,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1743. else if (tol <= pow< 3,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 3,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1744. else if (tol <= pow< 2,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 2,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1745. else if (tol <= pow< 1,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 1,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1746. else elem_lst.template NearInteracHelper< 0,true,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1747. } else {
  1748. if (tol <= pow<15,Real>((Real)0.1)) elem_lst.template NearInteracHelper<15,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1749. else if (tol <= pow<14,Real>((Real)0.1)) elem_lst.template NearInteracHelper<14,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1750. else if (tol <= pow<13,Real>((Real)0.1)) elem_lst.template NearInteracHelper<13,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1751. else if (tol <= pow<12,Real>((Real)0.1)) elem_lst.template NearInteracHelper<12,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1752. else if (tol <= pow<11,Real>((Real)0.1)) elem_lst.template NearInteracHelper<11,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1753. else if (tol <= pow<10,Real>((Real)0.1)) elem_lst.template NearInteracHelper<10,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1754. else if (tol <= pow< 9,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 9,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1755. else if (tol <= pow< 8,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 8,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1756. else if (tol <= pow< 7,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 7,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1757. else if (tol <= pow< 6,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 6,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1758. else if (tol <= pow< 5,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 5,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1759. else if (tol <= pow< 4,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 4,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1760. else if (tol <= pow< 3,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 3,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1761. else if (tol <= pow< 2,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 2,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1762. else if (tol <= pow< 1,Real>((Real)0.1)) elem_lst.template NearInteracHelper< 1,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1763. else elem_lst.template NearInteracHelper< 0,false,Kernel>(M, Xtrg, normal_trg, ker, elem_idx);
  1764. }
  1765. }
  1766. template <class Real> template <Integer digits, bool trg_dot_prod, class Kernel> void SlenderElemList<Real>::NearInteracHelper(Matrix<Real>& M, const Vector<Real>& Xtrg, const Vector<Real>& normal_trg, const Kernel& ker, const Long elem_idx) const {
  1767. using Vec3 = Tensor<Real,true,COORD_DIM,1>;
  1768. static constexpr Integer KDIM0 = Kernel::SrcDim();
  1769. static constexpr Integer KDIM1 = Kernel::TrgDim()/(trg_dot_prod?COORD_DIM:1);
  1770. //const Integer digits = (Integer)(log(tol)/log(0.1)+0.5);
  1771. static constexpr Real tol = pow<digits,Real>((Real)0.1);
  1772. const Integer ChebOrder = cheb_order[elem_idx];
  1773. const Integer FourierOrder = fourier_order[elem_idx];
  1774. const Integer FourierModes = FourierOrder/2+1;
  1775. const Matrix<Real> M_fourier_inv = fourier_matrix_inv_transpose<Real>(FourierOrder,FourierModes);
  1776. const Vector<Real> coord(COORD_DIM*ChebOrder,(Iterator<Real>)this-> coord.begin()+COORD_DIM*elem_dsp[elem_idx],false);
  1777. const Vector<Real> dx(COORD_DIM*ChebOrder,(Iterator<Real>)this-> dx.begin()+COORD_DIM*elem_dsp[elem_idx],false);
  1778. const Vector<Real> d2x(COORD_DIM*ChebOrder,(Iterator<Real>)this-> d2x.begin()+COORD_DIM*elem_dsp[elem_idx],false);
  1779. const Vector<Real> radius( 1*ChebOrder,(Iterator<Real>)this->radius.begin()+ elem_dsp[elem_idx],false);
  1780. const Vector<Real> dr( 1*ChebOrder,(Iterator<Real>)this-> dr.begin()+ elem_dsp[elem_idx],false);
  1781. const Vector<Real> e1(COORD_DIM*ChebOrder,(Iterator<Real>)this-> e1.begin()+COORD_DIM*elem_dsp[elem_idx],false);
  1782. const Long Ntrg = Xtrg.Dim() / COORD_DIM;
  1783. if (M.Dim(0) != ChebOrder*FourierOrder*KDIM0 || M.Dim(1) != Ntrg*KDIM1) {
  1784. M.ReInit(ChebOrder*FourierOrder*KDIM0, Ntrg*KDIM1);
  1785. }
  1786. #pragma omp parallel for
  1787. for (Long i = 0; i < Ntrg; i++) {
  1788. const Vec3 Xt((Iterator<Real>)Xtrg.begin()+i*COORD_DIM);
  1789. const Vec3 n_trg = (trg_dot_prod ? Vec3((Iterator<Real>)normal_trg.begin()+i*COORD_DIM) : Vec3((Real)0));
  1790. Matrix<Real> M_modal(ChebOrder, KDIM0*KDIM1*FourierModes*2);
  1791. { // Set M_modal
  1792. Vector<Real> quad_nds, quad_wts; // Quadrature rule in s
  1793. auto adap_quad_rule = [&ChebOrder,&radius,&coord,&dx](Vector<Real>& quad_nds, Vector<Real>& quad_wts, const Vec3& x_trg) {
  1794. const Long LegQuadOrder = (Long)(-0.24*log(tol)*const_pi<Real>()/2)+1;
  1795. const auto& leg_nds = LegendreQuadRule<Real>(LegQuadOrder).first;
  1796. const auto& leg_wts = LegendreQuadRule<Real>(LegQuadOrder).second;
  1797. auto adap_ref = [&LegQuadOrder,&leg_nds,&leg_wts](Vector<Real>& nds, Vector<Real>& wts, Real a, Real b, Integer levels) {
  1798. if (nds.Dim() != levels * LegQuadOrder) nds.ReInit(levels*LegQuadOrder);
  1799. if (wts.Dim() != levels * LegQuadOrder) wts.ReInit(levels*LegQuadOrder);
  1800. Vector<Real> nds_(nds.Dim(), nds.begin(), false);
  1801. Vector<Real> wts_(wts.Dim(), wts.begin(), false);
  1802. while (levels) {
  1803. Vector<Real> nds0(LegQuadOrder, nds_.begin(), false);
  1804. Vector<Real> wts0(LegQuadOrder, wts_.begin(), false);
  1805. Vector<Real> nds1((levels-1)*LegQuadOrder, nds_.begin()+LegQuadOrder, false);
  1806. Vector<Real> wts1((levels-1)*LegQuadOrder, wts_.begin()+LegQuadOrder, false);
  1807. Real end_point = (levels==1 ? b : (a+b)*0.5);
  1808. nds0 = leg_nds * (end_point-a) + a;
  1809. wts0 = leg_wts * fabs<Real>(end_point-a);
  1810. nds_.Swap(nds1);
  1811. wts_.Swap(wts1);
  1812. a = end_point;
  1813. levels--;
  1814. }
  1815. };
  1816. // TODO: develop special quadrature rule instead of adaptive integration
  1817. if (0) { // adaptive/dyadic refinement on element ends
  1818. const Integer levels = 6;
  1819. quad_nds.ReInit(2*levels*LegQuadOrder);
  1820. quad_wts.ReInit(2*levels*LegQuadOrder);
  1821. Vector<Real> nds0(levels*LegQuadOrder,quad_nds.begin(),false);
  1822. Vector<Real> wts0(levels*LegQuadOrder,quad_wts.begin(),false);
  1823. Vector<Real> nds1(levels*LegQuadOrder,quad_nds.begin()+levels*LegQuadOrder,false);
  1824. Vector<Real> wts1(levels*LegQuadOrder,quad_wts.begin()+levels*LegQuadOrder,false);
  1825. adap_ref(nds0, wts0, 0.5, 0.0, levels);
  1826. adap_ref(nds1, wts1, 0.5, 1.0, levels);
  1827. } else { // dyadic refinement near target point
  1828. Real dist_min, s_min, dxds;
  1829. { // Set dist_min, s_min, dxds
  1830. auto get_dist = [&ChebOrder,&radius,&coord,&dx] (const Vec3& x_trg, Real s) -> Real {
  1831. Vector<Real> interp_wts(ChebOrder); // TODO: pre-allocate
  1832. LagrangeInterp<Real>::Interpolate(interp_wts, CenterlineNodes(ChebOrder), Vector<Real>(1,Ptr2Itr<Real>(&s,1),false));
  1833. Real r0 = 0;
  1834. Vec3 x0, dx_ds0;
  1835. for (Long i = 0; i < COORD_DIM; i++) {
  1836. x0(i,0) = 0;
  1837. dx_ds0(i,0) = 0;
  1838. }
  1839. for (Long i = 0; i < ChebOrder; i++) {
  1840. r0 += radius[i] * interp_wts[i];
  1841. x0(0,0) += coord[0*ChebOrder+i] * interp_wts[i];
  1842. x0(1,0) += coord[1*ChebOrder+i] * interp_wts[i];
  1843. x0(2,0) += coord[2*ChebOrder+i] * interp_wts[i];
  1844. dx_ds0(0,0) += dx[0*ChebOrder+i] * interp_wts[i];
  1845. dx_ds0(1,0) += dx[1*ChebOrder+i] * interp_wts[i];
  1846. dx_ds0(2,0) += dx[2*ChebOrder+i] * interp_wts[i];
  1847. }
  1848. Vec3 dx = x0 - x_trg;
  1849. Vec3 n0 = dx_ds0 * sqrt<Real>(1/dot_prod(dx_ds0, dx_ds0));
  1850. Real dz = dot_prod(dx, n0);
  1851. Vec3 dr = dx - n0*dz;
  1852. Real dR = sqrt<Real>(dot_prod(dr,dr)) - r0;
  1853. return sqrt<Real>(dR*dR + dz*dz);
  1854. };
  1855. StaticArray<Real,2> dist;
  1856. StaticArray<Real,2> s_val{0,1};
  1857. dist[0] = get_dist(x_trg, s_val[0]);
  1858. dist[1] = get_dist(x_trg, s_val[1]);
  1859. for (Long i = 0; i < 20; i++) { // Binary search: set dist, s_val // TODO: use Netwon's method
  1860. Real ss = (s_val[0] + s_val[1]) * 0.5;
  1861. Real dd = get_dist(x_trg, ss);
  1862. if (dist[0] > dist[1]) {
  1863. dist[0] = dd;
  1864. s_val[0] = ss;
  1865. } else {
  1866. dist[1] = dd;
  1867. s_val[1] = ss;
  1868. }
  1869. }
  1870. if (dist[0] < dist[1]) { // Set dis_min, s_min
  1871. dist_min = dist[0];
  1872. s_min = s_val[0];
  1873. } else {
  1874. dist_min = dist[1];
  1875. s_min = s_val[1];
  1876. }
  1877. { // Set dx_ds;
  1878. Vector<Real> interp_wts(ChebOrder); // TODO: pre-allocate
  1879. LagrangeInterp<Real>::Interpolate(interp_wts, CenterlineNodes(ChebOrder), Vector<Real>(1,Ptr2Itr<Real>(&s_min,1),false));
  1880. Vec3 dxds_vec;
  1881. for (Long i = 0; i < COORD_DIM; i++) {
  1882. dxds_vec(i,0) = 0;
  1883. }
  1884. for (Long i = 0; i < ChebOrder; i++) {
  1885. dxds_vec(0,0) += dx[0*ChebOrder+i] * interp_wts[i];
  1886. dxds_vec(1,0) += dx[1*ChebOrder+i] * interp_wts[i];
  1887. dxds_vec(2,0) += dx[2*ChebOrder+i] * interp_wts[i];
  1888. }
  1889. dxds = sqrt<Real>(dot_prod(dxds_vec,dxds_vec))*const_pi<Real>()/2;
  1890. }
  1891. }
  1892. Real h0 = (s_min)*dxds/(LegQuadOrder-1);
  1893. Real h1 = (1-s_min)*dxds/(LegQuadOrder-1);
  1894. Real dist_far0 = -0.25 * log(tol)*h0; // TODO: use better estimate
  1895. Real dist_far1 = -0.25 * log(tol)*h1; // TODO: use better estimate
  1896. Integer adap_levels0 = (s_min==0 ? 0 : std::max<Integer>(0,(Integer)(log(dist_far0/dist_min)/log(2.0)+0.5))+1);
  1897. Integer adap_levels1 = (s_min==1 ? 0 : std::max<Integer>(0,(Integer)(log(dist_far1/dist_min)/log(2.0)+0.5))+1);
  1898. Long N0 = adap_levels0 * LegQuadOrder;
  1899. Long N1 = adap_levels1 * LegQuadOrder;
  1900. quad_nds.ReInit(N0+N1);
  1901. quad_wts.ReInit(N0+N1);
  1902. Vector<Real> nds0(N0, quad_nds.begin(), false);
  1903. Vector<Real> wts0(N0, quad_wts.begin(), false);
  1904. Vector<Real> nds1(N1, quad_nds.begin()+N0, false);
  1905. Vector<Real> wts1(N1, quad_wts.begin()+N0, false);
  1906. adap_ref(nds0, wts0, 0, s_min, adap_levels0);
  1907. adap_ref(nds1, wts1, 1, s_min, adap_levels1);
  1908. }
  1909. };
  1910. adap_quad_rule(quad_nds, quad_wts, Xt);
  1911. Matrix<Real> Minterp_quad_nds;
  1912. { // Set Minterp_quad_nds
  1913. Minterp_quad_nds.ReInit(ChebOrder, quad_nds.Dim());
  1914. Vector<Real> Vinterp_quad_nds(ChebOrder*quad_nds.Dim(), Minterp_quad_nds.begin(), false);
  1915. LagrangeInterp<Real>::Interpolate(Vinterp_quad_nds, CenterlineNodes(ChebOrder), quad_nds);
  1916. }
  1917. Vec3 x_trg = Xt;
  1918. Matrix<Real> r_src, dr_src, x_src, dx_src, d2x_src, e1_src;
  1919. r_src .ReInit( 1,quad_nds.Dim());
  1920. dr_src .ReInit( 1,quad_nds.Dim());
  1921. x_src .ReInit(COORD_DIM,quad_nds.Dim());
  1922. dx_src .ReInit(COORD_DIM,quad_nds.Dim());
  1923. d2x_src.ReInit(COORD_DIM,quad_nds.Dim());
  1924. e1_src .ReInit(COORD_DIM,quad_nds.Dim());
  1925. { // Set x_src, x_trg (improve numerical stability)
  1926. Matrix<Real> x_nodes(COORD_DIM,ChebOrder, (Iterator<Real>)coord.begin(), true);
  1927. for (Long j = 0; j < ChebOrder; j++) {
  1928. for (Integer k = 0; k < COORD_DIM; k++) {
  1929. x_nodes[k][j] -= x_trg(k,0);
  1930. }
  1931. }
  1932. Matrix<Real>::GEMM( x_src, x_nodes, Minterp_quad_nds);
  1933. for (Integer k = 0; k < COORD_DIM; k++) {
  1934. x_trg(k,0) = 0;
  1935. }
  1936. }
  1937. //Matrix<Real>::GEMM( x_src, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) coord.begin(),false), Minterp_quad_nds);
  1938. Matrix<Real>::GEMM( dx_src, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) dx.begin(),false), Minterp_quad_nds);
  1939. Matrix<Real>::GEMM(d2x_src, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) d2x.begin(),false), Minterp_quad_nds);
  1940. Matrix<Real>::GEMM( r_src, Matrix<Real>( 1,ChebOrder,(Iterator<Real>)radius.begin(),false), Minterp_quad_nds);
  1941. Matrix<Real>::GEMM( dr_src, Matrix<Real>( 1,ChebOrder,(Iterator<Real>) dr.begin(),false), Minterp_quad_nds);
  1942. Matrix<Real>::GEMM( e1_src, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) e1.begin(),false), Minterp_quad_nds);
  1943. for (Long j = 0; j < quad_nds.Dim(); j++) { // Set e2_src
  1944. Vec3 e1, dx;
  1945. for (Integer k = 0; k < COORD_DIM; k++) {
  1946. e1(k,0) = e1_src[k][j];
  1947. dx(k,0) = dx_src[k][j];
  1948. }
  1949. e1 = e1 - dx * dot_prod(e1, dx) * (1/dot_prod(dx,dx));
  1950. e1 = e1 * (1/sqrt<Real>(dot_prod(e1,e1)));
  1951. for (Integer k = 0; k < COORD_DIM; k++) {
  1952. e1_src[k][j] = e1(k,0);
  1953. }
  1954. }
  1955. const Vec3 y_trg = x_trg;
  1956. Matrix<Real> M_tor(quad_nds.Dim(), KDIM0*KDIM1*FourierModes*2); // TODO: pre-allocate
  1957. toroidal_greens_fn_batched<digits,ModalUpsample,trg_dot_prod>(M_tor, y_trg, Vec3((Real)1), (Real)0, n_trg, x_src, dx_src, d2x_src, r_src, dr_src, e1_src, ker, FourierModes);
  1958. for (Long ii = 0; ii < M_tor.Dim(0); ii++) {
  1959. for (Long jj = 0; jj < M_tor.Dim(1); jj++) {
  1960. M_tor[ii][jj] *= quad_wts[ii];
  1961. }
  1962. }
  1963. Matrix<Real>::GEMM(M_modal, Minterp_quad_nds, M_tor);
  1964. }
  1965. Matrix<Real> M_nodal(ChebOrder, KDIM0*KDIM1*FourierOrder);
  1966. { // Set M_nodal
  1967. Matrix<Real> M_nodal_(ChebOrder*KDIM0*KDIM1, FourierOrder, M_nodal.begin(), false);
  1968. const Matrix<Real> M_modal_(ChebOrder*KDIM0*KDIM1, FourierModes*2, M_modal.begin(), false);
  1969. Matrix<Real>::GEMM(M_nodal_, M_modal_, M_fourier_inv);
  1970. }
  1971. { // Set M
  1972. for (Integer i0 = 0; i0 < ChebOrder; i0++) {
  1973. for (Integer i1 = 0; i1 < FourierOrder; i1++) {
  1974. for (Integer k0 = 0; k0 < KDIM0; k0++) {
  1975. for (Integer k1 = 0; k1 < KDIM1; k1++) {
  1976. M[(i0*FourierOrder+i1)*KDIM0+k0][i*KDIM1+k1] = M_nodal[i0][(k0*KDIM1+k1)*FourierOrder+i1] * ker.template uKerScaleFactor<Real>();
  1977. }
  1978. }
  1979. }
  1980. }
  1981. }
  1982. }
  1983. }
  1984. template <class Real> const Vector<Real>& SlenderElemList<Real>::CenterlineNodes(Integer Order) {
  1985. return ChebQuadRule<Real>::nds(Order);
  1986. }
  1987. template <class Real> void SlenderElemList<Real>::Write(const std::string& fname, const Comm& comm) const {
  1988. auto allgather = [&comm](Vector<Real>& v_out, const Vector<Real>& v_in) {
  1989. const Long Nproc = comm.Size();
  1990. StaticArray<Long,1> len{v_in.Dim()};
  1991. Vector<Long> cnt(Nproc), dsp(Nproc);
  1992. comm.Allgather(len+0, 1, cnt.begin(), 1); dsp = 0;
  1993. omp_par::scan(cnt.begin(), dsp.begin(), Nproc);
  1994. v_out.ReInit(dsp[Nproc-1]+cnt[Nproc-1]);
  1995. comm.Allgatherv(v_in.begin(), v_in.Dim(), v_out.begin(), cnt.begin(), dsp.begin());
  1996. };
  1997. auto allgatherl = [&comm](Vector<Long>& v_out, const Vector<Long>& v_in) {
  1998. const Long Nproc = comm.Size();
  1999. StaticArray<Long,1> len{v_in.Dim()};
  2000. Vector<Long> cnt(Nproc), dsp(Nproc);
  2001. comm.Allgather(len+0, 1, cnt.begin(), 1); dsp = 0;
  2002. omp_par::scan(cnt.begin(), dsp.begin(), Nproc);
  2003. v_out.ReInit(dsp[Nproc-1]+cnt[Nproc-1]);
  2004. comm.Allgatherv(v_in.begin(), v_in.Dim(), v_out.begin(), cnt.begin(), dsp.begin());
  2005. };
  2006. Vector<Real> radius_, coord_, e1_;
  2007. allgather(radius_, radius);
  2008. allgather( coord_, coord);
  2009. allgather( e1_, e1);
  2010. Vector<Long> cheb_order_, elem_dsp_, fourier_order_;
  2011. allgatherl( cheb_order_, cheb_order );
  2012. allgatherl(fourier_order_, fourier_order);
  2013. elem_dsp_.ReInit(cheb_order_.Dim()); elem_dsp_ = 0;
  2014. omp_par::scan(cheb_order_.begin(), elem_dsp_.begin(), cheb_order_.Dim());
  2015. if (comm.Rank()) return;
  2016. const Integer precision = 18, width = 26;
  2017. std::ofstream file;
  2018. file.open(fname, std::ofstream::out | std::ofstream::trunc);
  2019. if (!file.good()) {
  2020. std::cout << "Unable to open file for writing:" << fname << '\n';
  2021. }
  2022. // Header
  2023. file<<"#";
  2024. file<<std::setw(width-1)<<"X";
  2025. file<<std::setw(width)<<"Y";
  2026. file<<std::setw(width)<<"Z";
  2027. file<<std::setw(width)<<"r";
  2028. file<<std::setw(width)<<"orient-x";
  2029. file<<std::setw(width)<<"orient-y";
  2030. file<<std::setw(width)<<"orient-z";
  2031. file<<std::setw(width)<<"ChebOrder";
  2032. file<<std::setw(width)<<"FourierOrder";
  2033. file<<'\n';
  2034. file<<std::scientific<<std::setprecision(precision);
  2035. for (Long i = 0; i < cheb_order_.Dim(); i++) {
  2036. for (Long j = 0; j < cheb_order_[i]; j++) {
  2037. for (Integer k = 0; k < COORD_DIM; k++) {
  2038. file<<std::setw(width)<<coord_[elem_dsp_[i]*COORD_DIM + k*cheb_order_[i]+j];
  2039. }
  2040. file<<std::setw(width)<<radius_[elem_dsp_[i] + j];
  2041. for (Integer k = 0; k < COORD_DIM; k++) {
  2042. file<<std::setw(width)<<e1_[elem_dsp_[i]*COORD_DIM + k*cheb_order_[i]+j];
  2043. }
  2044. if (!j) {
  2045. file<<std::setw(width)<<cheb_order_[i];
  2046. file<<std::setw(width)<<fourier_order_[i];
  2047. }
  2048. file<<"\n";
  2049. }
  2050. }
  2051. file.close();
  2052. }
  2053. template <class Real> void SlenderElemList<Real>::Read(const std::string& fname, const Comm& comm) {
  2054. std::ifstream file;
  2055. file.open(fname, std::ifstream::in);
  2056. if (!file.good()) {
  2057. std::cout << "Unable to open file for reading:" << fname << '\n';
  2058. }
  2059. std::string line;
  2060. Vector<Real> coord_, radius_, e1_;
  2061. Vector<Integer> cheb_order_, fourier_order_;
  2062. while (std::getline(file, line)) { // Set coord_, radius_, e1_, cheb_order_, fourier_order_
  2063. size_t first_char_pos = line.find_first_not_of(' ');
  2064. if (first_char_pos == std::string::npos || line[first_char_pos] == '#') continue;
  2065. std::istringstream iss(line);
  2066. for (Integer k = 0; k < COORD_DIM; k++) { // read coord_
  2067. Real a;
  2068. iss>>a;
  2069. SCTL_ASSERT(!iss.fail());
  2070. coord_.PushBack(a);
  2071. }
  2072. { // read radius_
  2073. Real a;
  2074. iss>>a;
  2075. SCTL_ASSERT(!iss.fail());
  2076. radius_.PushBack(a);
  2077. }
  2078. for (Integer k = 0; k < COORD_DIM; k++) { // read e1_
  2079. Real a;
  2080. iss>>a;
  2081. SCTL_ASSERT(!iss.fail());
  2082. e1.PushBack(a);
  2083. }
  2084. Integer ChebOrder, FourierOrder;
  2085. if (iss>>ChebOrder>>FourierOrder) {
  2086. cheb_order_.PushBack(ChebOrder);
  2087. fourier_order_.PushBack(FourierOrder);
  2088. } else {
  2089. cheb_order_.PushBack(-1);
  2090. fourier_order_.PushBack(-1);
  2091. }
  2092. }
  2093. file.close();
  2094. Long offset = 0;
  2095. Vector<Integer> cheb_order__, fourier_order__;
  2096. while (offset < cheb_order_.Dim()) { // Set cheb_order__, fourier_order__
  2097. Integer ChebOrder = cheb_order_[offset];
  2098. Integer FourierOrder = fourier_order_[offset];
  2099. for (Integer j = 1; j < ChebOrder; j++) {
  2100. SCTL_ASSERT(cheb_order_[offset+j] == ChebOrder || cheb_order_[offset+j] == -1);
  2101. SCTL_ASSERT(fourier_order_[offset+j] == FourierOrder || fourier_order_[offset+j] == -1);
  2102. }
  2103. cheb_order__.PushBack(ChebOrder);
  2104. fourier_order__.PushBack(FourierOrder);
  2105. offset += ChebOrder;
  2106. }
  2107. { // Distribute across processes and init SlenderElemList
  2108. const Long Np = comm.Size();
  2109. const Long pid = comm.Rank();
  2110. const Long Nelem = cheb_order__.Dim();
  2111. const Long i0 = Nelem*(pid+0)/Np;
  2112. const Long i1 = Nelem*(pid+1)/Np;
  2113. Vector<Integer> cheb_order, fourier_order;
  2114. cheb_order.ReInit(i1-i0, cheb_order__.begin()+i0, false);
  2115. fourier_order.ReInit(i1-i0, fourier_order__.begin()+i0, false);
  2116. Vector<Integer> elem_offset(Nelem+1); elem_offset = 0;
  2117. omp_par::scan(cheb_order__.begin(), elem_offset.begin(), Nelem);
  2118. elem_offset[Nelem] = (Nelem ? elem_offset[Nelem-1] + cheb_order__[Nelem-1] : 0);
  2119. const Long j0 = elem_offset[i0];
  2120. const Long j1 = elem_offset[i1];
  2121. Vector<Real> radius, coord, e1;
  2122. radius.ReInit((j1-j0), radius_.begin()+j0, false);
  2123. coord.ReInit((j1-j0)*COORD_DIM, coord_.begin()+j0*COORD_DIM, false);
  2124. if (e1_.Dim()) e1.ReInit((j1-j0)*COORD_DIM, e1_.begin()+j0*COORD_DIM, false);
  2125. Init(cheb_order, fourier_order, coord, radius, e1);
  2126. }
  2127. }
  2128. template <class Real> void SlenderElemList<Real>::GetVTUData(VTUData& vtu_data, const Vector<Real>& F, const Long elem_idx) const {
  2129. if (elem_idx == -1) {
  2130. const Long Nelem = cheb_order.Dim();
  2131. Long dof = 0, offset = 0;
  2132. if (F.Dim()) { // Set dof
  2133. Long Nnodes = 0;
  2134. for (Long i = 0; i < Nelem; i++) {
  2135. Nnodes += cheb_order[i] * fourier_order[i];
  2136. }
  2137. dof = F.Dim() / Nnodes;
  2138. SCTL_ASSERT(F.Dim() == Nnodes * dof);
  2139. }
  2140. for (Long i = 0; i < Nelem; i++) {
  2141. const Vector<Real> F_(cheb_order[i]*fourier_order[i]*dof, (Iterator<Real>)F.begin()+offset, false);
  2142. GetVTUData(vtu_data, F_, i);
  2143. offset += F_.Dim();
  2144. }
  2145. return;
  2146. }
  2147. Vector<Real> X;
  2148. const Integer ChebOrder = cheb_order[elem_idx];
  2149. const Integer FourierOrder = fourier_order[elem_idx];
  2150. GetGeom(&X,nullptr,nullptr,nullptr,nullptr, CenterlineNodes(ChebOrder), sin_theta<Real>(FourierOrder), cos_theta<Real>(FourierOrder), elem_idx);
  2151. Long point_offset = vtu_data.coord.Dim() / COORD_DIM;
  2152. for (const auto& x : X) vtu_data.coord.PushBack((VTUData::VTKReal)x);
  2153. for (const auto& f : F) vtu_data.value.PushBack((VTUData::VTKReal)f);
  2154. for (Long i = 0; i < ChebOrder-1; i++) {
  2155. for (Long j = 0; j <= FourierOrder; j++) {
  2156. vtu_data.connect.PushBack(point_offset + (i+0)*FourierOrder+(j%FourierOrder));
  2157. vtu_data.connect.PushBack(point_offset + (i+1)*FourierOrder+(j%FourierOrder));
  2158. }
  2159. vtu_data.offset.PushBack(vtu_data.connect.Dim());
  2160. vtu_data.types.PushBack(6);
  2161. }
  2162. }
  2163. template <class Real> void SlenderElemList<Real>::WriteVTK(const std::string& fname, const Vector<Real>& F, const Comm& comm) const {
  2164. VTUData vtu_data;
  2165. GetVTUData(vtu_data, F);
  2166. vtu_data.WriteVTK(fname, comm);
  2167. }
  2168. template <class Real> template <class Kernel> void SlenderElemList<Real>::test(const Comm& comm, Real tol) {
  2169. sctl::Profile::Enable(false);
  2170. const Long pid = comm.Rank();
  2171. const Long Np = comm.Size();
  2172. SlenderElemList<Real> elem_lst0;
  2173. //elem_lst0.Read("data/geom.data"); // Read geometry from file
  2174. if (1) { // Initialize elem_lst0 in code
  2175. const Long Nelem = 16;
  2176. const Long ChebOrder = 10;
  2177. const Long FourierOrder = 8;
  2178. Vector<Real> coord, radius;
  2179. Vector<Long> cheb_order, fourier_order;
  2180. const Long k0 = (Nelem*(pid+0))/Np;
  2181. const Long k1 = (Nelem*(pid+1))/Np;
  2182. for (Long k = k0; k < k1; k++) {
  2183. cheb_order.PushBack(ChebOrder);
  2184. fourier_order.PushBack(FourierOrder);
  2185. const auto& nds = CenterlineNodes(ChebOrder);
  2186. for (Long i = 0; i < nds.Dim(); i++) {
  2187. Real theta = 2*const_pi<Real>()*(k+nds[i])/Nelem;
  2188. coord.PushBack(cos<Real>(theta));
  2189. coord.PushBack(sin<Real>(theta));
  2190. coord.PushBack(0.1*sin<Real>(2*theta));
  2191. radius.PushBack(0.01*(2+sin<Real>(theta+sqrt<Real>(2))));
  2192. }
  2193. }
  2194. elem_lst0.Init(cheb_order, fourier_order, coord, radius);
  2195. }
  2196. Kernel ker_fn;
  2197. BoundaryIntegralOp<Real,Kernel> BIOp(ker_fn, false, comm);
  2198. BIOp.AddElemList(elem_lst0);
  2199. BIOp.SetAccuracy(tol);
  2200. // Warm-up run
  2201. Vector<Real> F(BIOp.Dim(0)), U; F = 1;
  2202. BIOp.ComputePotential(U,F);
  2203. BIOp.ClearSetup();
  2204. U = 0;
  2205. Profile::Enable(true);
  2206. Profile::Tic("Setup+Eval", &comm, true);
  2207. BIOp.ComputePotential(U,F);
  2208. Profile::Toc();
  2209. Vector<Real> Uerr = U + 0.5;
  2210. elem_lst0.WriteVTK("Uerr_", Uerr, comm); // Write VTK
  2211. { // Print error
  2212. StaticArray<Real,2> max_err{0,0};
  2213. for (auto x : Uerr) max_err[0] = std::max<Real>(max_err[0], fabs(x));
  2214. comm.Allreduce(max_err+0, max_err+1, 1, Comm::CommOp::MAX);
  2215. if (!pid) std::cout<<"Error = "<<max_err[1]<<'\n';
  2216. }
  2217. Profile::Enable(false);
  2218. Profile::print(&comm);
  2219. }
  2220. template <class Real> void SlenderElemList<Real>::test_greens_identity(const Comm& comm, Real tol) {
  2221. using KerSL = Laplace3D_FxU;
  2222. using KerDL = Laplace3D_DxU;
  2223. using KerGrad = Laplace3D_FxdU;
  2224. const auto concat_vecs = [](Vector<Real>& v, const Vector<Vector<Real>>& vec_lst) {
  2225. const Long N = vec_lst.Dim();
  2226. Vector<Long> dsp(N+1); dsp[0] = 0;
  2227. for (Long i = 0; i < N; i++) {
  2228. dsp[i+1] = dsp[i] + vec_lst[i].Dim();
  2229. }
  2230. if (v.Dim() != dsp[N]) v.ReInit(dsp[N]);
  2231. for (Long i = 0; i < N; i++) {
  2232. Vector<Real> v_(vec_lst[i].Dim(), v.begin()+dsp[i], false);
  2233. v_ = vec_lst[i];
  2234. }
  2235. };
  2236. auto loop_geom = [](Real& x, Real& y, Real& z, Real& r, const Real theta){
  2237. x = cos<Real>(theta);
  2238. y = sin<Real>(theta);
  2239. z = 0.1*sin<Real>(theta-sqrt<Real>(2));
  2240. r = 0.01*(2+sin<Real>(theta+sqrt<Real>(2)));
  2241. };
  2242. sctl::Profile::Enable(false);
  2243. const Long pid = comm.Rank();
  2244. const Long Np = comm.Size();
  2245. SlenderElemList elem_lst0;
  2246. SlenderElemList elem_lst1;
  2247. { // Set elem_lst0, elem_lst1
  2248. const Long Nelem = 16;
  2249. const Long idx0 = Nelem*(pid+0)/Np;
  2250. const Long idx1 = Nelem*(pid+1)/Np;
  2251. Vector<Real> coord0, radius0;
  2252. Vector<Long> cheb_order0, fourier_order0;
  2253. for (Long k = idx0; k < idx1; k++) { // Init elem_lst0
  2254. const Integer ChebOrder = 8, FourierOrder = 14;
  2255. const auto& nds = CenterlineNodes(ChebOrder);
  2256. for (Long i = 0; i < nds.Dim(); i++) {
  2257. Real x, y, z, r;
  2258. loop_geom(x, y, z, r, const_pi<Real>()*(k+nds[i])/Nelem);
  2259. coord0.PushBack(x);
  2260. coord0.PushBack(y);
  2261. coord0.PushBack(z);
  2262. radius0.PushBack(r);
  2263. }
  2264. cheb_order0.PushBack(ChebOrder);
  2265. fourier_order0.PushBack(FourierOrder);
  2266. }
  2267. elem_lst0.Init(cheb_order0, fourier_order0, coord0, radius0);
  2268. Vector<Real> coord1, radius1;
  2269. Vector<Long> cheb_order1, fourier_order1;
  2270. for (Long k = idx0; k < idx1; k++) { // Init elem_lst1
  2271. const Integer ChebOrder = 10, FourierOrder = 14;
  2272. const auto& nds = CenterlineNodes(ChebOrder);
  2273. for (Long i = 0; i < nds.Dim(); i++) {
  2274. Real x, y, z, r;
  2275. loop_geom(x, y, z, r, const_pi<Real>()*(1+(k+nds[i])/Nelem));
  2276. coord1.PushBack(x);
  2277. coord1.PushBack(y);
  2278. coord1.PushBack(z);
  2279. radius1.PushBack(r);
  2280. }
  2281. cheb_order1.PushBack(ChebOrder);
  2282. fourier_order1.PushBack(FourierOrder);
  2283. }
  2284. elem_lst1.Init(cheb_order1, fourier_order1, coord1, radius1);
  2285. }
  2286. KerSL kernel_sl;
  2287. KerDL kernel_dl;
  2288. KerGrad kernel_grad;
  2289. BoundaryIntegralOp<Real,KerSL> BIOpSL(kernel_sl, false, comm);
  2290. BoundaryIntegralOp<Real,KerDL> BIOpDL(kernel_dl, false, comm);
  2291. BIOpSL.AddElemList(elem_lst0, "elem_lst0");
  2292. BIOpSL.AddElemList(elem_lst1, "elem_lst1");
  2293. BIOpDL.AddElemList(elem_lst0, "elem_lst0");
  2294. BIOpDL.AddElemList(elem_lst1, "elem_lst1");
  2295. BIOpSL.SetAccuracy(tol);
  2296. BIOpDL.SetAccuracy(tol);
  2297. Vector<Real> X, Xn, Fs, Fd, Uref, Us, Ud;
  2298. { // Get X, Xn
  2299. Vector<Vector<Real>> X_(2), Xn_(2);
  2300. elem_lst0.GetNodeCoord(&X_[0], &Xn_[0], nullptr);
  2301. elem_lst1.GetNodeCoord(&X_[1], &Xn_[1], nullptr);
  2302. concat_vecs(X, X_);
  2303. concat_vecs(Xn, Xn_);
  2304. }
  2305. { // Set Fs, Fd, Uref
  2306. Vector<Real> X0{0.3,0.6,0.2}, Xn0{0,0,0}, F0{1}, dU;
  2307. kernel_sl.Eval(Uref, X, X0, Xn0, F0);
  2308. kernel_grad.Eval(dU, X, X0, Xn0, F0);
  2309. Fd = Uref;
  2310. { // Set Fs <-- -dot_prod(dU, Xn)
  2311. Fs.ReInit(X.Dim()/COORD_DIM);
  2312. for (Long i = 0; i < Fs.Dim(); i++) {
  2313. Real dU_dot_Xn = 0;
  2314. for (Long k = 0; k < COORD_DIM; k++) {
  2315. dU_dot_Xn += dU[i*COORD_DIM+k] * Xn[i*COORD_DIM+k];
  2316. }
  2317. Fs[i] = -dU_dot_Xn;
  2318. }
  2319. }
  2320. }
  2321. // Warm-up run
  2322. BIOpSL.ComputePotential(Us,Fs);
  2323. BIOpDL.ComputePotential(Ud,Fd);
  2324. BIOpSL.ClearSetup();
  2325. BIOpDL.ClearSetup();
  2326. Us = 0; Ud = 0;
  2327. sctl::Profile::Enable(true);
  2328. Profile::Tic("Setup+Eval", &comm);
  2329. BIOpSL.ComputePotential(Us,Fs);
  2330. BIOpDL.ComputePotential(Ud,Fd);
  2331. Profile::Toc();
  2332. Vector<Real> Uerr = Fd*0.5 + (Us - Ud) - Uref;
  2333. { // Write VTK
  2334. Vector<Vector<Real>> X_(2);
  2335. elem_lst0.GetNodeCoord(&X_[0], nullptr, nullptr);
  2336. elem_lst1.GetNodeCoord(&X_[1], nullptr, nullptr);
  2337. const Long N0 = X_[0].Dim()/COORD_DIM;
  2338. const Long N1 = X_[1].Dim()/COORD_DIM;
  2339. elem_lst0.WriteVTK("Uerr0", Vector<Real>(N0,Uerr.begin()+ 0,false), comm);
  2340. elem_lst1.WriteVTK("Uerr1", Vector<Real>(N1,Uerr.begin()+N0,false), comm);
  2341. }
  2342. { // Print error
  2343. StaticArray<Real,2> max_err{0,0};
  2344. StaticArray<Real,2> max_val{0,0};
  2345. for (auto x : Uerr) max_err[0] = std::max<Real>(max_err[0], fabs(x));
  2346. for (auto x : Uref) max_val[0] = std::max<Real>(max_val[0], fabs(x));
  2347. comm.Allreduce(max_err+0, max_err+1, 1, Comm::CommOp::MAX);
  2348. comm.Allreduce(max_val+0, max_val+1, 1, Comm::CommOp::MAX);
  2349. if (!pid) std::cout<<"Error = "<<max_err[1]/max_val[1]<<'\n';
  2350. }
  2351. sctl::Profile::print(&comm);
  2352. sctl::Profile::Enable(false);
  2353. }
  2354. template <class Real> void SlenderElemList<Real>::GetGeom(Vector<Real>* X, Vector<Real>* Xn, Vector<Real>* Xa, Vector<Real>* dX_ds, Vector<Real>* dX_dt, const Vector<Real>& s_param, const Vector<Real>& sin_theta_, const Vector<Real>& cos_theta_, const Long elem_idx) const {
  2355. SCTL_ASSERT_MSG(elem_idx < Size(), "element index is greater than number of elements in the list!");
  2356. using Vec3 = Tensor<Real,true,COORD_DIM,1>;
  2357. const Integer ChebOrder = cheb_order[elem_idx];
  2358. const Long Nt = sin_theta_.Dim();
  2359. const Long Ns = s_param.Dim();
  2360. const Long N = Ns * Nt;
  2361. if (X && X ->Dim() != N*COORD_DIM) X ->ReInit(N*COORD_DIM);
  2362. if (Xn && Xn ->Dim() != N*COORD_DIM) Xn ->ReInit(N*COORD_DIM);
  2363. if (Xa && Xa ->Dim() != N ) Xa ->ReInit(N);
  2364. if (dX_ds && dX_ds->Dim() != N*COORD_DIM) dX_ds->ReInit(N*COORD_DIM);
  2365. if (dX_dt && dX_dt->Dim() != N*COORD_DIM) dX_dt->ReInit(N*COORD_DIM);
  2366. Matrix<Real> M_lagrange_interp;
  2367. { // Set M_lagrange_interp
  2368. M_lagrange_interp.ReInit(ChebOrder, Ns);
  2369. Vector<Real> V_lagrange_interp(ChebOrder*Ns, M_lagrange_interp.begin(), false);
  2370. LagrangeInterp<Real>::Interpolate(V_lagrange_interp, CenterlineNodes(ChebOrder), s_param);
  2371. }
  2372. Matrix<Real> r_, dr_, x_, dx_, d2x_, e1_;
  2373. r_ .ReInit( 1,Ns);
  2374. x_ .ReInit(COORD_DIM,Ns);
  2375. dx_ .ReInit(COORD_DIM,Ns);
  2376. e1_ .ReInit(COORD_DIM,Ns);
  2377. Matrix<Real>::GEMM( x_, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) coord.begin()+COORD_DIM*elem_dsp[elem_idx],false), M_lagrange_interp);
  2378. Matrix<Real>::GEMM( dx_, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) dx.begin()+COORD_DIM*elem_dsp[elem_idx],false), M_lagrange_interp);
  2379. Matrix<Real>::GEMM( r_, Matrix<Real>( 1,ChebOrder,(Iterator<Real>)radius.begin()+ elem_dsp[elem_idx],false), M_lagrange_interp);
  2380. Matrix<Real>::GEMM( e1_, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) e1.begin()+COORD_DIM*elem_dsp[elem_idx],false), M_lagrange_interp);
  2381. if (Xn || Xa) { // Set dr_, d2x_
  2382. dr_ .ReInit( 1,Ns);
  2383. d2x_.ReInit(COORD_DIM,Ns);
  2384. Matrix<Real>::GEMM(d2x_, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) d2x.begin()+COORD_DIM*elem_dsp[elem_idx],false), M_lagrange_interp);
  2385. Matrix<Real>::GEMM( dr_, Matrix<Real>( 1,ChebOrder,(Iterator<Real>) dr.begin()+ elem_dsp[elem_idx],false), M_lagrange_interp);
  2386. }
  2387. auto compute_coord = [](Vec3& y, const Vec3& x, const Vec3& e1, const Vec3& e2, const Real r, const Real sint, const Real cost) {
  2388. y = x + e1*(r*cost) + e2*(r*sint);
  2389. };
  2390. auto compute_normal_area_elem_tangents = [](Vec3& n, Real& da, Vec3& dy_ds, Vec3& dy_dt, const Vec3& dx, const Vec3& e1, const Vec3& e2, const Vec3& de1, const Vec3& de2, const Real r, const Real dr, const Real sint, const Real cost) {
  2391. dy_ds = dx + e1*(dr*cost) + e2*(dr*sint) + de1*(r*cost) + de2*(r*sint);
  2392. dy_dt = e1*(-r*sint) + e2*(r*cost);
  2393. n = cross_prod(dy_ds, dy_dt);
  2394. da = sqrt<Real>(dot_prod(n,n));
  2395. n = n * (1/da);
  2396. };
  2397. for (Long j = 0; j < Ns; j++) {
  2398. Real r, inv_dx2;
  2399. Vec3 x, dx, e1, e2;
  2400. { // Set x, dx, e1, r, inv_dx2
  2401. for (Integer k = 0; k < COORD_DIM; k++) {
  2402. x(k,0) = x_[k][j];
  2403. dx(k,0) = dx_[k][j];
  2404. e1(k,0) = e1_[k][j];
  2405. }
  2406. inv_dx2 = 1/dot_prod(dx,dx);
  2407. r = r_[0][j];
  2408. e1 = e1 - dx * dot_prod(e1, dx) * inv_dx2;
  2409. e1 = e1 * (1/sqrt<Real>(dot_prod(e1,e1)));
  2410. e2 = cross_prod(e1, dx);
  2411. e2 = e2 * (1/sqrt<Real>(dot_prod(e2,e2)));
  2412. }
  2413. if (X) {
  2414. for (Integer i = 0; i < Nt; i++) { // Set X
  2415. Vec3 y;
  2416. compute_coord(y, x, e1, e2, r, sin_theta_[i], cos_theta_[i]);
  2417. for (Integer k = 0; k < COORD_DIM; k++) {
  2418. (*X)[(j*Nt+i)*COORD_DIM+k] = y(k,0);
  2419. }
  2420. }
  2421. }
  2422. if (Xn || Xa || dX_ds || dX_dt) {
  2423. Vec3 d2x, de1, de2;
  2424. for (Integer k = 0; k < COORD_DIM; k++) {
  2425. d2x(k,0) = d2x_[k][j];
  2426. }
  2427. de1 = dx*(-dot_prod(e1,d2x) * inv_dx2);
  2428. de2 = dx*(-dot_prod(e2,d2x) * inv_dx2);
  2429. Real dr = dr_[0][j];
  2430. for (Integer i = 0; i < Nt; i++) { // Set X, Xn, Xa, dX_ds, dX_dt
  2431. Real da;
  2432. Vec3 n, dx_ds, dx_dt;
  2433. compute_normal_area_elem_tangents(n, da, dx_ds, dx_dt, dx, e1, e2, de1, de2, r, dr, sin_theta_[i], cos_theta_[i]);
  2434. if (Xn) {
  2435. for (Integer k = 0; k < COORD_DIM; k++) {
  2436. (*Xn)[(j*Nt+i)*COORD_DIM+k] = n(k,0);
  2437. }
  2438. }
  2439. if (Xa) {
  2440. (*Xa)[j*Nt+i] = da;
  2441. }
  2442. if (dX_ds) {
  2443. for (Integer k = 0; k < COORD_DIM; k++) {
  2444. (*dX_ds)[(j*Nt+i)*COORD_DIM+k] = dx_ds(k,0);
  2445. }
  2446. }
  2447. if (dX_dt) {
  2448. for (Integer k = 0; k < COORD_DIM; k++) {
  2449. (*dX_dt)[(j*Nt+i)*COORD_DIM+k] = dx_dt(k,0);
  2450. }
  2451. }
  2452. }
  2453. }
  2454. }
  2455. }
  2456. template <class Real> template <Integer digits, bool trg_dot_prod, class Kernel> Matrix<Real> SlenderElemList<Real>::SelfInteracHelper(const Kernel& ker, const Long elem_idx) const {
  2457. using Vec3 = Tensor<Real,true,COORD_DIM,1>;
  2458. static constexpr Integer KDIM0 = Kernel::SrcDim();
  2459. static constexpr Integer KDIM1 = Kernel::TrgDim()/(trg_dot_prod?COORD_DIM:1);
  2460. //const Integer digits = (Integer)(log(tol)/log(0.1)+0.5);
  2461. const Integer ChebOrder = cheb_order[elem_idx];
  2462. const Integer FourierOrder = fourier_order[elem_idx];
  2463. const Integer FourierModes = FourierOrder/2+1;
  2464. const Matrix<Real> M_fourier_inv = fourier_matrix_inv_transpose<Real>(FourierOrder,FourierModes);
  2465. const auto& cheb_nds = SlenderElemList<Real>::CenterlineNodes(ChebOrder);
  2466. const Vector<Real> coord(COORD_DIM*ChebOrder,(Iterator<Real>)this-> coord.begin()+COORD_DIM*elem_dsp[elem_idx],false);
  2467. const Vector<Real> dx(COORD_DIM*ChebOrder,(Iterator<Real>)this-> dx.begin()+COORD_DIM*elem_dsp[elem_idx],false);
  2468. const Vector<Real> d2x(COORD_DIM*ChebOrder,(Iterator<Real>)this-> d2x.begin()+COORD_DIM*elem_dsp[elem_idx],false);
  2469. const Vector<Real> radius( 1*ChebOrder,(Iterator<Real>)this->radius.begin()+ elem_dsp[elem_idx],false);
  2470. const Vector<Real> dr( 1*ChebOrder,(Iterator<Real>)this-> dr.begin()+ elem_dsp[elem_idx],false);
  2471. const Vector<Real> e1(COORD_DIM*ChebOrder,(Iterator<Real>)this-> e1.begin()+COORD_DIM*elem_dsp[elem_idx],false);
  2472. const Real dtheta = 2*const_pi<Real>()/FourierOrder;
  2473. const Complex<Real> exp_dtheta(cos<Real>(dtheta), sin<Real>(dtheta));
  2474. Matrix<Real> M_modal(ChebOrder*FourierOrder, ChebOrder*KDIM0*KDIM1*FourierModes*2);
  2475. #pragma omp parallel for
  2476. for (Long i = 0; i < ChebOrder; i++) {
  2477. Real r_trg = radius[i];
  2478. Real dr_trg = dr[i];
  2479. Vec3 x_trg, dx_trg, d2x_trg, e1_trg, e2_trg;
  2480. { // Set x_trg, dx_trg, d2x_trg, e1_trg
  2481. for (Integer k = 0; k < COORD_DIM; k++) {
  2482. x_trg (k,0) = coord[k*ChebOrder+i];
  2483. e1_trg(k,0) = e1[k*ChebOrder+i];
  2484. dx_trg(k,0) = dx[k*ChebOrder+i];
  2485. d2x_trg(k,0) = d2x[k*ChebOrder+i];
  2486. }
  2487. Real inv_dx2 = 1/dot_prod(dx_trg,dx_trg);
  2488. e1_trg = e1_trg - dx_trg * dot_prod(e1_trg, dx_trg) * inv_dx2;
  2489. e1_trg = e1_trg * (1/sqrt<Real>(dot_prod(e1_trg,e1_trg)));
  2490. e2_trg = cross_prod(e1_trg, dx_trg);
  2491. e2_trg = e2_trg * (1/sqrt<Real>(dot_prod(e2_trg,e2_trg)));
  2492. }
  2493. const Real norm_dx_trg = sqrt<Real>(dot_prod(dx_trg,dx_trg));
  2494. const Real inv_norm_dx_trg = 1/norm_dx_trg;
  2495. Vector<Real> quad_nds, quad_wts; // Quadrature rule in s
  2496. SpecialQuadRule<ModalUpsample,Real,Kernel,trg_dot_prod>(quad_nds, quad_wts, ChebOrder, i, r_trg, sqrt<Real>(dot_prod(dx_trg, dx_trg)), digits);
  2497. const Long Nq = quad_nds.Dim();
  2498. Matrix<Real> Minterp_quad_nds;
  2499. { // Set Minterp_quad_nds
  2500. Minterp_quad_nds.ReInit(ChebOrder, Nq);
  2501. Vector<Real> Vinterp_quad_nds(ChebOrder*Nq, Minterp_quad_nds.begin(), false);
  2502. LagrangeInterp<Real>::Interpolate(Vinterp_quad_nds, cheb_nds-cheb_nds[i], quad_nds);
  2503. }
  2504. Matrix<Real> r_src, dr_src, x_src, dx_src, d2x_src, e1_src;
  2505. r_src .ReInit( 1, Nq);
  2506. dr_src .ReInit( 1, Nq);
  2507. x_src .ReInit(COORD_DIM, Nq);
  2508. dx_src .ReInit(COORD_DIM, Nq);
  2509. d2x_src.ReInit(COORD_DIM, Nq);
  2510. e1_src .ReInit(COORD_DIM, Nq);
  2511. { // Set x_src, x_trg (improve numerical stability)
  2512. Matrix<Real> x_nodes(COORD_DIM,ChebOrder, (Iterator<Real>)coord.begin(), true);
  2513. for (Long j = 0; j < ChebOrder; j++) {
  2514. for (Integer k = 0; k < COORD_DIM; k++) {
  2515. x_nodes[k][j] -= x_trg(k,0);
  2516. }
  2517. }
  2518. Matrix<Real>::GEMM( x_src, x_nodes, Minterp_quad_nds);
  2519. for (Integer k = 0; k < COORD_DIM; k++) {
  2520. x_trg(k,0) = 0;
  2521. }
  2522. }
  2523. //Matrix<Real>::GEMM( x_src, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) coord.begin(),false), Minterp_quad_nds);
  2524. Matrix<Real>::GEMM( dx_src, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) dx.begin(),false), Minterp_quad_nds);
  2525. Matrix<Real>::GEMM(d2x_src, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) d2x.begin(),false), Minterp_quad_nds);
  2526. Matrix<Real>::GEMM( r_src, Matrix<Real>( 1,ChebOrder,(Iterator<Real>)radius.begin(),false), Minterp_quad_nds);
  2527. Matrix<Real>::GEMM( dr_src, Matrix<Real>( 1,ChebOrder,(Iterator<Real>) dr.begin(),false), Minterp_quad_nds);
  2528. Matrix<Real>::GEMM( e1_src, Matrix<Real>(COORD_DIM,ChebOrder,(Iterator<Real>) e1.begin(),false), Minterp_quad_nds);
  2529. for (Long j = 0; j < Nq; j++) { // Set e1_src
  2530. Vec3 e1, dx;
  2531. for (Integer k = 0; k < COORD_DIM; k++) {
  2532. e1(k,0) = e1_src[k][j];
  2533. dx(k,0) = dx_src[k][j];
  2534. }
  2535. e1 = e1 - dx * dot_prod(e1, dx) * (1/dot_prod(dx,dx));
  2536. e1 = e1 * (1/sqrt<Real>(dot_prod(e1,e1)));
  2537. for (Integer k = 0; k < COORD_DIM; k++) {
  2538. e1_src[k][j] = e1(k,0);
  2539. }
  2540. }
  2541. Complex<Real> exp_theta_trg(1,0);
  2542. for (Long j = 0; j < FourierOrder; j++) {
  2543. auto compute_Xn_trg = [&exp_theta_trg,&dx_trg,&d2x_trg,&e1_trg,&e2_trg,&r_trg,&dr_trg,&norm_dx_trg,&inv_norm_dx_trg]() { // Set n_trg
  2544. Real cost = exp_theta_trg.real;
  2545. Real sint = exp_theta_trg.imag;
  2546. Real d2x_dot_e1 = dot_prod(d2x_trg, e1_trg);
  2547. Real d2x_dot_e2 = dot_prod(d2x_trg, e2_trg);
  2548. Vec3 n_trg;
  2549. Real norm_dy = norm_dx_trg - (cost*d2x_dot_e1 + sint*d2x_dot_e2) * (r_trg*inv_norm_dx_trg);
  2550. n_trg(0,0) = e1_trg(0,0)*norm_dy*cost + e2_trg(0,0)*norm_dy*sint - dx_trg(0,0)*(dr_trg*inv_norm_dx_trg);
  2551. n_trg(1,0) = e1_trg(1,0)*norm_dy*cost + e2_trg(1,0)*norm_dy*sint - dx_trg(1,0)*(dr_trg*inv_norm_dx_trg);
  2552. n_trg(2,0) = e1_trg(2,0)*norm_dy*cost + e2_trg(2,0)*norm_dy*sint - dx_trg(2,0)*(dr_trg*inv_norm_dx_trg);
  2553. Real scale = 1/sqrt<Real>(dot_prod(n_trg,n_trg));
  2554. return n_trg*scale;
  2555. };
  2556. //const Vec3 y_trg = x_trg + e1_trg*r_trg*exp_theta_trg.real + e2_trg*r_trg*exp_theta_trg.imag;
  2557. const Vec3 e_trg = e1_trg*exp_theta_trg.real + e2_trg*exp_theta_trg.imag;
  2558. const Vec3 n_trg(trg_dot_prod ? compute_Xn_trg() : Vec3((Real)0));
  2559. Matrix<Real> M_tor(Nq, KDIM0*KDIM1*FourierModes*2); // TODO: pre-allocate
  2560. toroidal_greens_fn_batched<digits+2,ModalUpsample,trg_dot_prod>(M_tor, x_trg, e_trg, r_trg, n_trg, x_src, dx_src, d2x_src, r_src, dr_src, e1_src, ker, FourierModes);
  2561. for (Long ii = 0; ii < Nq; ii++) {
  2562. for (Long jj = 0; jj < KDIM0*KDIM1*FourierModes*2; jj++) {
  2563. M_tor[ii][jj] *= quad_wts[ii];
  2564. }
  2565. }
  2566. Matrix<Real> M_modal_(ChebOrder, KDIM0*KDIM1*FourierModes*2, M_modal[i*FourierOrder+j], false);
  2567. Matrix<Real>::GEMM(M_modal_, Minterp_quad_nds, M_tor);
  2568. exp_theta_trg *= exp_dtheta;
  2569. }
  2570. }
  2571. Matrix<Real> M_nodal(ChebOrder*FourierOrder, ChebOrder*KDIM0*KDIM1*FourierOrder);
  2572. { // Set M_nodal
  2573. const Matrix<Real> M_modal_(ChebOrder*FourierOrder * ChebOrder*KDIM0*KDIM1, FourierModes*2, M_modal.begin(), false);
  2574. Matrix<Real> M_nodal_(ChebOrder*FourierOrder * ChebOrder*KDIM0*KDIM1, FourierOrder, M_nodal.begin(), false);
  2575. Matrix<Real>::GEMM(M_nodal_, M_modal_, M_fourier_inv);
  2576. }
  2577. Matrix<Real> M(ChebOrder*FourierOrder*KDIM0, ChebOrder*FourierOrder*KDIM1);
  2578. { // Set M
  2579. const Integer Nnds = ChebOrder*FourierOrder;
  2580. for (Integer i = 0; i < Nnds; i++) {
  2581. for (Integer j0 = 0; j0 < ChebOrder; j0++) {
  2582. for (Integer k0 = 0; k0 < KDIM0; k0++) {
  2583. for (Integer k1 = 0; k1 < KDIM1; k1++) {
  2584. for (Integer j1 = 0; j1 < FourierOrder; j1++) {
  2585. M[(j0*FourierOrder+j1)*KDIM0+k0][i*KDIM1+k1] = M_nodal[i][((j0*KDIM0+k0)*KDIM1+k1)*FourierOrder+j1] * ker.template uKerScaleFactor<Real>();
  2586. }
  2587. }
  2588. }
  2589. }
  2590. }
  2591. }
  2592. return M;
  2593. }
  2594. template <class Real> template <class ValueType> void SlenderElemList<Real>::Copy(SlenderElemList<ValueType>& elem_lst) const {
  2595. const Long N = radius.Dim();
  2596. Vector<ValueType> radius_(N), coord_(N*COORD_DIM), e1_(N*COORD_DIM);
  2597. for (Long i = 0; i < cheb_order.Dim(); i++) {
  2598. for (Long j = 0; j < cheb_order[i]; j++) {
  2599. radius_[elem_dsp[i]+j] = (ValueType)radius[elem_dsp[i]+j];
  2600. for (Integer k = 0; k < COORD_DIM; k++) {
  2601. Long idx_ = elem_dsp[i]*COORD_DIM+j*COORD_DIM+k;
  2602. Long idx = elem_dsp[i]*COORD_DIM+k*cheb_order[i]+j;
  2603. coord_[idx_] = (ValueType)coord[idx];
  2604. e1_[idx_] = (ValueType)e1[idx];
  2605. }
  2606. }
  2607. }
  2608. elem_lst.Init(cheb_order, fourier_order, coord_, radius_, e1_);
  2609. }
  2610. }