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-2025 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
51using namespace oomph;
52using namespace std;
53
54
55/////////////////////////////////////////////////////////////////////
56/////////////////////////////////////////////////////////////////////
57/////////////////////////////////////////////////////////////////////
58
59//===== start_of_namespace=============================================
60/// Namespace for the Helmholtz problem parameters
61//=====================================================================
62namespace 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)
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//=====================================================================
241template<class ELEMENT>
242class PMLProblem : public Problem
243{
244
245public:
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
291private:
292
293 /// Pointer to the refineable "bulk" mesh
294 RefineableTriangleMesh<ELEMENT>* Bulk_mesh_pt;
295
296#else
297
298private:
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
320
321 /// Pointer to the top left corner PML mesh
323
324 /// Pointer to the bottom right corner PML mesh
326
327 /// Pointer to the bottom left corner PML mesh
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//========================================================================
347template<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//========================================================================
548template<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//========================================================================
588template<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);
620er_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//========================================================================
639template<class ELEMENT>
640void 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 snprintf(filename, sizeof(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 snprintf(filename, sizeof(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 snprintf(filename, sizeof(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 snprintf(filename, sizeof(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 snprintf(filename, sizeof(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 // snprintf(filename, sizeof(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//============================================================================
746template<class ELEMENT>
748create_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//============================================================================
782template<class ELEMENT>
784create_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//============================================================================
813template<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//========================================================================
915int 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...
Problem class to demonstrate use of perfectly matched layers for Helmholtz problems.
Mesh * PML_bottom_left_mesh_pt
Pointer to the bottom left corner PML mesh.
void actions_before_newton_solve()
Update the problem specs before solve (empty)
void actions_before_adapt()
Actions before adapt: Wipe the PML meshes.
Mesh * PML_bottom_right_mesh_pt
Pointer to the bottom right corner PML mesh.
Mesh * PML_bottom_mesh_pt
Pointer to the bottom PML mesh.
ofstream Trace_file
Trace file.
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.
Mesh * PML_top_mesh_pt
Pointer to the top PML mesh.
Mesh * PML_top_right_mesh_pt
Pointer to the top right corner PML mesh.
void create_pml_meshes()
Create PML meshes.
Mesh * PML_right_mesh_pt
Pointer to the right PML mesh.
Mesh * PML_top_left_mesh_pt
Pointer to the top left corner PML mesh.
Mesh * PML_left_mesh_pt
Pointer to the left PML mesh.
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)
RefineableTriangleMesh< ELEMENT > * Bulk_mesh_pt
Pointer to the refineable "bulk" mesh.
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 ...
Namespace for the Helmholtz problem parameters.
void prescribed_incoming_flux(const Vector< double > &x, complex< double > &flux)
Flux (normal derivative) on the unit disk for a planar incoming wave.
double Wavenumber
Wavenumber (also known as k), k=omega/c.
std::complex< double > I(0.0, 1.0)
Imaginary unit.
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.