unstructured_two_d_helmholtz_scattering.cc
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// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 // Driver for a specific 2D Helmholtz problem with
31 // perfectly matched layer treatment for the exterior boundaries
32 
33 #include<fenv.h>
34 
35 
36 #include "math.h"
37 #include <complex>
38 
39 // Generic routines
40 #include "generic.h"
41 
42 // The Helmholtz equations
43 #include "helmholtz.h"
44 
45 // The pml Helmholtz equations
46 #include "pml_helmholtz.h"
47 
48 // The meshes needed in the PML constructions
49 #include "meshes/triangle_mesh.h"
50 #include "meshes/rectangular_quadmesh.h"
51 
52 // Get the Bessel functions
53 #include "oomph_crbond_bessel.h"
54 
55 using namespace oomph;
56 using namespace std;
57 
58 
59 /////////////////////////////////////////////////////////////////////
60 /////////////////////////////////////////////////////////////////////
61 /////////////////////////////////////////////////////////////////////
62 
63 //===== start_of_namespace=============================================
64 /// Namespace for the Helmholtz problem parameters
65 //=====================================================================
66 namespace GlobalParameters
67 {
68 
69  /// Wavenumber (also known as k), k=omega/c
70  double Wavenumber = sqrt(10.0);
71 
72  /// Square of the wavenumber (also known as k^2)
73  double K_squared = Wavenumber * Wavenumber;
74 
75  /// \short Number of terms used in the computation
76  /// of the exact solution
77  unsigned N_fourier=100;
78 
79  /// Imaginary unit
80  std::complex<double> I(0.0,1.0);
81 
82  /// \short Exact solution for scattered field
83  /// (vector returns real and impaginary parts).
84  void get_exact_u(const Vector<double>& x, Vector<double>& u)
85  {
86  // Switch to polar coordinates
87  double r;
88  r=sqrt(x[0]*x[0]+x[1]*x[1]);
89  double theta;
90  theta=atan2(x[1],x[0]);
91 
92  // Argument for Bessel/Hankel functions
93  double rr=Wavenumber*r;
94 
95  // Evaluate Bessel/Hankel functions
96  complex <double > u_ex(0.0,0.0);
97  Vector<double> jn(N_fourier+1), yn(N_fourier+1),
98  jnp(N_fourier+1), ynp(N_fourier+1);
99  Vector<double> jn_a(N_fourier+1),yn_a(N_fourier+1),
100  jnp_a(N_fourier+1), ynp_a(N_fourier+1);
101  Vector<complex<double> > h(N_fourier+1),h_a(N_fourier+1),
102  hp(N_fourier+1), hp_a(N_fourier+1);
103 
104  // We want to compute N_fourier terms but the function
105  // may return fewer than that.
106  int n_actual=0;
107  CRBond_Bessel::bessjyna(N_fourier,Wavenumber,n_actual,
108  &jn_a[0],&yn_a[0],
109  &jnp_a[0],&ynp_a[0]);
110 
111  // Shout if things went wrong
112 #ifdef PARANOID
113  if (n_actual!=int(N_fourier))
114  {
115  std::ostringstream error_stream;
116  error_stream << "CRBond_Bessel::bessjyna() only computed "
117  << n_actual << " rather than " << N_fourier
118  << " Bessel functions.\n";
119  throw OomphLibError(error_stream.str(),
120  OOMPH_CURRENT_FUNCTION,
121  OOMPH_EXCEPTION_LOCATION);
122  }
123 #endif
124 
125  // Evaluate Hankel at actual radius
126  Hankel_functions_for_helmholtz_problem::Hankel_first(N_fourier,rr,h,hp);
127 
128  // Evaluate Hankel at inner (unit) radius
129  Hankel_functions_for_helmholtz_problem::Hankel_first(N_fourier
130  ,Wavenumber,
131  h_a,hp_a);
132 
133  // Compute the sum: Separate the computation of the negative
134  // and positive terms
135  // ALH: The construction with the static cast to a double is to avoid
136  // a floating point exception when running unoptimised on my machine
137  for (unsigned i=0;i<N_fourier;i++)
138  {
139  u_ex-=pow(I,i)*h[i]*jnp_a[i]*pow(exp(I*theta),static_cast<double>(i))/hp_a[i];
140  }
141  for (unsigned i=1;i<N_fourier;i++)
142  {
143  u_ex-=pow(I,i)*h[i]*jnp_a[i]*pow(exp(-I*theta),static_cast<double>(i))/hp_a[i];
144  }
145 
146  // Get the real & imaginary part of the result
147  u[0]=real(u_ex);
148  u[1]=imag(u_ex);
149 
150  }// end of get_exact_u
151 
152 
153 
154  /// \short Flux (normal derivative) on the unit disk
155  /// for a planar incoming wave
156  void prescribed_incoming_flux(const Vector<double>& x,
157  complex<double>& flux)
158  {
159  // Switch to polar coordinates
160  double r;
161  r=sqrt(x[0]*x[0]+x[1]*x[1]);
162  double theta;
163  theta=atan2(x[1],x[0]);
164 
165  // Argument of the Bessel/Hankel fcts
166  double rr=Wavenumber*r;
167 
168  // Compute Bessel/Hankel functions
169  Vector<double> jn(N_fourier+1), yn(N_fourier+1),
170  jnp(N_fourier+1), ynp(N_fourier+1);
171 
172  // We want to compute N_fourier terms but the function
173  // may return fewer than that.
174  int n_actual=0;
175  CRBond_Bessel::bessjyna(N_fourier,rr,n_actual,&jn[0],&yn[0],
176  &jnp[0],&ynp[0]);
177 
178  // Shout if things went wrong...
179 #ifdef PARANOID
180  if (n_actual!=int(N_fourier))
181  {
182  std::ostringstream error_stream;
183  error_stream << "CRBond_Bessel::bessjyna() only computed "
184  << n_actual << " rather than " << N_fourier
185  << " Bessel functions.\n";
186  throw OomphLibError(error_stream.str(),
187  OOMPH_CURRENT_FUNCTION,
188  OOMPH_EXCEPTION_LOCATION);
189  }
190 #endif
191 
192  // Compute the sum: Separate the computation of the negative and
193  // positive terms
194  flux=std::complex<double>(0.0,0.0);
195  for (unsigned i=0;i<N_fourier;i++)
196  {
197  flux+=pow(I,i)*(Wavenumber)*pow(exp(I*theta),i)*jnp[i];
198  }
199  for (unsigned i=1;i<N_fourier;i++)
200  {
201  flux+=pow(I,i)*(Wavenumber)*pow(exp(-I*theta),i)*jnp[i];
202  }
203 
204  }// end of prescribed_incoming_flux
205 
206  class TestPMLMapping : public UniaxialPMLMapping
207  {
208 
209  public:
210 
211  /// Default constructor (empty)
213 
214  /// \short Overwrite the pure Pml mapping coefficient function to return the
215  /// coeffcients proposed by Bermudez et al
216  std::complex<double> dtransformed_nu_dnu(const double& nu,
217  const double& delta,
218  const double& wavenumber_squared,
219  const double& alpha_shift=0.0)
220  {
221  const double k = sqrt(wavenumber_squared);
222  return 1.0 + MathematicalConstants::I / k * (2.0/std::fabs(delta - nu));
223  }
224 
225  //
226  std::complex<double> transformed_nu(const double& nu,
227  const double& delta,
228  const double& wavenumber_squared,
229  const double& alpha_shift=0.0)
230  {
231  const double k = sqrt(wavenumber_squared);
232  return nu - MathematicalConstants::I/k * log(2.0 - std::fabs(nu/delta));
233  }
234 
235  };
236 
238 
239 } // end of namespace
240 
241 
242 /////////////////////////////////////////////////////////////////////
243 /////////////////////////////////////////////////////////////////////
244 /////////////////////////////////////////////////////////////////////
245 
246 //========= start_of_problem_class=====================================
247 /// Problem class to demonstrate use of perfectly matched layers
248 /// for Helmholtz problems.
249 //=====================================================================
250 template<class ELEMENT>
251 class PMLProblem : public Problem
252 {
253 
254 public:
255 
256  /// Constructor
257  PMLProblem();
258 
259  /// Destructor (empty)
261 
262  /// Update the problem specs before solve (empty)
264 
265  /// Update the problem specs after solve (empty)
267 
268  /// \short Doc the solution. DocInfo object stores flags/labels for where the
269  /// output gets written to
270  void doc_solution(DocInfo& doc_info);
271 
272  /// Create PML meshes
273  void create_pml_meshes();
274 
275  /// \short Create Helmholtz flux elements on boundary b of the Mesh pointed
276  /// to by bulk_mesh_pt and add them to the specified surface Mesh
277  void create_flux_elements(const unsigned &b, Mesh* const &bulk_mesh_pt,
278  Mesh* const & helmholtz_inner_boundary_mesh_pt);
279 
280  /// \short Create Helmholtz power elements on boundary b of the Mesh pointed
281  /// to by bulk_mesh_pt and add them to the specified surface Mesh
282  void create_power_elements(const unsigned &b, Mesh* const &bulk_mesh_pt,
283  Mesh* const & helmholtz_power_boundary_mesh_pt);
284 
285 #ifdef ADAPTIVE
286 
287  /// Actions before adapt: Wipe the PML meshes
288  void actions_before_adapt();
289 
290  /// Actions after adapt: Rebuild the PML meshes
291  void actions_after_adapt();
292 
293 
294 
295 #endif
296 
297 
298 #ifdef ADAPTIVE
299 
300 private:
301 
302  /// Pointer to the refineable "bulk" mesh
303  RefineableTriangleMesh<ELEMENT>* Bulk_mesh_pt;
304 
305 #else
306 
307 private:
308 
309  /// Pointer to the "bulk" mesh
310  TriangleMesh<ELEMENT>* Bulk_mesh_pt;
311 
312 #endif
313 
314 
315  /// Pointer to the right PML mesh
316  Mesh* PML_right_mesh_pt;
317 
318  /// Pointer to the top PML mesh
319  Mesh* PML_top_mesh_pt;
320 
321  /// Pointer to the left PML mesh
322  Mesh* PML_left_mesh_pt;
323 
324  /// Pointer to the bottom PML mesh
325  Mesh* PML_bottom_mesh_pt;
326 
327  /// Pointer to the top right corner PML mesh
328  Mesh* PML_top_right_mesh_pt;
329 
330  /// Pointer to the top left corner PML mesh
331  Mesh* PML_top_left_mesh_pt;
332 
333  /// Pointer to the bottom right corner PML mesh
334  Mesh* PML_bottom_right_mesh_pt;
335 
336  /// Pointer to the bottom left corner PML mesh
337  Mesh* PML_bottom_left_mesh_pt;
338 
339  /// \short Pointer to the mesh containing
340  /// the Helmholtz inner boundary condition elements
342 
343  /// Pointer to mesh of elements that compute the radiated power
345 
346  /// Trace file
347  ofstream Trace_file;
348 
349 }; // end of problem class
350 
351 
352 
353 //=======start_of_constructor=============================================
354 /// Constructor for Helmholtz problem
355 //========================================================================
356 template<class ELEMENT>
358 {
359 
360  // Open trace file
361  Trace_file.open("RESLT/trace.dat");
362 
363  // Create circle representing inner boundary
364  double a=1.0;
365  double x_c=0.0;
366  double y_c=0.0;
367  Circle* inner_circle_pt=new Circle(x_c,y_c,a);
368 
369  // Outer boundary
370  //---------------
371  TriangleMeshClosedCurve* outer_boundary_pt=0;
372 
373  unsigned n_segments = 20;
374  Vector<TriangleMeshCurveSection*> outer_boundary_line_pt(4);
375 
376  // Each polyline only has three vertices, provide storage for their
377  // coordinates
378  Vector<Vector<double> > vertex_coord(2);
379  for(unsigned i=0;i<2;i++)
380  {
381  vertex_coord[i].resize(2);
382  }
383 
384  // First polyline
385  vertex_coord[0][0]=-2.0;
386  vertex_coord[0][1]=-2.0;
387  vertex_coord[1][0]=-2.0;
388  vertex_coord[1][1]=2.0;
389 
390  // Build the 1st boundary polyline
391  unsigned boundary_id=2;
392  outer_boundary_line_pt[0] = new TriangleMeshPolyLine(vertex_coord,
393  boundary_id);
394 
395  // Second boundary polyline
396  vertex_coord[0][0]=-2.0;
397  vertex_coord[0][1]=2.0;
398  vertex_coord[1][0]=2.0;
399  vertex_coord[1][1]=2.0;
400 
401  // Build the 2nd boundary polyline
402  boundary_id=3;
403  outer_boundary_line_pt[1] = new TriangleMeshPolyLine(vertex_coord,
404  boundary_id);
405 
406  // Third boundary polyline
407  vertex_coord[0][0]=2.0;
408  vertex_coord[0][1]=2.0;
409  vertex_coord[1][0]=2.0;
410  vertex_coord[1][1]=-2.0;
411 
412  // Build the 3rd boundary polyline
413  boundary_id=4;
414  outer_boundary_line_pt[2] = new TriangleMeshPolyLine(vertex_coord,
415  boundary_id);
416 
417  // Fourth boundary polyline
418  vertex_coord[0][0]=2.0;
419  vertex_coord[0][1]=-2.0;
420  vertex_coord[1][0]=-2.0;
421  vertex_coord[1][1]=-2.0;
422 
423  // Build the 4th boundary polyline
424  boundary_id=5;
425  outer_boundary_line_pt[3] = new TriangleMeshPolyLine(vertex_coord,
426  boundary_id);
427 
428  // Create the triangle mesh polygon for outer boundary
429  outer_boundary_pt = new TriangleMeshPolygon(outer_boundary_line_pt);
430 
431  // Inner circular boundary
432  //------------------------
433  Vector<TriangleMeshCurveSection*> inner_boundary_line_pt(2);
434 
435  // The intrinsic coordinates for the beginning and end of the curve
436  double s_start = 0.0;
437  double s_end = MathematicalConstants::Pi;
438  boundary_id = 0;
439  inner_boundary_line_pt[0]=
440  new TriangleMeshCurviLine(inner_circle_pt,
441  s_start,
442  s_end,
443  n_segments,
444  boundary_id);
445 
446  // The intrinsic coordinates for the beginning and end of the curve
447  s_start = MathematicalConstants::Pi;
448  s_end = 2.0*MathematicalConstants::Pi;
449  boundary_id = 1;
450  inner_boundary_line_pt[1]=
451  new TriangleMeshCurviLine(inner_circle_pt,
452  s_start,
453  s_end,
454  n_segments,
455  boundary_id);
456 
457 
458  // Combine to hole
459  //----------------
460  Vector<TriangleMeshClosedCurve*> hole_pt(1);
461  Vector<double> hole_coords(2);
462  hole_coords[0]=0.0;
463  hole_coords[1]=0.0;
464  hole_pt[0]=new TriangleMeshClosedCurve(inner_boundary_line_pt,
465  hole_coords);
466 
467 
468  // Use the TriangleMeshParameters object for helping on the manage
469  // of the TriangleMesh parameters. The only parameter that needs to take
470  // is the outer boundary.
471  TriangleMeshParameters triangle_mesh_parameters(outer_boundary_pt);
472 
473  // Specify the closed curve using the TriangleMeshParameters object
474  triangle_mesh_parameters.internal_closed_curve_pt() = hole_pt;
475 
476  // Target element size in bulk mesh
477  triangle_mesh_parameters.element_area() = 0.05;
478 
479 #ifdef ADAPTIVE
480 
481  // Build adaptive "bulk" mesh
482  Bulk_mesh_pt=new RefineableTriangleMesh<ELEMENT>(triangle_mesh_parameters);
483 
484  // Create/set error estimator
485  Bulk_mesh_pt->spatial_error_estimator_pt()=new Z2ErrorEstimator;
486 
487  // Choose error tolerances to force some uniform refinement
488  Bulk_mesh_pt->min_permitted_error()=0.00004;
489  Bulk_mesh_pt->max_permitted_error()=0.0001;
490 
491 #else
492 
493  // Build "bulk" mesh
494  Bulk_mesh_pt=new TriangleMesh<ELEMENT>(triangle_mesh_parameters);
495 
496 #endif
497 
498  // Pointer to mesh containing the Helmholtz inner boundary condition
499  // elements. Specify outer radius
500  Helmholtz_inner_boundary_mesh_pt = new Mesh;
501 
502  // Create prescribed-flux elements from all elements that are
503  // adjacent to the inner boundary , but add them to a separate mesh.
504  create_flux_elements(0,Bulk_mesh_pt,Helmholtz_inner_boundary_mesh_pt);
505  create_flux_elements(1,Bulk_mesh_pt,Helmholtz_inner_boundary_mesh_pt);
506 
507  // Pointer to mesh containing the Helmholtz power condition
508  // elements.
509  Helmholtz_power_boundary_mesh_pt = new Mesh;
510 
511  // Create power elements from all elements that are
512  // adjacent to the inner boundary , but add them to a separate mesh.
513  create_power_elements(0,Bulk_mesh_pt,Helmholtz_power_boundary_mesh_pt);
514  create_power_elements(1,Bulk_mesh_pt,Helmholtz_power_boundary_mesh_pt);
515 
516  // Create the main triangular mesh
517  add_sub_mesh(Bulk_mesh_pt);
518 
519  // Create PML meshes and add them to the global mesh
520  create_pml_meshes();
521 
522  // Add the flux mesh
523  add_sub_mesh(Helmholtz_inner_boundary_mesh_pt);
524 
525  // Build the entire mesh from its submeshes
526  build_global_mesh();
527 
528  // Complete the build of all elements so they are fully functional
529  unsigned n_element = this->mesh_pt()->nelement();
530  for(unsigned e=0;e<n_element;e++)
531  {
532  // Upcast from GeneralisedElement to Pml Helmholtz bulk element
533  PMLHelmholtzEquations<2,AxisAlignedPMLElement<2> > *el_pt =
534  dynamic_cast<PMLHelmholtzEquations<2,AxisAlignedPMLElement<2> >*>(mesh_pt()->element_pt(e));
535 
536  if (el_pt!=0)
537  {
538  //Set the k_squared function pointer
539  el_pt->k_squared_pt() = &GlobalParameters::K_squared;
540  // Pass in a pointer to the class containing the PML mapping function
541  //el_pt->pml_mapping_pt() = GlobalParameters::Test_pml_mapping_pt;
542  }
543 
544  }
545 
546  // Setup equation numbering scheme
547  cout <<"Number of equations: " << assign_eqn_numbers() << std::endl;
548 
549 } // end of constructor
550 
551 
552 #ifdef ADAPTIVE
553 
554 //=====================start_of_actions_before_adapt======================
555 /// Actions before adapt: Wipe the mesh of face elements
556 //========================================================================
557 template<class ELEMENT>
559 {
560  // Before adapting the added PML meshes must be removed
561  // as they are not refineable and are to be rebuilt from the
562  // newly refined triangular mesh
563  delete PML_right_mesh_pt;
564  PML_right_mesh_pt=0;
565  delete PML_top_mesh_pt;
566  PML_top_mesh_pt=0;
567  delete PML_left_mesh_pt;
568  PML_left_mesh_pt=0;
569  delete PML_bottom_mesh_pt;
570  PML_bottom_mesh_pt=0;
571  delete PML_top_right_mesh_pt;
572  PML_top_right_mesh_pt=0;
573  delete PML_top_left_mesh_pt;
574  PML_top_left_mesh_pt=0;
575  delete PML_bottom_right_mesh_pt;
576  PML_bottom_right_mesh_pt=0;
577  delete PML_bottom_left_mesh_pt;
578  PML_bottom_left_mesh_pt=0;
579 
580  // Rebuild the Problem's global mesh from its various sub-meshes
581  // but first flush all its submeshes
582  flush_sub_meshes();
583 
584  // Then add the triangular mesh back
585  add_sub_mesh(Bulk_mesh_pt);
586 
587  // Rebuild the global mesh such that it now stores
588  // the triangular mesh only
589  rebuild_global_mesh();
590 
591 }// end of actions_before_adapt
592 
593 
594 //=====================start_of_actions_after_adapt=======================
595 /// Actions after adapt: Rebuild the face element meshes
596 //========================================================================
597 template<class ELEMENT>
599 {
600  create_inner_bc_elements(0,Bulk_mesh_pt,Helmholtz_power_boundary_mesh_pt);
601  create_inner_bc_elements(1,Bulk_mesh_pt,Helmholtz_power_boundary_mesh_pt);
602  // Build PML meshes and add them to the global mesh
603  create_pml_meshes();
604 
605  // Complete the build of all elements so they are fully functional
606 
607  // Loop over the entire mesh elements to set up element-specific
608  // things that cannot be handled by constructor
609  unsigned n_element = this->mesh_pt()->nelement();
610 
611  for(unsigned e=0;e<n_element;e++)
612  {
613  // Upcast from GeneralisedElement to PMLHelmholtz bulk element
614  PMLHelmholtzEquations<2,AxisAlignedPMLElement<2> > *el_pt =
615  dynamic_cast<PMLHelmholtzEquations<2,AxisAlignedPMLElement<2> >*>(mesh_pt()->element_pt(e));
616 
617  if (el_pt!=0)
618  {
619  //Set the k_squared double pointer
620  el_pt->k_squared_pt() = &GlobalParameters::K_squared;
621  }
622  }
623 
624  // Create prescribed-flux elements and BC elements
625  // from all elements that are adjacent to the boundaries and add them to
626  // Helmholtz_boundary_meshes
627  create_flux_elements(0,Bulk_mesh_pt,Helmholtz_inner_boundary_mesh_pt);
628  create_flux_elements(1,Bulk_mesh_pt,Helmholtz_inner_boundary_mesh_pt);
629 er_eleme
630  // Create power elements from all elements that are
631  // adjacent to the inner boundary , but add them to a separate mesh.
632  create_power_elements(0,Bulk_mesh_pt,Helmholtz_power_boundary_mesh_pt);
633  create_power_elements(1,Bulk_mesh_pt,Helmholtz_power_boundary_mesh_pt);
634 
635  // Add the flux mesh
636  add_sub_mesh(Helmholtz_inner_boundary_mesh_pt);
637 
638  // Rebuild the Problem's global mesh from its various sub-meshes
639  rebuild_global_mesh();
640 
641 }// end of actions_after_adapt
642 
643 #endif
644 
645 //=====================start_of_doc=======================================
646 /// Doc the solution: doc_info contains labels/output directory etc.
647 //========================================================================
648 template<class ELEMENT>
649 void PMLProblem<ELEMENT>::doc_solution(DocInfo& doc_info)
650 {
651 
652  ofstream some_file,some_file2;
653  char filename[100];
654 
655  // Number of plot points
656  unsigned npts;
657  npts=5;
658 
659  // Compute/output the radiated power
660  //----------------------------------
661  sprintf(filename,"%s/power%i.dat",doc_info.directory().c_str(),
662  doc_info.number());
663  some_file.open(filename);
664 
665  // Accumulate contribution from elements
666  double power=0.0;
667  unsigned nn_element=Helmholtz_power_boundary_mesh_pt->nelement();
668  for(unsigned e=0;e<nn_element;e++)
669  {
670  PMLHelmholtzPowerElement<ELEMENT> *el_pt =
671  dynamic_cast<PMLHelmholtzPowerElement<ELEMENT>*>(
672  Helmholtz_power_boundary_mesh_pt->element_pt(e));
673  power += el_pt->global_power_contribution(some_file);
674  }
675  some_file.close();
676  oomph_info << "Total radiated power: " << power << std::endl;
677 
678  // Output solution
679  //-----------------
680  sprintf(filename,"%s/soln%i.dat",doc_info.directory().c_str(),
681  doc_info.number());
682  some_file.open(filename);
683  Bulk_mesh_pt->output(some_file,npts);
684  some_file.close();
685 
686 // Output exact solution
687 //----------------------
688  sprintf(filename,"%s/exact_soln%i.dat",doc_info.directory().c_str(),
689  doc_info.number());
690  some_file.open(filename);
691  Bulk_mesh_pt->output_fct(some_file,npts,GlobalParameters::get_exact_u);
692  some_file.close();
693 
694  double error,norm;
695  sprintf(filename,"%s/error%i.dat",doc_info.directory().c_str(),
696  doc_info.number());
697  some_file.open(filename);
698  Bulk_mesh_pt->compute_error(some_file,GlobalParameters::get_exact_u,
699  error,norm);
700  some_file.close();
701 
702  // Doc L2 error and norm of solution
703  oomph_info << "\nNorm of error : " << sqrt(error) << std::endl;
704  oomph_info << "Norm of solution: " << sqrt(norm) << std::endl << std::endl;
705 
706  // Output PML layers
707  //-----------------
708  sprintf(filename,"%s/pml_soln%i.dat",doc_info.directory().c_str(),
709  doc_info.number());
710  some_file.open(filename);
711  PML_right_mesh_pt->output(some_file,npts);
712  PML_left_mesh_pt->output(some_file,npts);
713  PML_top_mesh_pt->output(some_file,npts);
714  PML_bottom_mesh_pt->output(some_file,npts);
715  PML_bottom_right_mesh_pt->output(some_file,npts);
716  PML_top_right_mesh_pt->output(some_file,npts);
717  PML_bottom_left_mesh_pt->output(some_file,npts);
718  PML_top_left_mesh_pt->output(some_file,npts);
719  some_file.close();
720 
721 
722 
723  // Write norm of solution to trace file
724  double norm2=0.0;
725  Bulk_mesh_pt->compute_norm(norm2);
726  Trace_file << norm2 << std::endl;
727 
728  // //Do animation of Helmholtz solution
729  // //-----------------------------------
730  // unsigned nstep=40;
731  // for (unsigned i=0;i<nstep;i++)
732  // {
733  // sprintf(filename,"%s/helmholtz_animation%i_frame%i.dat",
734  // doc_info.directory().c_str(),
735  // doc_info.number(),i);
736  // some_file.open(filename);
737  // double phi=2.0*MathematicalConstants::Pi*double(i)/double(nstep-1);
738  // unsigned nelem=Bulk_mesh_pt->nelement();
739  // for (unsigned e=0;e<nelem;e++)
740  // {
741  // ELEMENT* el_pt=dynamic_cast<ELEMENT*>(
742  // Bulk_mesh_pt->element_pt(e));
743  // el_pt->output_real(some_file,phi,npts);
744  // }
745  // some_file.close();
746  // }
747 
748 } // end of doc
749 
750 //============start_of_create_flux_elements==================================
751 /// Create Helmholtz inner Flux Elements on the b-th boundary of
752 /// the Mesh object pointed to by bulk_mesh_pt and add the elements
753 /// to the Mesh object pointed to by helmholtz_inner_boundary_mesh_pt
754 //============================================================================
755 template<class ELEMENT>
757 create_flux_elements(const unsigned &b, Mesh* const &bulk_mesh_pt,
758  Mesh* const & helmholtz_inner_boundary_mesh_pt)
759 {
760  // Loop over the bulk elements adjacent to boundary b
761  unsigned n_element = bulk_mesh_pt->nboundary_element(b);
762  for(unsigned e=0;e<n_element;e++)
763  {
764  // Get pointer to the bulk element that is adjacent to boundary b
765  ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(
766  bulk_mesh_pt->boundary_element_pt(b,e));
767 
768  //Find the index of the face of element e along boundary b
769  int face_index = bulk_mesh_pt->face_index_at_boundary(b,e);
770 
771  // Build the corresponding prescribed incoming-flux element
772  PMLHelmholtzFluxElement<ELEMENT>* flux_element_pt = new
773  PMLHelmholtzFluxElement<ELEMENT>(bulk_elem_pt,face_index);
774 
775  //Add the prescribed incoming-flux element to the surface mesh
776  helmholtz_inner_boundary_mesh_pt->add_element_pt(flux_element_pt);
777 
778  // Set the pointer to the prescribed flux function
779  flux_element_pt->flux_fct_pt() =
781 
782  } //end of loop over bulk elements adjacent to boundary b
783 
784 } // end of create_flux_elements
785 
786 //============start_of_create_power_elements==================================
787 /// Create Helmholtz inner Flux Elements on the b-th boundary of
788 /// the Mesh object pointed to by bulk_mesh_pt and add the elements
789 /// to the Mesh object pointed to by helmholtz_power_boundary_mesh_pt
790 //============================================================================
791 template<class ELEMENT>
793 create_power_elements(const unsigned &b, Mesh* const &bulk_mesh_pt,
794  Mesh* const & helmholtz_power_boundary_mesh_pt)
795 {
796  // Loop over the bulk elements adjacent to boundary b
797  unsigned n_element = bulk_mesh_pt->nboundary_element(b);
798  for(unsigned e=0;e<n_element;e++)
799  {
800  // Get pointer to the bulk element that is adjacent to boundary b
801  ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(
802  bulk_mesh_pt->boundary_element_pt(b,e));
803 
804  //Find the index of the face of element e along boundary b
805  int face_index = bulk_mesh_pt->face_index_at_boundary(b,e);
806 
807  // Build the corresponding prescribed power element
808  PMLHelmholtzPowerElement<ELEMENT>* power_element_pt = new
809  PMLHelmholtzPowerElement<ELEMENT>(bulk_elem_pt,face_index);
810 
811  //Add the prescribed power element to the surface mesh
812  helmholtz_power_boundary_mesh_pt->add_element_pt(power_element_pt);
813 
814  } //end of loop over bulk elements adjacent to boundary b
815 
816 } // end of create_power_elements
817 
818 
819 //============start_of_create_pml_meshes======================================
820 /// Create PML meshes and add them to the problem's sub-meshes
821 //============================================================================
822 template<class ELEMENT>
824 {
825 
826  // Bulk mesh left boundary id
827  unsigned int left_boundary_id = 2;
828 
829  // Bulk mesh top boundary id
830  unsigned int top_boundary_id = 3;
831 
832  // Bulk mesh right boundary id
833  unsigned int right_boundary_id = 4;
834 
835  // Bulk mesh bottom boundary id
836  unsigned int bottom_boundary_id = 5;
837 
838  // PML width in elements for the right layer
839  unsigned n_x_right_pml = 3;
840 
841  // PML width in elements for the top layer
842  unsigned n_y_top_pml = 3;
843 
844  // PML width in elements for the left layer
845  unsigned n_x_left_pml = 3;
846 
847  // PML width in elements for the bottom layer
848  unsigned n_y_bottom_pml = 3;
849 
850  // Outer physical length of the PML layers
851  // defaults to 0.2, so 10% of the size of the
852  // physical domain
853  double width_x_right_pml = 0.2;
854  double width_y_top_pml = 0.2;
855  double width_x_left_pml = 0.2;
856  double width_y_bottom_pml = 0.2;
857 
858  // Build the PML meshes based on the new adapted triangular mesh
859  PML_right_mesh_pt =
860  TwoDimensionalPMLHelper::create_right_pml_mesh
861  <EquivalentQElement<ELEMENT> >
862  (Bulk_mesh_pt,right_boundary_id,
863  n_x_right_pml, width_x_right_pml);
864  PML_top_mesh_pt =
865  TwoDimensionalPMLHelper::create_top_pml_mesh
866  <EquivalentQElement<ELEMENT> >
867  (Bulk_mesh_pt, top_boundary_id,
868  n_y_top_pml, width_y_top_pml);
869  PML_left_mesh_pt =
870  TwoDimensionalPMLHelper::create_left_pml_mesh
871  <EquivalentQElement<ELEMENT> >
872  (Bulk_mesh_pt, left_boundary_id,
873  n_x_left_pml, width_x_left_pml);
874  PML_bottom_mesh_pt=
875  TwoDimensionalPMLHelper::create_bottom_pml_mesh
876  <EquivalentQElement<ELEMENT> >
877  (Bulk_mesh_pt, bottom_boundary_id,
878  n_y_bottom_pml, width_y_bottom_pml);
879 
880  // Add submeshes to the global mesh
881  add_sub_mesh(PML_right_mesh_pt);
882  add_sub_mesh(PML_top_mesh_pt);
883  add_sub_mesh(PML_left_mesh_pt);
884  add_sub_mesh(PML_bottom_mesh_pt);
885 
886  // Rebuild corner PML meshes
887  PML_top_right_mesh_pt =
888  TwoDimensionalPMLHelper::create_top_right_pml_mesh
889  <EquivalentQElement<ELEMENT> >
890  (PML_right_mesh_pt, PML_top_mesh_pt,
891  Bulk_mesh_pt, right_boundary_id);
892 
893  PML_bottom_right_mesh_pt =
894  TwoDimensionalPMLHelper::create_bottom_right_pml_mesh
895  <EquivalentQElement<ELEMENT> >
896  (PML_right_mesh_pt, PML_bottom_mesh_pt,
897  Bulk_mesh_pt, right_boundary_id);
898 
899  PML_top_left_mesh_pt =
900  TwoDimensionalPMLHelper::create_top_left_pml_mesh
901  <EquivalentQElement<ELEMENT> >
902  (PML_left_mesh_pt, PML_top_mesh_pt,
903  Bulk_mesh_pt, left_boundary_id);
904 
905  PML_bottom_left_mesh_pt =
906  TwoDimensionalPMLHelper::create_bottom_left_pml_mesh
907  <EquivalentQElement<ELEMENT> >
908  (PML_left_mesh_pt, PML_bottom_mesh_pt,
909  Bulk_mesh_pt, left_boundary_id);
910 
911  // Add submeshes to the global mesh
912  add_sub_mesh(PML_top_right_mesh_pt);
913  add_sub_mesh(PML_bottom_right_mesh_pt);
914  add_sub_mesh(PML_top_left_mesh_pt);
915  add_sub_mesh(PML_bottom_left_mesh_pt);
916 
917 } // end of create_pml_meshes
918 
919 
920 
921 //==========start_of_main=================================================
922 /// Solve 2D Helmholtz problem
923 //========================================================================
924 int main(int argc, char **argv)
925 {
926  //Set up the problem
927  //------------------
928 
929 #ifdef ADAPTIVE
930 
931  // Set up the problem with projectable 2D six-node elements from the
932  // TPMLHelmholtzElement family.
933  PMLProblem<ProjectablePMLHelmholtzElement
934  <TPMLHelmholtzElement<2,3,AxisAlignedPMLElement<2> > > > problem;
935 
936 #else
937 
938  // Set up the problem with 2D six-node elements from the
939  // TPMLHelmholtzElement family.
941 #endif
942 
943  // Create label for output
944  //------------------------
945  DocInfo doc_info;
946 
947  // Set output directory
948  doc_info.set_directory("RESLT");
949 
950 
951 #ifdef ADAPTIVE
952 
953  // Max. number of adaptations
954  unsigned max_adapt=1;
955 
956  // Solve the problem with the adaptive Newton's method, allowing
957  // up to max_adapt mesh adaptations after every solve.
958  problem.newton_solve(max_adapt);
959 
960 #else
961 
962  // Solve the problem with Newton's method
963  problem.newton_solve();
964 
965 #endif
966 
967  //Output solution
968  problem.doc_solution(doc_info);
969 
970 } //end of main
double K_squared
Square of the wavenumber (also known as k^2)
void actions_before_newton_solve()
Update the problem specs before solve (empty)
void create_flux_elements(const unsigned &b, Mesh *const &bulk_mesh_pt, Mesh *const &helmholtz_inner_boundary_mesh_pt)
Create Helmholtz flux elements on boundary b of the Mesh pointed to by bulk_mesh_pt and add them to t...
void create_pml_meshes()
Create PML meshes.
std::complex< double > transformed_nu(const double &nu, const double &delta, const double &wavenumber_squared, const double &alpha_shift=0.0)
std::complex< double > dtransformed_nu_dnu(const double &nu, const double &delta, const double &wavenumber_squared, const double &alpha_shift=0.0)
Overwrite the pure Pml mapping coefficient function to return the coeffcients proposed by Bermudez et...
double Wavenumber
Wavenumber (also known as k), k=omega/c.
void create_power_elements(const unsigned &b, Mesh *const &bulk_mesh_pt, Mesh *const &helmholtz_power_boundary_mesh_pt)
Create Helmholtz power elements on boundary b of the Mesh pointed to by bulk_mesh_pt and add them to ...
std::complex< double > I(0.0, 1.0)
Imaginary unit.
Namespace for the Helmholtz problem parameters.
void doc_solution(DocInfo &doc_info)
Doc the solution. DocInfo object stores flags/labels for where the output gets written to...
void actions_after_adapt()
Actions after adapt: Rebuild the PML meshes.
unsigned N_fourier
Number of terms used in the computation of the exact solution.
int main(int argc, char **argv)
Solve 2D Helmholtz problem.
Mesh * Helmholtz_inner_boundary_mesh_pt
Pointer to the mesh containing the Helmholtz inner boundary condition elements.
void actions_after_newton_solve()
Update the problem specs after solve (empty)
void actions_before_adapt()
Actions before adapt: Wipe the PML meshes.
void get_exact_u(const Vector< double > &x, Vector< double > &u)
Exact solution for scattered field (vector returns real and impaginary parts).
Mesh * Helmholtz_power_boundary_mesh_pt
Pointer to mesh of elements that compute the radiated power.
void prescribed_incoming_flux(const Vector< double > &x, complex< double > &flux)
Flux (normal derivative) on the unit disk for a planar incoming wave.