fourier_decomposed_helmholtz_bc_elements.h
Go to the documentation of this file.
1// LIC// ====================================================================
2// LIC// This file forms part of oomph-lib, the object-oriented,
3// LIC// multi-physics finite-element library, available
4// LIC// at http://www.oomph-lib.org.
5// LIC//
6// LIC// Copyright (C) 2006-2026 Matthias Heil and Andrew Hazel
7// LIC//
8// LIC// This library is free software; you can redistribute it and/or
9// LIC// modify it under the terms of the GNU Lesser General Public
10// LIC// License as published by the Free Software Foundation; either
11// LIC// version 2.1 of the License, or (at your option) any later version.
12// LIC//
13// LIC// This library is distributed in the hope that it will be useful,
14// LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// LIC// Lesser General Public License for more details.
17// LIC//
18// LIC// You should have received a copy of the GNU Lesser General Public
19// LIC// License along with this library; if not, write to the Free Software
20// LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21// LIC// 02110-1301 USA.
22// LIC//
23// LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24// LIC//
25// LIC//====================================================================
26// Header file for elements that are used to apply Sommerfeld
27// boundary conditions to the Fourier decomposed Helmholtz equations
28#ifndef OOMPH_FOURIER_DECOMPOSED_HELMHOLTZ_BC_ELEMENTS_HEADER
29#define OOMPH_FOURIER_DECOMPOSED_HELMHOLTZ_BC_ELEMENTS_HEADER
30
31
32// Config header
33#ifdef HAVE_CONFIG_H
34#include <oomph-lib-config.h>
35#endif
36
37#include "math.h"
38#include <complex>
39
40// Get the Bessel functions
41#include "oomph_crbond_bessel.h"
42
44
45namespace oomph
46{
47 /////////////////////////////////////////////////////////////////////
48 /////////////////////////////////////////////////////////////////////
49 /////////////////////////////////////////////////////////////////////
50
51
52 //======================================================================
53 /// A class for elements that allow the approximation of the
54 /// Sommerfeld radiation BC for Fourier decomposed Helmholtz equations.
55 /// The element geometry is obtained from the FaceGeometry<ELEMENT>
56 /// policy class.
57 //======================================================================
58 template<class ELEMENT>
60 : public virtual FaceGeometry<ELEMENT>,
61 public virtual FaceElement
62 {
63 public:
64 /// Constructor, takes the pointer to the "bulk" element and the
65 /// index of the face to which the element is attached.
67 const int& face_index);
68
69 /// Broken empty constructor
71 {
72 throw OomphLibError("Don't call empty constructor for "
73 "FourierDecomposedHelmholtzBCElementBase",
76 }
77
78 /// Broken copy constructor
81
82 /// Broken assignment operator
83 // Commented out broken assignment operator because this can lead to a
84 // conflict warning when used in the virtual inheritence hierarchy.
85 // Essentially the compiler doesn't realise that two separate
86 // implementations of the broken function are the same and so, quite
87 // rightly, it shouts.
88 /*void operator=(const FourierDecomposedHelmholtzBCElementBase&) = delete;*/
89
90
91 /// Specify the value of nodal zeta from the face geometry
92 /// The "global" intrinsic coordinate of the element when
93 /// viewed as part of a geometric object should be given by
94 /// the FaceElement representation, by default (needed to break
95 /// indeterminacy if bulk element is SolidElement)
96 double zeta_nodal(const unsigned& n,
97 const unsigned& k,
98 const unsigned& i) const
99 {
100 return FaceElement::zeta_nodal(n, k, i);
101 }
102
103
104 /// Output function -- forward to broken version in FiniteElement
105 /// until somebody decides what exactly they want to plot here...
106 void output(std::ostream& outfile)
107 {
109 }
110
111 /// Output function -- forward to broken version in FiniteElement
112 /// until somebody decides what exactly they want to plot here...
113 void output(std::ostream& outfile, const unsigned& n_plot)
114 {
116 }
117
118 /// C-style output function -- forward to broken version in FiniteElement
119 /// until somebody decides what exactly they want to plot here...
124
125 /// C-style output function -- forward to broken version in
126 /// FiniteElement until somebody decides what exactly they want to plot
127 /// here...
128 void output(FILE* file_pt, const unsigned& n_plot)
129 {
131 }
132
133 /// Return the index at which the real/imag unknown value
134 /// is stored.
135 virtual inline std::complex<unsigned> u_index_fourier_decomposed_helmholtz()
136 const
137 {
138 return std::complex<unsigned>(
141 }
142
143 /// Compute the element's contribution to the time-averaged
144 /// radiated power over the artificial boundary
146 {
147 // Dummy output file
148 std::ofstream outfile;
150 }
151
152 /// Compute the element's contribution to the time-averaged
153 /// radiated power over the artificial boundary. Also output the
154 /// power density as a fct of the zenith angle in the specified
155 /// output file if it's open.
156 double global_power_contribution(std::ofstream& outfile)
157 {
158 // pointer to the corresponding bulk element
159 ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(this->bulk_element_pt());
160
161 // Number of nodes in bulk element
162 unsigned nnode_bulk = bulk_elem_pt->nnode();
163 const unsigned n_node_local = this->nnode();
164
165 // get the dim of the bulk and local nodes
166 const unsigned bulk_dim = bulk_elem_pt->dim();
167 const unsigned local_dim = this->node_pt(0)->ndim();
168
169 // Set up memory for the shape and test functions
171
172 // Set up memory for the shape functions
175
176 // Set up memory for the outer unit normal
178
179 // Set the value of n_intpt
180 const unsigned n_intpt = integral_pt()->nweight();
181
182 // Set the Vector to hold local coordinates
184 double power = 0.0;
185
186 // Output?
187 if (outfile.is_open())
188 {
189 outfile << "ZONE\n";
190 }
191
192 // Loop over the integration points
193 //--------------------------------
194 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
195 {
196 // Assign values of s
197 for (unsigned i = 0; i < (local_dim - 1); i++)
198 {
199 s[i] = integral_pt()->knot(ipt, i);
200 }
201 // get the outer_unit_ext vector
203
204 // Get the integral weight
205 double w = integral_pt()->weight(ipt);
206
207 // Get jacobian of mapping
208 double J = J_eulerian(s);
209
210 // Premultiply the weights and the Jacobian
211 double W = w * J;
212
213 // Get local coordinates in bulk element by copy construction
215
216 // Call the derivatives of the shape functions
217 // in the bulk -- must do this via s because this point
218 // is not an integration point the bulk element!
220 this->shape(s, psi);
221
222 // Derivs of Eulerian coordinates w.r.t. local coordinates
223 std::complex<double> dphi_dn(0.0, 0.0);
225 std::complex<double> interpolated_phi(0.0, 0.0);
227
228 // Calculate function value and derivatives:
229 //-----------------------------------------
230 // Loop over nodes
231 for (unsigned l = 0; l < nnode_bulk; l++)
232 {
233 // Get the nodal value of the helmholtz unknown
234 const std::complex<double> phi_value(
236 l, bulk_elem_pt->u_index_fourier_decomposed_helmholtz().real()),
238 l, bulk_elem_pt->u_index_fourier_decomposed_helmholtz().imag()));
239
240 // Loop over directions
241 for (unsigned i = 0; i < bulk_dim; i++)
242 {
244 }
245 } // End of loop over the bulk_nodes
246
247 for (unsigned l = 0; l < n_node_local; l++)
248 {
249 // Get the nodal value of the Helmholtz unknown
250 const std::complex<double> phi_value(
253
255 }
256
257 // define dphi_dn
258 for (unsigned i = 0; i < bulk_dim; i++)
259 {
261 }
262
263 // Power density
264 double integrand = (interpolated_phi.real() * dphi_dn.imag() -
265 interpolated_phi.imag() * dphi_dn.real());
266
267 interpolated_x(s, x);
268 double theta = atan2(x[0], x[1]);
269 // Output?
270 if (outfile.is_open())
271 {
272 outfile << x[0] << " " << x[1] << " " << theta << " " << integrand
273 << "\n";
274 }
275
276 // ...add to integral
278 }
279
280 return power;
281 }
282
283 protected:
284 /// Function to compute the test functions and to return
285 /// the Jacobian of mapping between local and global (Eulerian)
286 /// coordinates
287 inline double shape_and_test(const Vector<double>& s,
288 Shape& psi,
289 Shape& test) const
290 {
291 // Get the shape functions
292 shape(s, test);
293
294 unsigned nnod = nnode();
295 for (unsigned i = 0; i < nnod; i++)
296 {
297 psi[i] = test[i];
298 }
299
300 // Return the value of the jacobian
301 return J_eulerian(s);
302 }
303
304 /// Function to compute the shape, test functions and derivates
305 /// and to return
306 /// the Jacobian of mapping between local and global (Eulerian)
307 /// coordinates
309 Shape& psi,
310 Shape& test,
312 DShape& dtest_ds) const
313 {
314 // Find number of nodes
315 unsigned n_node = nnode();
316
317 // Get the shape functions
319
320 // Set the test functions equal to the shape functions
321 test.shallow_copy_from(psi);
322 dtest_ds.shallow_copy_from(dpsi_ds);
323
324 // Return the value of the jacobian
325 return J_eulerian(s);
326 }
327
328 /// The index at which the real and imag part of the unknown is
329 /// stored at the nodes
331 };
332
333
334 //////////////////////////////////////////////////////////////////////
335 //////////////////////////////////////////////////////////////////////
336 //////////////////////////////////////////////////////////////////////
337
338
339 /// =================================================================
340 /// Mesh for DtN boundary condition elements -- provides
341 /// functionality to apply Sommerfeld radiation condtion
342 /// via DtN BC.
343 /// =================================================================
344 template<class ELEMENT>
346 {
347 public:
348 /// Constructor: Specify radius of outer boundary and number of
349 /// terms used in the computation of the gamma integral
351 const unsigned& n_terms)
353 {
354 }
355
356 /// Compute and store the gamma integral at all integration
357 /// points of the constituent elements.
358 void setup_gamma();
359
360 /// Gamma integral evaluated at Gauss points
361 /// for specified element
366
367 /// Derivative of Gamma integral w.r.t global unknown, evaluated
368 /// at Gauss points for specified element
374
375 /// The outer radius
376 double& outer_radius()
377 {
378 return Outer_radius;
379 }
380
381 /// Number of terms used in the computation of the
382 /// gamma integral
383 unsigned& n_terms()
384 {
385 return N_terms;
386 }
387
388 private:
389 /// Outer radius
391
392 /// Number of terms used in the Gamma computation
393 unsigned N_terms;
394
395
396 /// Container to store the gamma integral for given Gauss point
397 /// and element
398 std::map<FiniteElement*, Vector<std::complex<double>>> Gamma_at_gauss_point;
399
400
401 /// Container to store the derivate of Gamma integral w.r.t
402 /// global unknown evaluated at Gauss points for specified element
403 std::map<FiniteElement*, Vector<std::map<unsigned, std::complex<double>>>>
405 };
406
407 /////////////////////////////////////////////////////////////////////
408 /////////////////////////////////////////////////////////////////////
409
410 //=============================================================
411 /// FaceElement used to apply Sommerfeld radiation conditon
412 /// via Dirichlet to Neumann map.
413 //==============================================================
414 template<class ELEMENT>
417 {
418 public:
419 /// Construct element from specification of bulk element and
420 /// face index.
426
427 /// Add the element's contribution to its residual vector
429 {
430 // Call the generic residuals function with flag set to 0
431 // using a dummy matrix argument
434 }
435
436
437 /// Add the element's contribution to its residual vector and its
438 /// Jacobian matrix
440 DenseMatrix<double>& jacobian)
441 {
442 // Call the generic routine with the flag set to 1
444 residuals, jacobian, 1);
445 }
446
447 /// Compute the contribution of the element
448 /// to the Gamma integral and its derivates w.r.t
449 /// to global unknows; the function takes the wavenumber (for gamma
450 /// integral, not the one from the Fourier decomposition of the Helmholtz
451 /// equations!) and the polar angle theta as input.
453 const double& theta,
454 const unsigned& n,
455 std::complex<double>& gamma_con,
456 std::map<unsigned, std::complex<double>>& d_gamma_con);
457
458
459 /// Access function to mesh of all DtN boundary condition elements
460 /// (needed to get access to gamma values)
465
466 /// Set mesh of all DtN boundary condition elements
472
473
474 /// Complete the setup of additional dependencies arising
475 /// through the far-away interaction with other nodes in
476 /// Outer_boundary_mesh_pt.
478 {
479 // Create a set of all nodes
480 std::set<Node*> node_set;
481 unsigned nel = Outer_boundary_mesh_pt->nelement();
482 for (unsigned e = 0; e < nel; e++)
483 {
484 FiniteElement* el_pt = Outer_boundary_mesh_pt->finite_element_pt(e);
485 unsigned nnod = el_pt->nnode();
486 for (unsigned j = 0; j < nnod; j++)
487 {
489
490 // Don't add copied nodes
491 if (!(nod_pt->is_a_copy()))
492 {
493 node_set.insert(nod_pt);
494 }
495 }
496 }
497 // Now erase the current element's own nodes
498 unsigned nnod = this->nnode();
499 for (unsigned j = 0; j < nnod; j++)
500 {
501 Node* nod_pt = this->node_pt(j);
502 node_set.erase(nod_pt);
503
504 // If the element's node is a copy then its "master" will
505 // already have been added in the set above -- remove the
506 // master to avoid double counting eqn numbers
507 if (nod_pt->is_a_copy())
508 {
509 node_set.erase(nod_pt->copied_node_pt());
510 }
511 }
512
513 // Now declare these nodes to be the element's external Data
514 for (std::set<Node*>::iterator it = node_set.begin();
515 it != node_set.end();
516 it++)
517 {
518 this->add_external_data(*it);
519 }
520 }
521
522
523 private:
524 /// Compute the element's residual vector
525 /// Jacobian matrix.
526 /// Overloaded version, using the gamma computed in the mesh
529 DenseMatrix<double>& jacobian,
530 const unsigned& flag)
531 {
532 // Find out how many nodes there are
533 const unsigned n_node = this->nnode();
534
535 // Set up memory for the shape and test functions
538
539 // Set the value of Nintpt
540 const unsigned n_intpt = this->integral_pt()->nweight();
541
542 // Set the Vector to hold local coordinates
543 Vector<double> s(1);
544
545 // Integers to hold the local equation and unknown numbers
550
551
552 // Get the gamma value for the current integration point
553 // from the mesh
555 Outer_boundary_mesh_pt->gamma_at_gauss_point(this));
556
558 Outer_boundary_mesh_pt->d_gamma_at_gauss_point(this));
559
560 // Loop over the integration points
561 //--------------------------------
562 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
563 {
564 // Assign values of s
565 for (unsigned i = 0; i < 1; i++)
566 {
567 s[i] = this->integral_pt()->knot(ipt, i);
568 }
569
570 // Get the integral weight
571 double w = this->integral_pt()->weight(ipt);
572
573 // Find the shape test functions and derivates; return the Jacobian
574 // of the mapping between local and global (Eulerian)
575 // coordinates
576 double J = this->shape_and_test(s, psi, test);
577
578 // Premultiply the weights and the Jacobian
579 double W = w * J;
580
581 // Build up radius
582 double r = 0.0;
583 for (unsigned j = 0; j < n_node; j++)
584 {
585 r += this->node_pt(j)->x(0) * psi(j);
586 }
587
588 // Now add to the appropriate equations
589 // Loop over the test functions:loop over the nodes
590 for (unsigned l = 0; l < n_node; l++)
591 {
594
597
598 // IF it's not a boundary condition
599 if (local_eqn_real >= 0)
600 {
601 // Add the gamma contribution in this int_point to the res
602 residuals[local_eqn_real] += gamma[ipt].real() * test[l] * r * W;
603
604 // Calculate the jacobian
605 //-----------------------
606 if (flag)
607 {
608 // Loop over the shape functions again
609 for (unsigned l2 = 0; l2 < n_node; l2++)
610 {
611 // Add the contribution of the local data
614
617
618 // If at a non-zero degree of freedom add in the entry
619 if (local_unknown_real >= 0)
620 {
621 // Add the contribution
624 d_gamma[ipt][global_unk_real].real() * test[l] * r * W;
625 }
626 if (local_unknown_imag >= 0)
627 {
628 // Add the contribution
631 d_gamma[ipt][global_unk_imag].real() * test[l] * r * W;
632 }
633 } // End of loop over nodes l2
634
635 // Add the contribution of the external data
636 unsigned n_ext_data = this->nexternal_data();
637 // Loop over the shape functions again
638 for (unsigned l2 = 0; l2 < n_ext_data; l2++)
639 {
640 // Add the contribution of the local data
643
646
647 // If at a non-zero degree of freedom add in the entry
648 if (external_unknown_real >= 0)
649 {
650 // Add the contribution
655 r * W;
656 }
657 if (external_unknown_imag >= 0)
658 {
659 // Add the contribution
664 r * W;
665 }
666 } // End of loop over external data
667 } // End of flag
668 } // end of local_eqn_real
669
670 if (local_eqn_imag >= 0)
671 {
672 // Add the gamma contribution in this int_point to the res
673 residuals[local_eqn_imag] += gamma[ipt].imag() * test[l] * r * W;
674
675 // Calculate the jacobian
676 //-----------------------
677 if (flag)
678 {
679 // Loop over the shape functions again
680 for (unsigned l2 = 0; l2 < n_node; l2++)
681 {
682 // Add the contribution of the local data
685
688
689 // If at a non-zero degree of freedom add in the entry
690 if (local_unknown_real >= 0)
691 {
692 // Add the contribution
695 d_gamma[ipt][global_unk_real].imag() * test[l] * r * W;
696 }
697 if (local_unknown_imag >= 0)
698 {
699 // Add the contribution
702 d_gamma[ipt][global_unk_imag].imag() * test[l] * r * W;
703 }
704 } // End of loop over nodes l2
705
706 // Add the contribution of the external data
707 unsigned n_ext_data = this->nexternal_data();
708
709 // Loop over the shape functions again
710 for (unsigned l2 = 0; l2 < n_ext_data; l2++)
711 {
712 // Add the contribution of the local data
715
718
719 // If at a non-zero degree of freedom add in the entry
720 if (external_unknown_real >= 0)
721 {
722 // Add the contribution
727 r * W;
728 }
729 if (external_unknown_imag >= 0)
730 {
731 // Add the contribution
736 r * W;
737 }
738 } // End of loop over external data
739 } // End of flag
740 } // end of local_eqn_imag
741 } // end of loop over the nodes
742 } // End of loop over int_pt
743 }
744
745
746 /// Pointer to mesh of all DtN boundary condition elements
747 /// (needed to get access to gamma values)
749 };
750
751
752 ////////////////////////////////////////////////////////////////
753 ////////////////////////////////////////////////////////////////
754 ////////////////////////////////////////////////////////////////
755
756
757 //===========start_compute_gamma_contribution==================
758 /// compute the contribution of the element
759 /// to the Gamma integral and its derivates w.r.t
760 /// to global unknows; the function takes wavenumber n
761 /// (from the computation of the gamma integral, not the one
762 /// from the Fourier decomposition of the Helmholtz equations!)
763 /// and polar angle theta as input.
764 //==============================================================
765 template<class ELEMENT>
768 const double& theta,
769 const unsigned& n,
770 std::complex<double>& gamma_con,
771 std::map<unsigned, std::complex<double>>& d_gamma_con)
772 {
773 // Parameters
775 dynamic_cast<ELEMENT*>(this->bulk_element_pt())->fourier_wavenumber();
776
777 // define the imaginary number
778 const std::complex<double> I(0.0, 1.0);
779
780 // Find out how many nodes there are
781 const unsigned n_node = this->nnode();
782
783 // Set up memory for the shape functions
785 DShape dpsi(n_node, 1);
786
787 // initialise the variable
789 int global_eqn_real = 0, global_eqn_imag = 0;
790
791 // Set the value of n_intpt
792 const unsigned n_intpt = this->integral_pt()->nweight();
793
794 // Set the Vector to hold local coordinates
795 Vector<double> s(1);
796
797 // Initialise
798 gamma_con = std::complex<double>(0.0, 0.0);
799 d_gamma_con.clear();
800
801 // Loop over the integration points
802 //--------------------------------
803 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
804 {
805 // Assign values of s
806 for (unsigned i = 0; i < 1; i++)
807 {
808 s[i] = this->integral_pt()->knot(ipt, i);
809 }
810
811 // Get the integral weight
812 double w = this->integral_pt()->weight(ipt);
813
814 // Get the shape functions
815 this->dshape_local(s, psi, dpsi);
816
817 // Eulerian coordinates at Gauss point
819
820 // Derivs of Eulerian coordinates w.r.t. local coordinates
822 std::complex<double> interpolated_u(0.0, 0.0);
823
824 // Assemble x and its derivs
825 for (unsigned l = 0; l < n_node; l++)
826 {
827 // Loop over directions
828 for (unsigned i = 0; i < 2; i++)
829 {
830 interpolated_x[i] += this->nodal_position(l, i) * psi[l];
831 interpolated_dxds[i] += this->nodal_position(l, i) * dpsi(l, 0);
832 }
833
834 // Get the nodal value of the helmholtz unknown
835 std::complex<double> u_value(
836 this->nodal_value(l,
837 this->U_index_fourier_decomposed_helmholtz.real()),
839 this->U_index_fourier_decomposed_helmholtz.imag()));
840
841 interpolated_u += u_value * psi(l);
842
843 } // End of loop over the nodes
844
845
846 // calculate the integral
847 //-----------------------
848 // define the variable theta
849 double phi = atan2(interpolated_x[0], interpolated_x[1]);
850
851 // define dphi_ds=-z'/r
852 double dphi_ds = -std::fabs(-interpolated_dxds[1] / interpolated_x[0]);
853
854 // define the associated legendre functions
855 double p_theta =
857
858 double p_phi =
860
861 gamma_con += interpolated_u * p_phi * p_theta * sin(phi) * w * dphi_ds;
862
863 // compute the contribution to each node to the map
864 for (unsigned l = 0; l < n_node; l++)
865 {
866 // Add the contribution of the real local data
868 l, this->U_index_fourier_decomposed_helmholtz.real());
869 if (local_unknown_real >= 0)
870 {
871 global_eqn_real = this->eqn_number(local_unknown_real);
873 psi(l) * p_phi * p_theta * sin(phi) * w * dphi_ds;
874 }
875
876 // Add the contribution of the imag local data
878 l, this->U_index_fourier_decomposed_helmholtz.imag());
879 if (local_unknown_imag >= 0)
880 {
883 I * psi(l) * p_phi * p_theta * sin(phi) * w * dphi_ds;
884 }
885 } // end of loop over the node
886 } // End of loop over integration points
887 }
888
889
890 /////////////////////////////////////////////////////////////////////
891 /////////////////////////////////////////////////////////////////////
892 /////////////////////////////////////////////////////////////////////
893
894
895 //===========================================================================
896 /// Namespace for checking radius of nodes on (assumed to be circular)
897 /// DtN boundary
898 //===========================================================================
899 namespace ToleranceForFourierDecomposedHelmholtzOuterBoundary
900 {
901 /// Relative tolerance to within radius of points on DtN boundary
902 /// are allowed to deviate from specified value
903 extern double Tol;
904
905 } // namespace ToleranceForFourierDecomposedHelmholtzOuterBoundary
906
907
908 //===========================================================================
909 /// Namespace for checking radius of nodes on (assumed to be circular)
910 /// DtN boundary
911 //===========================================================================
912 namespace ToleranceForFourierDecomposedHelmholtzOuterBoundary
913 {
914 /// Relative tolerance to within radius of points on DtN boundary
915 /// are allowed to deviate from specified value
916 double Tol = 1.0e-3;
917
918 } // namespace ToleranceForFourierDecomposedHelmholtzOuterBoundary
919
920 /////////////////////////////////////////////////////////////////////
921 /////////////////////////////////////////////////////////////////////
922 /////////////////////////////////////////////////////////////////////
923
924
925 /// ================================================================
926 /// Compute and store the gamma integral and derivates
927 // /w.r.t global unknows at all integration points
928 /// of the mesh's constituent elements
929 //================================================================
930 template<class ELEMENT>
932 {
933#ifdef PARANOID
934 {
935 // Loop over elements e
936 unsigned nel = this->nelement();
937 for (unsigned e = 0; e < nel; e++)
938 {
939 FiniteElement* fe_pt = finite_element_pt(e);
940 unsigned nnod = fe_pt->nnode();
941 for (unsigned j = 0; j < nnod; j++)
942 {
944
945 // Extract nodal coordinates from node:
946 Vector<double> x(2);
947 x[0] = nod_pt->x(0);
948 x[1] = nod_pt->x(1);
949
950 // Evaluate the radial distance
951 double r = sqrt(x[0] * x[0] + x[1] * x[1]);
952
953 // Check
954 if (Outer_radius == 0.0)
955 {
956 throw OomphLibError("Outer radius for DtN BC must not be zero!",
959 }
960
961 if (std::fabs((r - this->Outer_radius) / Outer_radius) >
963 {
964 std::ostringstream error_stream;
966 << "Node at " << x[0] << " " << x[1] << " has radius " << r
967 << " which does not "
968 << " agree with \nspecified outer radius " << this->Outer_radius
969 << " within relative tolerance "
971 << ".\nYou can adjust the tolerance via\n"
972 << "ToleranceForFourierDecomposedHelmholtzOuterBoundary::Tol\n"
973 << "or recompile without PARANOID.\n";
974 throw OomphLibError(error_stream.str(),
977 }
978 }
979 }
980 }
981#endif
982
983
984 // Get parameters from first element
987 this->element_pt(0));
988 double k =
989 sqrt(dynamic_cast<ELEMENT*>(el_pt->bulk_element_pt())->k_squared());
991 dynamic_cast<ELEMENT*>(el_pt->bulk_element_pt())->fourier_wavenumber();
992 double n_hankel_order_max = double(N_terms) + 0.5;
993 double n_hankel_order_tmp = 0.0;
994
995 /// Imaginary unit
996 std::complex<double> I(0.0, 1.0);
997
998 // Precompute factors in sum
999 Vector<std::complex<double>> h_a(N_terms + 1), hp_a(N_terms + 1),
1000 q(N_terms + 1, std::complex<double>(0.0, 0.0));
1001 Vector<double> jv(N_terms + 1), yv(N_terms + 1), djv(N_terms + 1),
1002 dyv(N_terms + 1);
1003
1004 // Get Bessel functions
1005 CRBond_Bessel::bessjyv(n_hankel_order_max,
1006 Outer_radius * k,
1008 &jv[0],
1009 &yv[0],
1010 &djv[0],
1011 &dyv[0]);
1012
1013 // Assemble Hankel functions and their derivatives
1014 for (unsigned j = 0; j < N_terms + 1; j++)
1015 {
1016 h_a[j] = jv[j] + I * yv[j];
1017 hp_a[j] = djv[j] + I * dyv[j];
1018 }
1019
1020 // Precompute relevant terms in sum
1021 for (unsigned i = n_fourier_decomposed; i < N_terms; i++)
1022 {
1023 double n_1 =
1025 double n_2 =
1027
1028 q[i] = k * sqrt(MathematicalConstants::Pi / (2.0 * k * Outer_radius)) *
1029 (hp_a[i] - h_a[i] / (2.0 * k * Outer_radius)) *
1030 (2.0 * double(i) + 1.0) /
1031 (2.0 * sqrt(MathematicalConstants::Pi / (2.0 * k * Outer_radius)) *
1032 h_a[i]) *
1033 (n_1 / n_2);
1034 }
1035
1036 // first loop over elements e
1037 unsigned nel = this->nelement();
1038 for (unsigned e = 0; e < nel; e++)
1039 {
1040 // Get a pointer to element
1043 this->element_pt(e));
1044
1045 // Set the value of n_intpt
1046 const unsigned n_intpt = el_pt->integral_pt()->nweight();
1047
1048 // initialise gamma integral and its derivatives
1050 std::complex<double>(0.0, 0.0));
1052
1053 // Loop over the integration points
1054 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
1055 {
1056 // Allocate and initialise coordinate
1057 Vector<double> x(el_pt->dim() + 1, 0.0);
1058
1059 // Set the Vector to hold local coordinates
1060 Vector<double> s(el_pt->dim(), 0.0);
1061 for (unsigned i = 0; i < el_pt->dim(); i++)
1062 {
1063 s[i] = el_pt->integral_pt()->knot(ipt, i);
1064 }
1065
1066 // Get the coordinates of the integration point
1067 el_pt->interpolated_x(s, x);
1068
1069 // Polar angle
1070 double theta = atan2(x[0], x[1]);
1071
1072 // Elemental contribution to gamma integral and its derivative
1073 std::complex<double> gamma_con(0.0, 0.0);
1074 std::map<unsigned, std::complex<double>> d_gamma_con;
1075
1076 // loop over the Fourier terms
1077 for (unsigned nn = n_fourier_decomposed; nn < N_terms; nn++)
1078 {
1079 // Second loop over the elements
1080 // to evaluate the complete integral
1081 for (unsigned ee = 0; ee < nel; ee++)
1082 {
1084 dynamic_cast<
1086 this->element_pt(ee));
1087
1088 // contribution of the positive term in the sum
1089 eel_pt->compute_gamma_contribution(
1091
1092 unsigned n_node = eel_pt->nnode();
1093
1094 gamma_vector[ipt] += q[nn] * gamma_con;
1095 for (unsigned l = 0; l < n_node; l++)
1096 {
1097 // Add the contribution of the real local data
1099 l, eel_pt->u_index_fourier_decomposed_helmholtz().real());
1100
1101 if (local_unknown_real >= 0)
1102 {
1106 }
1107
1108 // Add the contribution of the imag local data
1110 l, eel_pt->u_index_fourier_decomposed_helmholtz().imag());
1111
1112 if (local_unknown_imag >= 0)
1113 {
1117 }
1118 } // end of loop over the node
1119 } // End of second loop over the elements
1120 } // End of loop over Fourier terms
1121 } // end of loop over integration point
1122
1123 // Store it in map
1124 Gamma_at_gauss_point[el_pt] = gamma_vector;
1125 D_Gamma_at_gauss_point[el_pt] = d_gamma_vector;
1126
1127 } // end of first loop over element
1128 }
1129
1130 //===========================================================================
1131 /// Constructor, takes the pointer to the "bulk" element and the face index.
1132 //===========================================================================
1133 template<class ELEMENT>
1136 const int& face_index)
1137 : FaceGeometry<ELEMENT>(), FaceElement()
1138 {
1139 // Let the bulk element build the FaceElement, i.e. setup the pointers
1140 // to its nodes (by referring to the appropriate nodes in the bulk
1141 // element), etc.
1143
1144 // Set up U_index_fourier_decomposedhelmholtz.
1145 U_index_fourier_decomposed_helmholtz = std::complex<unsigned>(0, 1);
1146
1147 // Cast to the appropriate FourierDecomposedHelmholtzEquation so that we can
1148 // find the index at which the variable is stored
1149 // We assume that the dimension of the full problem is the same
1150 // as the dimension of the node, if this is not the case you will have
1151 // to write custom elements, sorry
1154 if (eqn_pt == 0)
1155 {
1156 std::string error_string =
1157 "Bulk element must inherit from FourierDecomposedHelmholtzEquations.";
1158 throw OomphLibError(
1160 }
1161 // Otherwise read out the value
1162 else
1163 {
1164 // Read the index from the (cast) bulk element
1166 eqn_pt->u_index_fourier_decomposed_helmholtz();
1167 }
1168 }
1169} // namespace oomph
1170
1171#endif
e
Definition cfortran.h:571
static char t char * s
Definition cfortran.h:568
cstr elem_len * i
Definition cfortran.h:603
A Class for the derivatives of shape functions The class design is essentially the same as Shape,...
Definition shape.h:359
FaceElements are elements that coincide with the faces of higher-dimensional "bulk" elements....
Definition elements.h:4342
int & face_index()
Index of the face (a number that uniquely identifies the face in the element)
Definition elements.h:4630
void outer_unit_normal(const Vector< double > &s, Vector< double > &unit_normal) const
Compute outer unit normal at the specified local coordinate.
Definition elements.cc:6037
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,...
Definition elements.h:4501
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.
Definition elements.cc:6384
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...
Definition elements.h:4532
FiniteElement *& bulk_element_pt()
Pointer to higher-dimensional "bulk" element.
Definition elements.h:4739
double J_eulerian(const Vector< double > &s) const
Return the Jacobian of mapping from local to global coordinates at local position s....
Definition elements.cc:5273
FaceGeometry class definition: This policy class is used to allow construction of face elements that ...
Definition elements.h:5002
A general Finite Element class.
Definition elements.h:1317
Integral *const & integral_pt() const
Return the pointer to the integration scheme (const version)
Definition elements.h:1967
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...
Definition elements.h:2597
virtual void output(std::ostream &outfile)
Output the element data — typically the values at the nodes in a format suitable for post-processing.
Definition elements.h:3054
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...
virtual double interpolated_x(const Vector< double > &s, const unsigned &i) const
Return FE interpolated coordinate x[i] at local coordinate s.
Definition elements.cc:3992
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.
Definition elements.h:1436
unsigned dim() const
Return the spatial dimension of the element, i.e. the number of local coordinates required to paramet...
Definition elements.h:2615
unsigned nnode() const
Return the number of nodes.
Definition elements.h:2214
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 ...
Definition elements.h:2321
double dshape_eulerian(const Vector< double > &s, Shape &psi, DShape &dpsidx) const
Compute the geometric shape functions and also first derivatives w.r.t. global coordinates at local c...
Definition elements.cc:3328
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition elements.h:2179
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...
Definition elements.h:1985
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....
Definition elements.cc:5163
A class for elements that allow the approximation of the Sommerfeld radiation BC for Fourier decompos...
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...
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...
double global_power_contribution(std::ofstream &outfile)
Compute the element's contribution to the time-averaged radiated power over the artificial boundary....
void output(std::ostream &outfile, const unsigned &n_plot)
Output function – forward to broken version in FiniteElement until somebody decides what exactly they...
void output(FILE *file_pt, const unsigned &n_plot)
C-style output function – forward to broken version in FiniteElement until somebody decides what exac...
double shape_and_test(const Vector< double > &s, Shape &psi, Shape &test) const
Function to compute the test functions and to return the Jacobian of mapping between local and global...
FourierDecomposedHelmholtzBCElementBase(const FourierDecomposedHelmholtzBCElementBase &dummy)=delete
Broken copy constructor.
double zeta_nodal(const unsigned &n, const unsigned &k, const unsigned &i) const
Broken assignment operator.
virtual std::complex< unsigned > u_index_fourier_decomposed_helmholtz() const
Return the index at which the real/imag unknown value is stored.
std::complex< unsigned > U_index_fourier_decomposed_helmholtz
The index at which the real and imag part of the unknown is stored at the nodes.
double global_power_contribution()
Compute the element's contribution to the time-averaged radiated power over the artificial boundary.
FaceElement used to apply Sommerfeld radiation conditon via Dirichlet to Neumann map.
FourierDecomposedHelmholtzDtNMesh< ELEMENT > * outer_boundary_mesh_pt() const
Access function to mesh of all DtN boundary condition elements (needed to get access to gamma values)
void fill_in_generic_residual_contribution_fourier_decomposed_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...
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.
FourierDecomposedHelmholtzDtNBoundaryElement(FiniteElement *const &bulk_el_pt, const int &face_index)
Construct element from specification of bulk element and face index.
void fill_in_contribution_to_residuals(Vector< double > &residuals)
Add the element's contribution to its residual vector.
void complete_setup_of_dependencies()
Complete the setup of additional dependencies arising through the far-away interaction with other nod...
FourierDecomposedHelmholtzDtNMesh< ELEMENT > * Outer_boundary_mesh_pt
Pointer to mesh of all DtN boundary condition elements (needed to get access to gamma values)
void set_outer_boundary_mesh_pt(FourierDecomposedHelmholtzDtNMesh< ELEMENT > *mesh_pt)
Set mesh of all DtN boundary condition elements.
void compute_gamma_contribution(const double &theta, const unsigned &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....
================================================================= Mesh for DtN boundary condition ele...
std::map< FiniteElement *, Vector< std::complex< double > > > Gamma_at_gauss_point
Container to store the gamma integral for given Gauss point and element.
unsigned & n_terms()
Number of terms used in the computation of the gamma integral.
FourierDecomposedHelmholtzDtNMesh(const double &outer_radius, const unsigned &n_terms)
Constructor: Specify radius of outer boundary and number of terms used in the computation of the gamm...
Vector< std::complex< double > > & gamma_at_gauss_point(FiniteElement *el_pt)
Gamma integral evaluated at Gauss points for specified element.
void setup_gamma()
Compute and store the gamma integral at all integration points of the constituent elements.
unsigned N_terms
Number of terms used in the Gamma computation.
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::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.
A class for all isoparametric elements that solve the Helmholtz equations.
unsigned nexternal_data() const
Return the number of external data objects.
Definition elements.h:816
unsigned long eqn_number(const unsigned &ieqn_local) const
Return the global equation number corresponding to the ieqn_local-th local equation number.
Definition elements.h:691
static DenseMatrix< double > Dummy_matrix
Empty dense matrix used as a dummy argument to combined residual and jacobian functions in the case w...
Definition elements.h:227
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.
Definition elements.h:311
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....
Definition elements.cc:312
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.
A general mesh class.
Definition mesh.h:67
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
unsigned ndim() const
Return (Eulerian) spatial dimension of the node.
Definition nodes.h:1054
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition nodes.h:1060
An OomphLibError object which should be thrown when an run-time error is encountered....
A Class for shape functions. In simple cases, the shape functions have only one index that can be tho...
Definition shape.h:77
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
TAdvectionDiffusionReactionElement()
Constructor: Call constructors for TElement and AdvectionDiffusionReaction equations.
double plgndr2(const unsigned &l, const unsigned &m, const double &x)
Legendre polynomials depending on two parameters.
const double Pi
50 digits from maple
double Tol
Relative tolerance to within radius of points on DtN boundary are allowed to deviate from specified v...
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).