7 #ifndef NIHU_HELMHOLTZ_2D_WB_FMM_HPP_INCLUDED
8 #define NIHU_HELMHOLTZ_2D_WB_FMM_HPP_INCLUDED
20 #include "library/helmholtz_2d.hpp"
22 #include <boost/math/special_functions/hankel.hpp>
23 #include <boost/math/special_functions/bessel.hpp>
24 #include <boost/math/special_functions/bessel_prime.hpp>
25 #include <boost/math/constants/constants.hpp>
27 #include <Eigen/Dense>
41 template <
class WaveNumber>
52 typedef Eigen::Matrix<std::complex<double>, Eigen::Dynamic, 1>
cvector_t;
68 static void cart2pol(
location_t const& d,
double& r,
double& theta)
71 theta = std::atan2(d(1), d(0));
91 m2m(wave_number_t
const& wave_number)
115 using boost::math::cyl_bessel_j;
116 using namespace std::complex_literals;
121 cart2pol(X -
Y, r, theta);
122 auto z = this->get_wave_number() * r;
125 int L = std::max(Lto, Lfrom);
127 for (
int nu = -L; nu <= L; ++nu)
128 diag_coeffs(-nu + L) = std::exp(-1.0i * (nu * theta))
129 * cyl_bessel_j(nu, z);
153 l2l(wave_number_t
const& wave_number)
175 using boost::math::cyl_bessel_j;
176 using namespace boost::math::double_constants;
177 using namespace std::complex_literals;
182 cart2pol(X -
Y, r, theta);
183 auto z = this->get_wave_number() * r;
186 int L = std::max(Lto, Lfrom);
188 for (
int nu = -L; nu <= L; ++nu)
189 diag_coeffs(nu + L) = std::exp(1.0i * (nu * (pi + theta)))
190 * cyl_bessel_j(nu, z);
213 m2l(wave_number_t
const& wave_number)
233 using boost::math::cyl_hankel_2;
234 using namespace boost::math::double_constants;
235 using namespace std::complex_literals;
241 cart2pol(X -
Y, r, theta);
242 auto z = this->get_wave_number() * r;
244 for (
int nu = -
int(L); nu <= int(L); ++nu)
245 diag_coeffs(nu + L) = std::exp(1.0i * (nu * (pi + theta)))
246 * cyl_hankel_2(nu, z);
255 template <
unsigned int Ny>
273 p2m(wave_number_t
const& wave_number)
292 result_t res = eval(to, y, std::integral_constant<int, Ny>());
299 result_t eval(test_input_t
const& to, trial_input_t
const& tri,
300 std::integral_constant<int, 0>)
const
302 using boost::math::cyl_bessel_j;
303 using namespace std::complex_literals;
304 using namespace std::complex_literals;
306 int L = int(to.get_level_data().get_expansion_length());
307 location_t const&
Y = to.get_bounding_box().get_center();
310 cart2pol(
Y - y, r, theta);
312 auto z = this->get_wave_number() * r;
313 for (
int nu = -L; nu <= L; ++nu)
314 res(-nu + L, 0) = std::exp(-1.0i * (nu * theta)) *
319 result_t eval(test_input_t
const& to, trial_input_t
const& tri,
320 std::integral_constant<int, 1>)
const
322 using boost::math::cyl_bessel_j;
323 using boost::math::cyl_bessel_j_prime;
324 using namespace std::complex_literals;
326 int L = int(to.get_level_data().get_expansion_length());
327 location_t
const&
Y = to.get_bounding_box().get_center();
328 location_t
const& y = tri.get_x();
329 location_t d =
Y - y;
331 cart2pol(d, r, theta);
332 double rdny = -d.dot(tri.get_unit_normal()) / r;
333 location_t Td(d(1), -d(0));
334 double thetadny = Td.dot(tri.get_unit_normal()) / (r * r);
336 auto const& k = this->get_wave_number();
338 for (
int nu = -L; nu <= L; ++nu)
339 res(-nu + L, 0) = std::exp(-1.0i * (nu * theta)) *
341 cyl_bessel_j_prime(nu, z) * k * rdny
342 - 1.0i * (nu * thetadny) * cyl_bessel_j(nu, z)
351 template <
unsigned int Ny>
363 >::trial_input_t trial_input_t;
368 p2l(wave_number_t
const& wave_number)
387 result_t res = eval(to, y, std::integral_constant<int, Ny>());
394 result_t eval(test_input_t
const& to, trial_input_t
const& tri,
395 std::integral_constant<int, 0>)
const
397 using boost::math::cyl_hankel_2;
398 using namespace boost::math::double_constants;
399 using namespace std::complex_literals;
401 int L = int(to.get_level_data().get_expansion_length());
402 location_t const& X = to.get_bounding_box().get_center();
405 cart2pol(X - y, r, theta);
407 auto z = this->get_wave_number() * r;
408 for (
int nu = -L; nu <= L; ++nu)
409 res(nu + L, 0) = std::exp(1.0i * (nu * (pi + theta))) *
414 result_t eval(test_input_t
const& to, trial_input_t
const& tri,
415 std::integral_constant<int, 1>)
const
417 using boost::math::cyl_bessel_j_prime;
418 using boost::math::cyl_neumann_prime;
419 using boost::math::cyl_hankel_2;
420 using namespace boost::math::double_constants;
421 using namespace std::complex_literals;
423 int L = int(to.get_level_data().get_expansion_length());
424 location_t
const& X = to.get_bounding_box().get_center();
425 location_t
const& y = tri.get_x();
426 location_t d = X - y;
428 cart2pol(X - y, r, theta);
429 double rdny = -d.dot(tri.get_unit_normal()) / r;
430 location_t Td(d(1), -d(0));
431 double thetadny = Td.dot(tri.get_unit_normal()) / (r * r);
433 auto const& k = this->get_wave_number();
435 for (
int nu = -L; nu <= L; ++nu)
437 std::exp(1.0i * (nu * (pi + theta))) *
439 (cyl_bessel_j_prime(nu, z) - 1.0i * cyl_neumann_prime(nu, z)) * k * rdny +
440 cyl_hankel_2(nu, z) * 1.0i * (nu * thetadny)
449 template <
unsigned int Nx>
462 >::test_input_t test_input_t;
463 typedef Eigen::Matrix<std::complex<double>, 1, Eigen::Dynamic>
result_t;
467 l2p(wave_number_t
const& wave_number)
486 result_t res = eval(x, from, std::integral_constant<int, Nx>());
488 return from.
get_level_data().
dft(res.conjugate().transpose()).conjugate().transpose() / double(cols(from));
493 result_t eval(test_input_t
const& tsi, trial_input_t
const& from,
494 std::integral_constant<int, 0>)
const
496 using boost::math::cyl_bessel_j;
497 using namespace std::complex_literals;
499 int L = int(from.get_level_data().get_expansion_length());
500 location_t const& X = from.get_bounding_box().get_center();
503 cart2pol(x - X, r, theta);
504 auto z = this->get_wave_number() * r;
506 for (
int nu = -L; nu <= L; ++nu)
507 res(0, nu + L) = std::exp(-1.0i * (nu * theta)) *
512 result_t eval(test_input_t
const& tsi, trial_input_t
const& from,
513 std::integral_constant<int, 1>)
const
515 using boost::math::cyl_bessel_j;
516 using boost::math::cyl_bessel_j_prime;
517 using namespace std::complex_literals;
519 location_t
const& X = from.get_bounding_box().get_center();
520 location_t
const& x = tsi.get_x();
521 location_t d = x - X;
523 cart2pol(d, r, theta);
524 double rdnx = d.dot(tsi.get_unit_normal()) / r;
525 location_t Td(d(1), -d(0));
526 double thetadnx = -Td.dot(tsi.get_unit_normal()) / (r * r);
528 int L = int(from.get_level_data().get_expansion_length());
531 auto const& k = this->get_wave_number();
533 for (
int nu = -L; nu <= L; ++nu)
534 res(0, nu + L) = std::exp(-1.0i * (nu * theta)) *
536 cyl_bessel_j_prime(nu, z) * k * rdnx
537 - cyl_bessel_j(nu, z) * 1.0i * (nu * thetadnx)
546 template <
unsigned int Nx>
558 >::test_input_t test_input_t;
559 typedef Eigen::Matrix<std::complex<double>, 1, Eigen::Dynamic>
result_t;
563 m2p(wave_number_t
const& wave_number)
582 result_t res = eval(x, from, std::integral_constant<int, Nx>());
584 return from.
get_level_data().
dft(res.conjugate().transpose()).conjugate().transpose() / double(cols(from));
589 result_t eval(test_input_t
const& tsi, trial_input_t
const& from,
590 std::integral_constant<int, 0>)
const
592 using boost::math::cyl_hankel_2;
593 using namespace boost::math::double_constants;
594 using namespace std::complex_literals;
596 int L = int(from.get_level_data().get_expansion_length());
597 location_t const&
Y = from.get_bounding_box().get_center();
601 cart2pol(d, r, theta);
602 auto z = this->get_wave_number() * r;
604 for (
int nu = -L; nu <= L; ++nu)
605 res(0, -nu + L) = std::exp(1.0i * (nu * (pi + theta))) *
610 result_t eval(test_input_t
const& tsi, trial_input_t
const& from,
611 std::integral_constant<int, 1>)
const
613 using boost::math::cyl_bessel_j_prime;
614 using boost::math::cyl_neumann_prime;
615 using boost::math::cyl_hankel_2;
616 using namespace boost::math::double_constants;
617 using namespace std::complex_literals;
619 location_t
const&
Y = from.get_bounding_box().get_center();
620 location_t
const& x = tsi.get_x();
621 location_t d = x -
Y;
623 cart2pol(d, r, theta);
624 double rdnx = d.dot(tsi.get_unit_normal()) / r;
625 location_t Td(d(1), -d(0));
626 double thetadnx = -Td.dot(tsi.get_unit_normal()) / (r * r);
628 int L = int(from.get_level_data().get_expansion_length());
631 auto const& k = this->get_wave_number();
633 for (
int nu = -L; nu <= L; ++nu)
634 res(0, -nu + L) = std::exp(1.0i * (nu * (pi + theta))) *
636 (cyl_bessel_j_prime(nu, z) - 1.0i * cyl_neumann_prime(nu, z)) * k * rdnx
637 + cyl_hankel_2(nu, z) * (1.0i * (nu * thetadnx)));
646 : m_wave_number(wave_number)
650 template <
int Nx,
int Ny>
682 template <
int Nx,
int Ny>
694 p2m<Ny> create_p2m()
const
696 return p2m<Ny>(this->get_wave_number());
700 p2l<Ny> create_p2l()
const
702 return p2l<Ny>(this->get_wave_number());
706 m2p<Nx> create_m2p()
const
708 return m2p<Nx>(this->get_wave_number());
712 l2p<Nx> create_l2p()
const
714 return l2p<Nx>(this->get_wave_number());
717 m2m create_m2m()
const
719 return m2m(this->get_wave_number());
722 l2l create_l2l()
const
724 return l2l(this->get_wave_number());
727 m2l create_m2l()
const
729 return m2l(this->get_wave_number());
732 wave_number_t
const& get_wave_number()
const
734 return m_wave_number;
746 using namespace boost::math::double_constants;
748 double lambda = two_pi / std::real(m_wave_number);
749 m_level_data_vector.clear();
757 double D = tree[idx].get_bounding_box().get_diameter();
758 m_level_data_vector[i].init(D / lambda);
764 if (tree[idx].is_leaf() && m_level_data_vector[tree[idx].get_level()].is_high_freq())
765 throw std::runtime_error(
"Leaf level cluster (" + std::to_string(idx) +
") has been found in the high frequency domain.");
770 size_t L_from = m_level_data_vector[i + 1].get_expansion_length();
771 m_level_data_vector[i].set_interp_up(L_from);
777 size_t L_from = m_level_data_vector[i - 1].get_expansion_length();
778 m_level_data_vector[i].set_interp_dn(L_from);
787 return m_level_data_vector[idx];
795 return m_level_data_vector[idx];
802 for (
size_t i = 2; i < m_level_data_vector.size(); ++i)
804 auto const& ld = m_level_data_vector[i];
805 os <<
"level: " << i <<
" " << ld.get_expansion_length() <<
' ' << ld.is_high_freq() << std::endl;
810 wave_number_t m_wave_number;
811 std::vector<helmholtz_2d_wb_level_data> m_level_data_vector;