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...
//////////////////////////////////////////////////////////////////// ////////////////////////////////...