28 #ifndef OOMPH_HELMHOLTZ_BC_ELEMENTS_HEADER 
   29 #define OOMPH_HELMHOLTZ_BC_ELEMENTS_HEADER 
   33 #include <oomph-lib-config.h> 
   40 #include "oomph_crbond_bessel.h" 
   55   namespace Hankel_functions_for_helmholtz_problem
 
   64                       Vector<std::complex<double>>& h,
 
   65                       Vector<std::complex<double>>& hp)
 
   69       CRBond_Bessel::bessjyna(
 
   70         int(n), x, n_actual, &jn[0], &yn[0], &jnp[0], &ynp[0]);
 
   72       if (n_actual != 
int(n))
 
   74         std::ostringstream error_stream;
 
   75         error_stream << 
"CRBond_Bessel::bessjyna() only computed " << n_actual
 
   76                      << 
" rather than " << n << 
" Bessel functions.\n";
 
   78           error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
 
   81       for (
unsigned i = 0; 
i < n; 
i++)
 
   83         h[
i] = std::complex<double>(jn[
i], yn[
i]);
 
   84         hp[
i] = std::complex<double>(jnp[
i], ynp[
i]);
 
   96                        const std::complex<double>& x,
 
   97                        Vector<std::complex<double>>& h,
 
   98                        Vector<std::complex<double>>& hp)
 
  116       CRBond_Bessel::cbessjyna(
 
  117         int(n), x, n_actual, &jn[0], &yn[0], &jnp[0], &ynp[0]);
 
  121       if (n_actual != 
int(n))
 
  124         std::ostringstream error_message_stream;
 
  127         error_message_stream << 
"CRBond_Bessel::cbessjyna() only computed " 
  128                              << n_actual << 
" rather than " << n
 
  129                              << 
" Bessel functions.\n";
 
  133                             OOMPH_CURRENT_FUNCTION,
 
  134                             OOMPH_EXCEPTION_LOCATION);
 
  140       for (
unsigned i = 0; 
i < n; 
i++)
 
  143         h[
i] = std::complex<double>(jn[
i].real() - yn[
i].imag(),
 
  144                                     jn[
i].imag() + yn[
i].real());
 
  147         hp[
i] = std::complex<double>(jnp[
i].real() - ynp[
i].imag(),
 
  148                                      jnp[
i].imag() + ynp[
i].real());
 
  165   template<
class ELEMENT>
 
  179         "Don't call empty constructor for HelmholtzBCElementBase",
 
  180         OOMPH_CURRENT_FUNCTION,
 
  181         OOMPH_EXCEPTION_LOCATION);
 
  203                       const unsigned& 
i)
 const 
  218     void output(std::ostream& outfile, 
const unsigned& n_plot)
 
  233     void output(FILE* file_pt, 
const unsigned& n_plot)
 
  251       std::ofstream outfile;
 
  262       ELEMENT* bulk_elem_pt = 
dynamic_cast<ELEMENT*
>(this->
bulk_element_pt());
 
  265       unsigned nnode_bulk = bulk_elem_pt->nnode();
 
  266       const unsigned n_node_local = 
nnode();
 
  269       const unsigned bulk_dim = bulk_elem_pt->dim();
 
  270       const unsigned local_dim = this->
dim();
 
  273       Shape psi(n_node_local);
 
  276       Shape psi_bulk(nnode_bulk);
 
  277       DShape dpsi_bulk_dx(nnode_bulk, bulk_dim);
 
  290       if (outfile.is_open())
 
  297       for (
unsigned ipt = 0; ipt < n_intpt; ipt++)
 
  300         for (
unsigned i = 0; 
i < local_dim; 
i++)
 
  322         (void)bulk_elem_pt->dshape_eulerian(s_bulk, psi_bulk, dpsi_bulk_dx);
 
  326         std::complex<double> dphi_dn(0.0, 0.0);
 
  328         std::complex<double> interpolated_phi(0.0, 0.0);
 
  334         for (
unsigned l = 0; l < nnode_bulk; l++)
 
  337           const std::complex<double> phi_value(
 
  338             bulk_elem_pt->nodal_value(l,
 
  339                                       bulk_elem_pt->u_index_helmholtz().real()),
 
  340             bulk_elem_pt->nodal_value(
 
  341               l, bulk_elem_pt->u_index_helmholtz().imag()));
 
  344           for (
unsigned i = 0; 
i < bulk_dim; 
i++)
 
  346             interpolated_dphidx[
i] += phi_value * dpsi_bulk_dx(l, 
i);
 
  350         for (
unsigned l = 0; l < n_node_local; l++)
 
  353           const std::complex<double> phi_value(
 
  357           interpolated_phi += phi_value * psi(l);
 
  361         for (
unsigned i = 0; 
i < bulk_dim; 
i++)
 
  363           dphi_dn += interpolated_dphidx[
i] * unit_normal[
i];
 
  367         double integrand = 0.5 * (interpolated_phi.real() * dphi_dn.imag() -
 
  368                                   interpolated_phi.imag() * dphi_dn.real());
 
  371         if (outfile.is_open())
 
  374           double phi = atan2(x[1], x[0]);
 
  375           outfile << x[0] << 
" " << x[1] << 
" " << phi << 
" " << integrand
 
  380         power += integrand * 
W;
 
  390       Vector<std::complex<double>>& a_coeff_pos,
 
  391       Vector<std::complex<double>>& a_coeff_neg)
 
  394       if (a_coeff_pos.size() != a_coeff_neg.size())
 
  396         std::ostringstream error_stream;
 
  397         error_stream << 
"a_coeff_pos and a_coeff_neg must have " 
  398                      << 
"the same size. \n";
 
  400           error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
 
  405       const std::complex<double> 
I(0.0, 1.0);
 
  408       const unsigned n_node = this->
nnode();
 
  421       unsigned n = a_coeff_pos.size();
 
  422       for (
unsigned i = 0; 
i < n; 
i++)
 
  424         a_coeff_pos[
i] = std::complex<double>(0.0, 0.0);
 
  425         a_coeff_neg[
i] = std::complex<double>(0.0, 0.0);
 
  430       for (
unsigned ipt = 0; ipt < n_intpt; ipt++)
 
  433         for (
unsigned i = 0; 
i < (this->
Dim - 1); 
i++)
 
  449         std::complex<double> interpolated_u(0.0, 0.0);
 
  452         for (
unsigned l = 0; l < n_node; l++)
 
  455           for (
unsigned i = 0; 
i < this->
Dim; 
i++)
 
  462           const std::complex<double> u_value(
 
  464             this->nodal_value(l, this->U_index_helmholtz.imag()));
 
  467           interpolated_u += u_value * psi(l);
 
  481         double dphi_ds = std::fabs(nom / denom);
 
  484         for (
unsigned i = 0; 
i < n; 
i++)
 
  487             interpolated_u * exp(-
I * phi * 
double(
i)) * dphi_ds * w;
 
  490         for (
unsigned i = 1; 
i < n; 
i++)
 
  493             interpolated_u * exp(
I * phi * 
double(
i)) * dphi_ds * w;
 
  524       unsigned n_node = 
nnode();
 
  530       for (
unsigned i = 0; 
i < n_node; 
i++)
 
  532         for (
unsigned j = 0; j < (
Dim - 1); j++)
 
  535           dtest_ds(
i, j) = dpsi_ds(
i, j);
 
  560   template<
class ELEMENT>
 
  578                                     Vector<std::complex<double>>& a_coeff_neg);
 
  623     std::map<FiniteElement*, Vector<std::map<unsigned, std::complex<double>>>>
 
  637   template<
class ELEMENT>
 
  677         residuals, jacobian, 1);
 
  694       const unsigned& flag)
 
  701                             OOMPH_CURRENT_FUNCTION,
 
  702                             OOMPH_EXCEPTION_LOCATION);
 
  707         throw OomphLibError(
"Pointer to outer radius hasn't been set!",
 
  708                             OOMPH_CURRENT_FUNCTION,
 
  709                             OOMPH_EXCEPTION_LOCATION);
 
  715       const unsigned n_node = this->
nnode();
 
  718       Shape psi(n_node), test(n_node);
 
  719       DShape dpsi_ds(n_node, this->
Dim - 1), dtest_ds(n_node, this->
Dim - 1),
 
  720         dtest_dS(n_node, this->
Dim - 1), dpsi_dS(n_node, this->
Dim - 1);
 
  729       int local_eqn_real = 0, local_unknown_real = 0;
 
  730       int local_eqn_imag = 0, local_unknown_imag = 0;
 
  739       for (
unsigned ipt = 0; ipt < n_intpt; ipt++)
 
  742         for (
unsigned i = 0; 
i < (this->
Dim - 1); 
i++)
 
  759         double inv_J = 1 / J;
 
  763         std::complex<double> interpolated_u(0.0, 0.0);
 
  764         std::complex<double> du_dS(0.0, 0.0);
 
  767         for (
unsigned l = 0; l < n_node; l++)
 
  771           const std::complex<double> u_value(
 
  773             this->nodal_value(l, this->U_index_helmholtz.imag()));
 
  775           interpolated_u += u_value * psi[l];
 
  777           du_dS += u_value * dpsi_ds(l, 0) * inv_J;
 
  780           dtest_dS(l, 0) = dtest_ds(l, 0) * inv_J;
 
  782           dpsi_dS(l, 0) = dpsi_ds(l, 0) * inv_J;
 
  790           for (
unsigned l = 0; l < n_node; l++)
 
  800             if (local_eqn_real >= 0)
 
  804               residuals[local_eqn_real] += (k * interpolated_u.imag() +
 
  805                                             (0.5 / 
R) * interpolated_u.real()) *
 
  813                 for (
unsigned l2 = 0; l2 < n_node; l2++)
 
  820                   if (local_unknown_real >= 0)
 
  822                     jacobian(local_eqn_real, local_unknown_real) +=
 
  823                       (0.5 / 
R) * psi[l2] * test[l] * 
W;
 
  827                   if (local_unknown_imag >= 0)
 
  829                     jacobian(local_eqn_real, local_unknown_imag) +=
 
  830                       k * psi[l2] * test[l] * 
W;
 
  839             if (local_eqn_imag >= 0)
 
  842               residuals[local_eqn_imag] += (-k * interpolated_u.real() +
 
  843                                             (0.5 / 
R) * interpolated_u.imag()) *
 
  852                 for (
unsigned l2 = 0; l2 < n_node; l2++)
 
  859                   if (local_unknown_real >= 0)
 
  861                     jacobian(local_eqn_imag, local_unknown_real) +=
 
  862                       (-k) * psi[l2] * test[l] * 
W;
 
  866                   if (local_unknown_imag >= 0)
 
  868                     jacobian(local_eqn_imag, local_unknown_imag) +=
 
  869                       (0.5 / 
R) * psi[l2] * test[l] * 
W;
 
  882           for (
unsigned l = 0; l < n_node; l++)
 
  892             if (local_eqn_real >= 0)
 
  896               residuals[local_eqn_real] +=
 
  897                 (k * interpolated_u.imag() +
 
  898                  (0.5 / 
R) * interpolated_u.real()) *
 
  900                 ((0.125 / (k * 
R * 
R)) * interpolated_u.imag()) * test[l] * 
W;
 
  902               residuals[local_eqn_real] +=
 
  903                 (-0.5 / k) * du_dS.imag() * dtest_dS(l, 0) * 
W;
 
  910                 for (
unsigned l2 = 0; l2 < n_node; l2++)
 
  917                   if (local_unknown_real >= 0)
 
  919                     jacobian(local_eqn_real, local_unknown_real) +=
 
  920                       (0.5 / 
R) * psi[l2] * test[l] * 
W;
 
  924                   if (local_unknown_imag >= 0)
 
  926                     jacobian(local_eqn_real, local_unknown_imag) +=
 
  927                       k * psi[l2] * test[l] * 
W +
 
  928                       (0.125 / (k * 
R * 
R)) * psi[l2] * test[l] * 
W;
 
  930                     jacobian(local_eqn_real, local_unknown_imag) +=
 
  931                       (-0.5 / k) * dpsi_dS(l2, 0) * dtest_dS(l, 0) * 
W;
 
  940             if (local_eqn_imag >= 0)
 
  943               residuals[local_eqn_imag] +=
 
  944                 (-k * interpolated_u.real() +
 
  945                  (0.5 / 
R) * interpolated_u.imag()) *
 
  947                 ((-0.125 / (k * 
R * 
R)) * interpolated_u.real()) * test[l] * 
W;
 
  949               residuals[local_eqn_imag] +=
 
  950                 (0.5 / k) * du_dS.real() * dtest_dS(l, 0) * 
W;
 
  957                 for (
unsigned l2 = 0; l2 < n_node; l2++)
 
  964                   if (local_unknown_real >= 0)
 
  966                     jacobian(local_eqn_imag, local_unknown_real) +=
 
  967                       (-k) * psi[l2] * test[l] * 
W -
 
  968                       (0.125 / (k * 
R * 
R)) * psi[l2] * test[l] * 
W;
 
  970                     jacobian(local_eqn_imag, local_unknown_real) +=
 
  971                       (0.5 / k) * dpsi_dS(l2, 0) * dtest_dS(l, 0) * 
W;
 
  975                   if (local_unknown_imag >= 0)
 
  977                     jacobian(local_eqn_imag, local_unknown_imag) +=
 
  978                       (0.5 / 
R) * psi[l2] * test[l] * 
W;
 
  990           for (
unsigned l = 0; l < n_node; l++)
 
 1000             if (local_eqn_real >= 0)
 
 1003               residuals[local_eqn_real] +=
 
 1004                 ((k * (1 + 0.125 / (k * k * 
R * 
R))) * interpolated_u.imag() +
 
 1005                  (0.5 / 
R - 0.125 / (k * k * 
R * 
R * 
R)) *
 
 1006                    interpolated_u.real()) *
 
 1009               residuals[local_eqn_real] +=
 
 1010                 ((-0.5 / k) * du_dS.imag() +
 
 1011                  (0.5 / (k * k * 
R)) * du_dS.real()) *
 
 1019                 for (
unsigned l2 = 0; l2 < n_node; l2++)
 
 1021                   local_unknown_real =
 
 1023                   local_unknown_imag =
 
 1026                   if (local_unknown_real >= 0)
 
 1028                     jacobian(local_eqn_real, local_unknown_real) +=
 
 1029                       (0.5 / 
R - 0.125 / (k * k * 
R * 
R * 
R)) * psi[l2] *
 
 1032                     jacobian(local_eqn_real, local_unknown_real) +=
 
 1033                       (0.5 / (k * k * 
R)) * dpsi_dS(l2, 0) * dtest_dS(l, 0) * 
W;
 
 1037                   if (local_unknown_imag >= 0)
 
 1039                     jacobian(local_eqn_real, local_unknown_imag) +=
 
 1040                       (k * (1 + 0.125 / (k * k * 
R * 
R))) * psi[l2] * test[l] *
 
 1043                     jacobian(local_eqn_real, local_unknown_imag) +=
 
 1044                       (-0.5 / k) * dpsi_dS(l2, 0) * dtest_dS(l, 0) * 
W;
 
 1053             if (local_eqn_imag >= 0)
 
 1056               residuals[local_eqn_imag] +=
 
 1057                 ((-k * (1 + 0.125 / (k * k * 
R * 
R))) * interpolated_u.real() +
 
 1058                  (0.5 / 
R - 0.125 / (k * k * 
R * 
R * 
R)) *
 
 1059                    interpolated_u.imag()) *
 
 1062               residuals[local_eqn_imag] +=
 
 1063                 ((0.5 / k) * du_dS.real() +
 
 1064                  (0.5 / (k * k * 
R)) * du_dS.imag()) *
 
 1072                 for (
unsigned l2 = 0; l2 < n_node; l2++)
 
 1074                   local_unknown_real =
 
 1076                   local_unknown_imag =
 
 1079                   if (local_unknown_real >= 0)
 
 1081                     jacobian(local_eqn_imag, local_unknown_real) +=
 
 1082                       (-k * (1 + 0.125 / (k * k * 
R * 
R))) * psi[l2] * test[l] *
 
 1085                     jacobian(local_eqn_imag, local_unknown_real) +=
 
 1086                       (0.5 / k) * dpsi_dS(l2, 0) * dtest_dS(l, 0) * 
W;
 
 1089                   if (local_unknown_imag >= 0)
 
 1091                     jacobian(local_eqn_imag, local_unknown_imag) +=
 
 1092                       (0.5 / 
R - 0.125 / (k * k * 
R * 
R * 
R)) * psi[l2] *
 
 1095                     jacobian(local_eqn_imag, local_unknown_imag) +=
 
 1096                       (0.5 / (k * k * 
R)) * dpsi_dS(l2, 0) * dtest_dS(l, 0) * 
W;
 
 1119   template<
class ELEMENT>
 
 1147         residuals, jacobian, 1);
 
 1157       std::complex<double>& gamma_con,
 
 1158       std::map<
unsigned, std::complex<double>>& d_gamma_con);
 
 1181       std::set<Node*> node_set;
 
 1183       for (
unsigned e = 0; 
e < nel; 
e++)
 
 1186         unsigned nnod = el_pt->
nnode();
 
 1187         for (
unsigned j = 0; j < nnod; j++)
 
 1194             node_set.insert(nod_pt);
 
 1199       unsigned nnod = this->
nnode();
 
 1200       for (
unsigned j = 0; j < nnod; j++)
 
 1203         node_set.erase(nod_pt);
 
 1215       for (std::set<Node*>::iterator it = node_set.begin();
 
 1216            it != node_set.end();
 
 1231       const unsigned& flag)
 
 1234       const unsigned n_node = this->
nnode();
 
 1246       int local_eqn_real = 0, local_unknown_real = 0, global_eqn_real = 0,
 
 1247           local_eqn_imag = 0, local_unknown_imag = 0, global_eqn_imag = 0;
 
 1248       int external_global_eqn_real = 0, external_unknown_real = 0,
 
 1249           external_global_eqn_imag = 0, external_unknown_imag = 0;
 
 1262       for (
unsigned ipt = 0; ipt < n_intpt; ipt++)
 
 1265         for (
unsigned i = 0; 
i < (this->
Dim - 1); 
i++)
 
 1283         for (
unsigned l = 0; l < n_node; l++)
 
 1291           if (local_eqn_real >= 0)
 
 1294             residuals[local_eqn_real] -= gamma[ipt].real() * test[l] * 
W;
 
 1301               for (
unsigned l2 = 0; l2 < n_node; l2++)
 
 1304                 local_unknown_real =
 
 1307                 local_unknown_imag =
 
 1311                 if (local_unknown_real >= 0)
 
 1313                   global_eqn_real = this->
eqn_number(local_unknown_real);
 
 1316                   jacobian(local_eqn_real, local_unknown_real) -=
 
 1317                     d_gamma[ipt][global_eqn_real].real() * test[l] * 
W;
 
 1319                 if (local_unknown_imag >= 0)
 
 1321                   global_eqn_imag = this->
eqn_number(local_unknown_imag);
 
 1324                   jacobian(local_eqn_real, local_unknown_imag) -=
 
 1325                     d_gamma[ipt][global_eqn_imag].real() * test[l] * 
W;
 
 1332               for (
unsigned l2 = 0; l2 < n_ext_data; l2++)
 
 1335                 external_unknown_real =
 
 1338                 external_unknown_imag =
 
 1342                 if (external_unknown_real >= 0)
 
 1344                   external_global_eqn_real =
 
 1348                   jacobian(local_eqn_real, external_unknown_real) -=
 
 1349                     d_gamma[ipt][external_global_eqn_real].real() * test[l] * 
W;
 
 1351                 if (external_unknown_imag >= 0)
 
 1353                   external_global_eqn_imag =
 
 1357                   jacobian(local_eqn_real, external_unknown_imag) -=
 
 1358                     d_gamma[ipt][external_global_eqn_imag].real() * test[l] * 
W;
 
 1364           if (local_eqn_imag >= 0)
 
 1367             residuals[local_eqn_imag] -= gamma[ipt].imag() * test[l] * 
W;
 
 1374               for (
unsigned l2 = 0; l2 < n_node; l2++)
 
 1377                 local_unknown_real =
 
 1380                 local_unknown_imag =
 
 1384                 if (local_unknown_real >= 0)
 
 1386                   global_eqn_real = this->
eqn_number(local_unknown_real);
 
 1389                   jacobian(local_eqn_imag, local_unknown_real) -=
 
 1390                     d_gamma[ipt][global_eqn_real].imag() * test[l] * 
W;
 
 1392                 if (local_unknown_imag >= 0)
 
 1394                   global_eqn_imag = this->
eqn_number(local_unknown_imag);
 
 1397                   jacobian(local_eqn_imag, local_unknown_imag) -=
 
 1398                     d_gamma[ipt][global_eqn_imag].imag() * test[l] * 
W;
 
 1405               for (
unsigned l2 = 0; l2 < n_ext_data; l2++)
 
 1408                 external_unknown_real =
 
 1411                 external_unknown_imag =
 
 1415                 if (external_unknown_real >= 0)
 
 1417                   external_global_eqn_real =
 
 1421                   jacobian(local_eqn_imag, external_unknown_real) -=
 
 1422                     d_gamma[ipt][external_global_eqn_real].imag() * test[l] * 
W;
 
 1424                 if (external_unknown_imag >= 0)
 
 1426                   external_global_eqn_imag =
 
 1430                   jacobian(local_eqn_imag, external_unknown_imag) -=
 
 1431                     d_gamma[ipt][external_global_eqn_imag].imag() * test[l] * 
W;
 
 1458   template<
class ELEMENT>
 
 1462     std::complex<double>& gamma_con,
 
 1463     std::map<
unsigned, std::complex<double>>& d_gamma_con)
 
 1466     const std::complex<double> 
I(0.0, 1.0);
 
 1469     const unsigned n_node = this->nnode();
 
 1476     int local_unknown_real = 0, local_unknown_imag = 0;
 
 1477     int global_unknown_real = 0, global_unknown_imag = 0;
 
 1480     const unsigned n_intpt = this->integral_pt()->nweight();
 
 1486     gamma_con = std::complex<double>(0.0, 0.0);
 
 1487     d_gamma_con.clear();
 
 1491     for (
unsigned ipt = 0; ipt < n_intpt; ipt++)
 
 1494       for (
unsigned i = 0; 
i < (this->
Dim - 1); 
i++)
 
 1496         s[
i] = this->integral_pt()->knot(ipt, 
i);
 
 1500       double w = this->integral_pt()->weight(ipt);
 
 1503       this->dshape_local(
s, psi, dpsi);
 
 1510       std::complex<double> interpolated_u(0.0, 0.0);
 
 1513       for (
unsigned l = 0; l < n_node; l++)
 
 1516         for (
unsigned i = 0; 
i < this->
Dim; 
i++)
 
 1518           interpolated_x[
i] += this->nodal_position(l, 
i) * psi[l];
 
 1519           interpolated_dxds[
i] += this->nodal_position(l, 
i) * dpsi(l, 0);
 
 1523         std::complex<double> u_value(
 
 1524           this->nodal_value(l, this->U_index_helmholtz.real()),
 
 1525           this->nodal_value(l, this->U_index_helmholtz.imag()));
 
 1527         interpolated_u += u_value * psi(l);
 
 1533       double phi_p = atan2(interpolated_x[1], interpolated_x[0]);
 
 1536       double denom = (interpolated_x[0] * interpolated_x[0]) +
 
 1537                      (interpolated_x[1] * interpolated_x[1]);
 
 1538       double nom = -interpolated_dxds[1] * interpolated_x[0] +
 
 1539                    interpolated_dxds[0] * interpolated_x[1];
 
 1540       double dphi_ds = std::fabs(nom / denom);
 
 1546       gamma_con += (dphi_ds)*w *
 
 1547                    pow(exp(
I * (phi - phi_p)), 
static_cast<double>(n)) *
 
 1551       for (
unsigned l = 0; l < n_node; l++)
 
 1554         local_unknown_real =
 
 1555           this->nodal_local_eqn(l, this->U_index_helmholtz.real());
 
 1556         if (local_unknown_real >= 0)
 
 1558           global_unknown_real = this->eqn_number(local_unknown_real);
 
 1559           d_gamma_con[global_unknown_real] +=
 
 1560             (dphi_ds)*w * exp(
I * (phi - phi_p) * double(n)) * psi(l);
 
 1564         local_unknown_imag =
 
 1565           this->nodal_local_eqn(l, this->U_index_helmholtz.imag());
 
 1566         if (local_unknown_imag >= 0)
 
 1568           global_unknown_imag = this->eqn_number(local_unknown_imag);
 
 1572           d_gamma_con[global_unknown_imag] +=
 
 1574             pow(exp(
I * (phi - phi_p)), 
static_cast<double>(n)) * psi(l);
 
 1590   namespace ToleranceForHelmholtzOuterBoundary
 
 1603   namespace ToleranceForHelmholtzOuterBoundary
 
 1620   template<
class ELEMENT>
 
 1622     Vector<std::complex<double>>& a_coeff_pos,
 
 1623     Vector<std::complex<double>>& a_coeff_neg)
 
 1626     if (a_coeff_pos.size() != a_coeff_neg.size())
 
 1628       std::ostringstream error_stream;
 
 1629       error_stream << 
"a_coeff_pos and a_coeff_neg must have " 
 1630                    << 
"the same size. \n";
 
 1632         error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
 
 1637     unsigned n = a_coeff_pos.size();
 
 1640     for (
unsigned i = 0; 
i < n; 
i++)
 
 1642       a_coeff_pos[
i] = std::complex<double>(0.0, 0.0);
 
 1643       a_coeff_neg[
i] = std::complex<double>(0.0, 0.0);
 
 1647     unsigned nel = this->nelement();
 
 1648     for (
unsigned e = 0; 
e < nel; 
e++)
 
 1659       for (
unsigned i = 0; 
i < n; 
i++)
 
 1661         a_coeff_pos[
i] += el_a_coeff_pos[
i];
 
 1662         a_coeff_neg[
i] += el_a_coeff_neg[
i];
 
 1673   template<
class ELEMENT>
 
 1679       unsigned nel = this->nelement();
 
 1680       for (
unsigned e = 0; 
e < nel; 
e++)
 
 1683         unsigned nnod = fe_pt->
nnode();
 
 1684         for (
unsigned j = 0; j < nnod; j++)
 
 1690           x[0] = nod_pt->
x(0);
 
 1691           x[1] = nod_pt->
x(1);
 
 1694           double r = sqrt(x[0] * x[0] + x[1] * x[1]);
 
 1697           if (Outer_radius == 0.0)
 
 1699             throw OomphLibError(
"Outer radius for DtN BC must not be zero!",
 
 1700                                 OOMPH_CURRENT_FUNCTION,
 
 1701                                 OOMPH_EXCEPTION_LOCATION);
 
 1704           if (std::fabs((r - this->Outer_radius) / Outer_radius) >
 
 1707             std::ostringstream error_stream;
 
 1708             error_stream << 
"Node at " << x[0] << 
" " << x[1] << 
" has radius " 
 1709                          << r << 
" which does not " 
 1710                          << 
" agree with \nspecified outer radius " 
 1711                          << this->Outer_radius << 
" within relative tolerance " 
 1713                          << 
".\nYou can adjust the tolerance via\n" 
 1714                          << 
"ToleranceForHelmholtzOuterBoundary::Tol\n" 
 1715                          << 
"or recompile without PARANOID.\n";
 
 1717                                 OOMPH_CURRENT_FUNCTION,
 
 1718                                 OOMPH_EXCEPTION_LOCATION);
 
 1736       Nfourier_terms, Outer_radius * k, h_a, hp_a);
 
 1737     for (
unsigned i = 0; 
i < Nfourier_terms; 
i++)
 
 1743     unsigned nel = this->nelement();
 
 1744     for (
unsigned e = 0; 
e < nel; 
e++)
 
 1749           this->element_pt(
e));
 
 1756                                                 std::complex<double>(0.0, 0.0));
 
 1760       for (
unsigned ipt = 0; ipt < n_intpt; ipt++)
 
 1763         unsigned ndim_local = el_pt->
dim();
 
 1768         for (
unsigned i = 0; 
i < ndim_local; 
i++)
 
 1777         double phi = atan2(x[1], x[0]);
 
 1780         std::complex<double> gamma_con_p(0.0, 0.0), gamma_con_n(0.0, 0.0);
 
 1781         std::map<unsigned, std::complex<double>> d_gamma_con_p, d_gamma_con_n;
 
 1784         for (
unsigned nn = 0; nn < Nfourier_terms; nn++)
 
 1788           for (
unsigned ee = 0; ee < nel; ee++)
 
 1792                 this->element_pt(ee));
 
 1796               phi, 
int(nn), gamma_con_p, d_gamma_con_p);
 
 1800               phi, -
int(nn), gamma_con_n, d_gamma_con_n);
 
 1802             unsigned n_node = eel_pt->
nnode();
 
 1805               gamma_vector[ipt] += q[nn] * gamma_con_p;
 
 1806               for (
unsigned l = 0; l < n_node; l++)
 
 1811                 if (local_unknown_p_real >= 0)
 
 1813                   int global_unknown_p_real =
 
 1815                   d_gamma_vector[ipt][global_unknown_p_real] +=
 
 1816                     q[nn] * d_gamma_con_p[global_unknown_p_real];
 
 1823                 if (local_unknown_p_imag >= 0)
 
 1825                   int global_unknown_p_imag =
 
 1828                   d_gamma_vector[ipt][global_unknown_p_imag] +=
 
 1829                     q[nn] * d_gamma_con_p[global_unknown_p_imag];
 
 1835               gamma_vector[ipt] += q[nn] * (gamma_con_p + gamma_con_n);
 
 1836               for (
unsigned l = 0; l < n_node; l++)
 
 1841                 if (local_unknown_real >= 0)
 
 1843                   int global_unknown_real =
 
 1845                   d_gamma_vector[ipt][global_unknown_real] +=
 
 1846                     q[nn] * (d_gamma_con_p[global_unknown_real] +
 
 1847                              d_gamma_con_n[global_unknown_real]);
 
 1852                 if (local_unknown_imag >= 0)
 
 1854                   int global_unknown_imag =
 
 1856                   d_gamma_vector[ipt][global_unknown_imag] +=
 
 1857                     q[nn] * (d_gamma_con_p[global_unknown_imag] +
 
 1858                              d_gamma_con_n[global_unknown_imag]);
 
 1867       Gamma_at_gauss_point[el_pt] = gamma_vector;
 
 1868       D_Gamma_at_gauss_point[el_pt] = d_gamma_vector;
 
 1876   template<
class ELEMENT>
 
 1884       ELEMENT* elem_pt = 
new ELEMENT;
 
 1886       if (elem_pt->dim() == 3)
 
 1893                           "work correctly if nodes are hanging\n",
 
 1894                           "HelmholtzBCElementBase::Constructor",
 
 1895                           OOMPH_EXCEPTION_LOCATION);
 
 1930             "Bulk element must inherit from HelmholtzEquations.";
 
 1932             "Nodes are one dimensional, but cannot cast the bulk element to\n";
 
 1933           error_string += 
"HelmholtzEquations<1>\n.";
 
 1934           error_string += 
"If you desire this functionality, you must " 
 1935                           "implement it yourself\n";
 
 1938             error_string, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
 
 1958             "Bulk element must inherit from HelmholtzEquations.";
 
 1960             "Nodes are two dimensional, but cannot cast the bulk element to\n";
 
 1961           error_string += 
"HelmholtzEquations<2>\n.";
 
 1962           error_string += 
"If you desire this functionality, you must " 
 1963                           "implement it yourself\n";
 
 1966             error_string, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
 
 1986             "Bulk element must inherit from HelmholtzEquations.";
 
 1987           error_string += 
"Nodes are three dimensional, but cannot cast the " 
 1988                           "bulk element to\n";
 
 1989           error_string += 
"HelmholtzEquations<3>\n.";
 
 1990           error_string += 
"If you desire this functionality, you must " 
 1991                           "implement it yourself\n";
 
 1994             error_string, OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
 
 2006         std::ostringstream error_stream;
 
 2007         error_stream << 
"Dimension of node is " << 
Dim 
 2008                      << 
". It should be 1,2, or 3!" << std::endl;
 
 2011           error_stream.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
 
A Class for the derivatives of shape functions The class design is essentially the same as Shape,...
 
virtual bool is_a_copy() const
Return a boolean to indicate whether the Data objact contains any copied values. A base Data object c...
 
FaceElements are elements that coincide with the faces of higher-dimensional "bulk" elements....
 
int & face_index()
Index of the face (a number that uniquely identifies the face in the element)
 
void outer_unit_normal(const Vector< double > &s, Vector< double > &unit_normal) const
Compute outer unit normal at the specified local coordinate.
 
double zeta_nodal(const unsigned &n, const unsigned &k, const unsigned &i) const
In a FaceElement, the "global" intrinsic coordinate of the element along the boundary,...
 
Vector< double > local_coordinate_in_bulk(const Vector< double > &s) const
Return vector of local coordinates in bulk element, given the local coordinates in this FaceElement.
 
FiniteElement *& bulk_element_pt()
Pointer to higher-dimensional "bulk" element.
 
double interpolated_x(const Vector< double > &s, const unsigned &i) const
Return FE interpolated coordinate x[i] at local coordinate s. Overloaded to get information from bulk...
 
double J_eulerian(const Vector< double > &s) const
Return the Jacobian of mapping from local to global coordinates at local position s....
 
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
 
A general Finite Element class.
 
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
 
double nodal_value(const unsigned &n, const unsigned &i) const
Return the i-th value stored at local node n. Produces suitably interpolated values for hanging nodes...
 
virtual void output(std::ostream &outfile)
Output the element data — typically the values at the nodes in a format suitable for post-processing.
 
virtual void shape(const Vector< double > &s, Shape &psi) const =0
Calculate the geometric shape functions at local coordinate s. This function must be overloaded for e...
 
int nodal_local_eqn(const unsigned &n, const unsigned &i) const
Return the local equation number corresponding to the i-th value at the n-th local node.
 
unsigned dim() const
Return the spatial dimension of the element, i.e. the number of local coordinates required to paramet...
 
unsigned nnode() const
Return the number of nodes.
 
Integral *const  & integral_pt() const
Return the pointer to the integration scheme (const version)
 
double nodal_position(const unsigned &n, const unsigned &i) const
Return the i-th coordinate at local node n. If the node is hanging, the appropriate interpolation is ...
 
virtual void dshape_local(const Vector< double > &s, Shape &psi, DShape &dpsids) const
Function to compute the geometric shape functions and derivatives w.r.t. local coordinates at local c...
 
virtual void build_face_element(const int &face_index, FaceElement *face_element_pt)
Function for building a lower dimensional FaceElement on the specified face of the FiniteElement....
 
unsigned nexternal_data() const
Return the number of external data objects.
 
unsigned long eqn_number(const unsigned &ieqn_local) const
Return the global equation number corresponding to the ieqn_local-th local equation number.
 
static DenseMatrix< double > Dummy_matrix
Empty dense matrix used as a dummy argument to combined residual and jacobian functions in the case w...
 
int external_local_eqn(const unsigned &i, const unsigned &j)
Return the local equation number corresponding to the j-th value stored at the i-th external data.
 
unsigned add_external_data(Data *const &data_pt, const bool &fd=true)
Add a (pointer to an) external data object to the element and return its index (i....
 
////////////////////////////////////////////////////////////////// //////////////////////////////////...
 
void fill_in_generic_residual_contribution_helmholtz_abc(Vector< double > &residuals, DenseMatrix< double > &jacobian, const unsigned &flag)
Compute the element's residual vector and the ( Jacobian matrix. Overloaded version,...
 
void fill_in_contribution_to_jacobian(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Add the element's contribution to its residual vector and its Jacobian matrix.
 
unsigned * ABC_order_pt
Pointer to order of absorbing boundary condition.
 
double * Outer_radius_pt
Pointer to radius of outer boundary (must be a circle!)
 
void fill_in_contribution_to_residuals(Vector< double > &residuals)
Add the element's contribution to its residual vector.
 
HelmholtzAbsorbingBCElement(FiniteElement *const &bulk_el_pt, const int &face_index)
Construct element from specification of bulk element and face index.
 
double *& outer_radius_pt()
Get pointer to radius of outer boundary (must be a cirle)
 
unsigned *& abc_order_pt()
Pointer to order of absorbing boundary condition.
 
////////////////////////////////////////////////////////////////// //////////////////////////////////...
 
double test_only(const Vector< double > &s, Shape &test) const
Function to compute the shape and test functions and to return the Jacobian of mapping between local ...
 
void output(std::ostream &outfile, const unsigned &n_plot)
Output function – forward to broken version in FiniteElement until somebody decides what exactly they...
 
double global_power_contribution()
Compute the element's contribution to the time-averaged radiated power over the artificial boundary.
 
double global_power_contribution(std::ofstream &outfile)
Compute the element's contribution to the time-averaged radiated power over the artificial boundary....
 
HelmholtzBCElementBase()
Broken empty constructor.
 
double zeta_nodal(const unsigned &n, const unsigned &k, const unsigned &i) const
Broken assignment operator.
 
void output(FILE *file_pt)
C-style output function – forward to broken version in FiniteElement until somebody decides what exac...
 
void output(std::ostream &outfile)
Output function – forward to broken version in FiniteElement until somebody decides what exactly they...
 
virtual std::complex< unsigned > u_index_helmholtz() const
Return the index at which the real/imag unknown value is stored.
 
HelmholtzBCElementBase(const HelmholtzBCElementBase &dummy)=delete
Broken copy constructor.
 
double d_shape_and_test_local(const Vector< double > &s, Shape &psi, Shape &test, DShape &dpsi_ds, DShape &dtest_ds) const
Function to compute the shape, test functions and derivates and to return the Jacobian of mapping bet...
 
void compute_contribution_to_fourier_components(Vector< std::complex< double >> &a_coeff_pos, Vector< std::complex< double >> &a_coeff_neg)
Compute element's contribution to Fourier components of the solution – length of vector indicates num...
 
void output(FILE *file_pt, const unsigned &n_plot)
C-style output function – forward to broken version in FiniteElement until somebody decides what exac...
 
unsigned Dim
The spatial dimension of the problem.
 
std::complex< unsigned > U_index_helmholtz
The index at which the real and imag part of the unknown is stored at the nodes.
 
FaceElement used to apply Sommerfeld radiation conditon via Dirichlet to Neumann map.
 
void fill_in_generic_residual_contribution_helmholtz_DtN_bc(Vector< double > &residuals, DenseMatrix< double > &jacobian, const unsigned &flag)
Compute the element's residual vector Jacobian matrix. Overloaded version, using the gamma computed i...
 
HelmholtzDtNMesh< ELEMENT > * outer_boundary_mesh_pt() const
Access function to mesh of all DtN boundary condition elements (needed to get access to gamma values)
 
HelmholtzDtNBoundaryElement(FiniteElement *const &bulk_el_pt, const int &face_index)
Construct element from specification of bulk element and face index.
 
void set_outer_boundary_mesh_pt(HelmholtzDtNMesh< ELEMENT > *mesh_pt)
Set mesh of all DtN boundary condition elements.
 
void fill_in_contribution_to_residuals(Vector< double > &residuals)
Add the element's contribution to its residual vector.
 
HelmholtzDtNMesh< ELEMENT > * Outer_boundary_mesh_pt
Pointer to mesh of all DtN boundary condition elements (needed to get access to gamma values)
 
void complete_setup_of_dependencies()
Complete the setup of additional dependencies arising through the far-away interaction with other nod...
 
void compute_gamma_contribution(const double &phi, const int &n, std::complex< double > &gamma_con, std::map< unsigned, std::complex< double >> &d_gamma_con)
Compute the contribution of the element to the Gamma integral and its derivates w....
 
void fill_in_contribution_to_jacobian(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Add the element's contribution to its residual vector and its Jacobian matrix.
 
/////////////////////////////////////////////////////////////////// /////////////////////////////////...
 
std::map< FiniteElement *, Vector< std::complex< double > > > Gamma_at_gauss_point
Container to store the gamma integral for given Gauss point and element.
 
std::map< FiniteElement *, Vector< std::map< unsigned, std::complex< double > > > > D_Gamma_at_gauss_point
Container to store the derivate of Gamma integral w.r.t global unknown evaluated at Gauss points for ...
 
Vector< std::complex< double > > & gamma_at_gauss_point(FiniteElement *el_pt)
Gamma integral evaluated at Gauss points for specified element.
 
unsigned Nfourier_terms
Nbr of Fourier terms used in the Gamma computation.
 
void setup_gamma()
Compute and store the gamma integral at all integration points of the constituent elements.
 
void compute_fourier_components(Vector< std::complex< double >> &a_coeff_pos, Vector< std::complex< double >> &a_coeff_neg)
Compute Fourier components of the solution – length of vector indicates number of terms to be compute...
 
unsigned & nfourier_terms()
Number of Fourier terms used in the computation of the gamma integral.
 
double Outer_radius
Outer radius.
 
HelmholtzDtNMesh(const double &outer_radius, const unsigned &nfourier_terms)
Constructor: Specify radius of outer boundary and number of Fourier terms used in the computation of ...
 
Vector< std::map< unsigned, std::complex< double > > > & d_gamma_at_gauss_point(FiniteElement *el_pt)
Derivative of Gamma integral w.r.t global unknown, evaluated at Gauss points for specified element.
 
double & outer_radius()
The outer radius.
 
A class for all isoparametric elements that solve the Helmholtz equations.
 
virtual std::complex< unsigned > u_index_helmholtz() const
Broken assignment operator.
 
virtual double knot(const unsigned &i, const unsigned &j) const =0
Return local coordinate s[j] of i-th integration point.
 
virtual unsigned nweight() const =0
Return the number of integration points of the scheme.
 
virtual double weight(const unsigned &i) const =0
Return weight of i-th integration point.
 
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
 
double & x(const unsigned &i)
Return the i-th nodal coordinate.
 
unsigned ndim() const
Return (Eulerian) spatial dimension of the node.
 
virtual Node * copied_node_pt() const
Return pointer to copied node (null if the current node is not a copy – always the case here; it's ov...
 
An OomphLibError object which should be thrown when an run-time error is encountered....
 
An OomphLibWarning object which should be created as a temporary object to issue a warning....
 
RefineableElements are FiniteElements that may be subdivided into children to provide a better local ...
 
A Class for shape functions. In simple cases, the shape functions have only one index that can be tho...
 
A slight extension to the standard template vector class so that we can include "graceful" array rang...
 
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn't been defined.
 
void CHankel_first(const unsigned &n, const std::complex< double > &x, Vector< std::complex< double >> &h, Vector< std::complex< double >> &hp)
Compute Hankel function of the first kind of orders 0...n and its derivates at coordinate x....
 
void Hankel_first(const unsigned &n, const double &x, Vector< std::complex< double >> &h, Vector< std::complex< double >> &hp)
Compute Hankel function of the first kind of orders 0...n and its derivates at coordinate x....
 
const double Pi
50 digits from maple
 
const std::complex< double > I(0.0, 1.0)
The imaginary unit.
 
unsigned Dim
Dimension of zeta tuples (set by get_dim_helper) – needed because we store the scalar coordinates in ...
 
double Tol
Relative tolerance to within radius of points on DtN boundary are allowed to deviate from specified v...
 
//////////////////////////////////////////////////////////////////// ////////////////////////////////...