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