pml_meshes.h
Go to the documentation of this file.
1// LIC// ====================================================================
2// LIC// This file forms part of oomph-lib, the object-oriented,
3// LIC// multi-physics finite-element library, available
4// LIC// at http://www.oomph-lib.org.
5// LIC//
6// LIC// Copyright (C) 2006-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#ifndef OOMPH_PML_MESH_HEADER
27#define OOMPH_PML_MESH_HEADER
28
29// Config header
30#ifdef HAVE_CONFIG_H
31#include <oomph-lib-config.h>
32#endif
33
34#include "mesh.h"
35#include "../meshes/rectangular_quadmesh.h"
36
37
38namespace oomph
39{
40 //=======================================================================
41 /// General definition of policy class defining the elements to
42 /// be used in the actual PML layers. Has to be instantiated for
43 /// each specific "bulk" PML element type.
44 //=======================================================================
45 template<class ELEMENT>
47 {
48 };
49
50 ////////////////////////////////////////////////////////////////////////
51 ////////////////////////////////////////////////////////////////////////
52 ////////////////////////////////////////////////////////////////////////
53
54 //==============================================================
55 /// Base class for elements with pml capabilities
56 //==============================================================
57 template<unsigned DIM>
59 {
60 public:
61 /// Constructor
69
70 /// Virtual destructor
71 virtual ~PMLElementBase() {}
72
73 /// Disable pml. Ensures the PML-ification in all directions
74 /// has been deactivated
76 {
77 // Disable the PML-ification
78 Pml_is_enabled = false;
79
80 // Loop over the entries in Pml_direction_active and deactivate the
81 // PML in this direction
82 for (unsigned direction = 0; direction < DIM; direction++)
83 {
84 // Disable the PML in this direction
86 }
87 } // End of disable_pml
88
89 /// Enable pml. Specify the coordinate direction along which
90 /// pml boundary is constant, as well as the coordinate
91 /// along the dimension for the interface between the physical and
92 /// artificial domains and the coordinate for the outer boundary. All of
93 /// these are used to adjust the perfectly matched layer mechanism. Needs to
94 /// be called separately for each pml-ified direction (if needed -- e.g. in
95 /// corner elements)
105
106 /// Pure virtual function in which we have to specify the
107 /// values to be pinned (and set to zero) on the outer edge of
108 /// the pml layer. This is usually all of the nodal values
109 /// (values 0 and 1 (real and imag part) for Helmholtz;
110 /// values 0,1,2 and 3 (real and imag part of x- and y-displacement
111 /// for 2D time-harmonic linear elasticity; etc.). Vector
112 /// must be resized internally!
115
116 protected:
117 /// Boolean indicating if element is used in pml mode
119
120 /// Coordinate direction along which pml boundary is constant;
121 /// alternatively: coordinate direction in which coordinate stretching
122 /// is performed.
123 std::vector<bool> Pml_direction_active;
124
125 /// Coordinate of inner pml boundary
126 /// (Storage is provided for any coordinate
127 /// direction; only the entries for "active" directions is used.)
129
130 /// Coordinate of outer pml boundary
131 /// (Storage is provided for any coordinate
132 /// direction; only the entries for "active" directions is used.)
134 };
135
136 //////////////////////////////////////////////////////////////////////
137 //////////////////////////////////////////////////////////////////////
138 //////////////////////////////////////////////////////////////////////
139
140 //===================================================================
141 /// All helper routines for 2D bulk boundary mesh usage in order to
142 /// generate PML meshes aligned to the main mesh
143 //===================================================================
144 namespace TwoDimensionalPMLHelper
145 {
146 /// helper function for sorting the right boundary nodes
148
149 /// helper function for sorting the top boundary nodes
151
152 /// helper function for sorting the left boundary nodes
154
155 /// helper function for sorting the bottom boundary nodes
157
158 } // namespace TwoDimensionalPMLHelper
159
160 //////////////////////////////////////////////////////////////////
161 //////////////////////////////////////////////////////////////////
162 //////////////////////////////////////////////////////////////////
163
164 //====================================================================
165 /// PML mesh base class. Contains a pure virtual locate_zeta function
166 /// to be uploaded in PMLQuadMesh and PMLBrickMesh (once the code for
167 /// it has been written)
168 //====================================================================
170 {
171 public:
172 /// Pure virtual function to provide an optimised version of
173 /// the locate_zeta function for PML meshes. As the PML meshes are
174 /// rectangular (in 2D, or rectangular prisms in 3D) the location of
175 /// a point in the mesh is simpler than in a more complex mesh
176 virtual void pml_locate_zeta(const Vector<double>& x,
177 FiniteElement*& el_pt) = 0;
178 };
179
180 //====================================================================
181 /// PML mesh class. Policy class for 2D PML meshes
182 //====================================================================
183 template<class ELEMENT>
184 class PMLQuadMeshBase : public RectangularQuadMesh<ELEMENT>,
185 public PMLMeshBase
186 {
187 public:
188 /// Constructor: Create the underlying rectangular quad mesh
189 PMLQuadMeshBase(const unsigned& n_pml_x,
190 const unsigned& n_pml_y,
191 const double& x_pml_start,
192 const double& x_pml_end,
193 const double& y_pml_start,
194 const double& y_pml_end,
195 TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
196 : RectangularQuadMesh<ELEMENT>(n_pml_x,
197 n_pml_y,
199 x_pml_end,
201 y_pml_end,
202 time_stepper_pt)
203 {
204 }
205
206 /// Overloaded function to allow the user to locate an element
207 /// in mesh given the (Eulerian) position of a point. Note, the result
208 /// may be nonunique if the given point lies on the boundary of two
209 /// elements in the mesh. Additionally, we assume that the PML mesh is
210 /// axis aligned when deciding if the given point lies inside the mesh
213 {
214 //------------------------------------------
215 // Make sure the point lies inside the mesh:
216 //------------------------------------------
217 // Get the lower x limit of the mesh
218 const double x_min = this->x_min();
219
220 // Get the upper x limit of the mesh
221 const double x_max = this->x_max();
222
223 // Get the lower y limit of the mesh
224 const double y_min = this->y_min();
225
226 // Get the upper y limit of the mesh
227 const double y_max = this->y_max();
228
229 // Due to roundoff error we will choose a small tolerance which will be
230 // used to determine whether or not the point lies in the mesh
231 double tol = 1.0e-12;
232
233 // Assuming the mesh is axis-aligned we merely need to check if the
234 // x-coordinate of the input lies in the range [x_min,x_max] and the
235 // y-coordinate of the input lies in the range [y_min,y_max]
236 if ((x[0] < x_min - tol) || (x[0] > x_max + tol) ||
237 (x[1] < y_min - tol) || (x[1] > y_max + tol))
238 {
239 // Open an output string stream
240 std::ostringstream error_message_stream;
241
242 // Create an error message
243 error_message_stream << "Point does not lie in the mesh." << std::endl;
244
245 // Throw the error message
249 }
250
251 //-----------------------------------------
252 // Collect some information about the mesh:
253 //-----------------------------------------
254 // Find out how many elements there are in the x-direction
255 const unsigned nx = this->nx();
256
257 // Find out how many elements there are in the y-direction
258 const unsigned ny = this->ny();
259
260 // Find out how many nodes there are in one direction in each element
261 unsigned nnode_1d = this->finite_element_pt(0)->nnode_1d();
262
263 // Find out how many nodes there are in each element
264 unsigned nnode = this->finite_element_pt(0)->nnode();
265
266 // Create a pointer to store the element pointer as a FiniteElement
267 FiniteElement* el_pt = 0;
268
269 // Vector to store the coordinate of each element corner node which
270 // lies on the bottom boundary of the mesh and varies, i.e. if we're
271 // on the bottom boundary of the PML mesh then the y-coordinate will
272 // be fixed therefore we need only store the x-coordinates of the
273 // corner nodes of each element attached to this boundary
275
276 // Vector to store the coordinate of each element corner node which lies
277 // on the right boundary of the mesh and varies
279
280 // Recall, we already know the start and end coordinates of the mesh
281 // in the x-direction so we can assign these entries straight away.
282 // Note, these values are exactly the same as those had we instead
283 // considered the upper boundary so it is only necessary to store
284 // the information of the one of these boundaries. A similar argument
285 // holds for the left and right boundaries.
286
287 // The first entry of bottom_boundary_x_coordinates is:
289
290 // The last entry is:
292
293 // The first entry of right_boundary_y_coordinates is:
295
296 // The last entry is:
298
299 // To collect the remaining entries we need to loop over all of the
300 // elements on the bottom boundary and collect the x-coordinate of
301 // the bottom right node in the element. To avoid assigning the
302 // last entry twice we ignore the last element.
303
304 // Store the lower boundary ID
305 unsigned lower_boundary_id = 0;
306
307 // Store the right boundary ID
308 unsigned right_boundary_id = 1;
309
310 // Loop over the elements on the bottom boundary
311 for (unsigned i = 0; i < nx; i++)
312 {
313 // Assign the element pointer
314 el_pt = this->boundary_element_pt(lower_boundary_id, i);
315
316 // Store the x-coordinate of the bottom right node in this element
318 el_pt->node_pt(nnode_1d - 1)->x(0);
319 }
320
321 // To collect the remaining entries we need to loop over all of the
322 // elements on the right boundary and collect the y-coordinate of
323 // the top right node in the element. To avoid assigning the
324 // last entry twice we ignore the last element.
325
326 // Loop over the elements on the bottom boundary
327 for (unsigned i = 0; i < ny; i++)
328 {
329 // Assign the element pointer
330 el_pt = this->boundary_element_pt(right_boundary_id, i);
331
332 // Store the y-coordinate of the top right node in this element
334 }
335
336 //---------------------------
337 // Find the matching element:
338 //---------------------------
339 // Boolean variable to indicate that the ID of the element in the
340 // x-direction has been found. Note, the value of this ID must lie
341 // in the range [0,nx]
342 bool element_x_id_has_been_found = false;
343
344 // Boolean variable to indicate that the ID of the element in the
345 // y-direction has been found. Note, the value of this ID must lie
346 // in the range [0,ny]
347 bool element_y_id_has_been_found = false;
348
349 // Initialise the ID of the element in the x-direction
350 unsigned element_x_id = 0;
351
352 // Initialise the ID of the element in the y-direction
353 unsigned element_y_id = 0;
354
355 // Loop over all of the entries in bottom_boundary_x_coordinates
356 for (unsigned i = 0; i < nx; i++)
357 {
358 // Check if the x-coordinate of the input lies in this interval
359 if ((x[0] >= bottom_boundary_x_coordinates[i]) &&
360 (x[0] <= bottom_boundary_x_coordinates[i + 1]))
361 {
362 // The point lies in the i-th interval so the element ID is i
363 element_x_id = i;
364
365 // Indicate that the element ID has been found
367 }
368 } // for (unsigned i=0;i<nx;i++)
369
370 // If the element ID hasn't been found
372 {
373 // Open an output string stream
374 std::ostringstream error_message_stream;
375
376 // Create an error message
377 error_message_stream << "The ID of the element in the x-direction "
378 << "has not been found." << std::endl;
379
380 // Throw the error message
384 }
385
386 // Loop over all of the entries in right_boundary_y_coordinates
387 for (unsigned i = 0; i < ny; i++)
388 {
389 // Check if the y-coordinate of the input lies in this interval
390 if ((x[1] >= right_boundary_y_coordinates[i]) &&
391 (x[1] <= right_boundary_y_coordinates[i + 1]))
392 {
393 // The point lies in the i-th interval so the element ID is i
394 element_y_id = i;
395
396 // Indicate that the element ID has been found
398 }
399 } // for (unsigned i=0;i<ny;i++)
400
401 // If the element ID hasn't been found
403 {
404 // Open an output string stream
405 std::ostringstream error_message_stream;
406
407 // Create an error message
408 error_message_stream << "The ID of the element in the y-direction "
409 << "has not been found." << std::endl;
410
411 // Throw the error message
415 }
416
417 // Calculate the element number in the mesh
418 unsigned el_id = element_y_id * nx + element_x_id;
419
420 // Set the pointer to this element
421 coarse_mesh_el_pt = dynamic_cast<FiniteElement*>(this->element_pt(el_id));
422 } // End of pml_locate_zeta
423 };
424
425 //====================================================================
426 /// PML mesh, derived from RectangularQuadMesh.
427 //====================================================================
428 template<class ELEMENT>
429 class PMLQuadMesh : public PMLQuadMeshBase<ELEMENT>
430 {
431 public:
432 /// Constructor: Pass pointer to "bulk" mesh,
433 /// the boundary ID of axis aligned boundary to which the
434 /// mesh is supposed to be attached and the boundary ID in the
435 /// rectangular quad mesh that contains the pml elements.
436 /// 0: constant y, bulk mesh below;
437 /// 1: constant x, bulk mesh to the right;
438 /// 2: constant y, bulk mesh above;
439 /// 3: constant x, bulk mesh to left.
441 const unsigned& boundary_id,
442 const unsigned& quad_boundary_id,
443 const unsigned& n_pml_x,
444 const unsigned& n_pml_y,
445 const double& x_pml_start,
446 const double& x_pml_end,
447 const double& y_pml_start,
448 const double& y_pml_end,
449 TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
450 : PMLQuadMeshBase<ELEMENT>(n_pml_x,
451 n_pml_y,
453 x_pml_end,
455 y_pml_end,
456 time_stepper_pt)
457 {
458 unsigned n_boundary_node = bulk_mesh_pt->nboundary_node(boundary_id);
459
460 // Create a vector of ordered boundary nodes
462
463 // Fill the vector with the nodes on the respective boundary
464 for (unsigned n = 0; n < n_boundary_node; n++)
465 {
467 bulk_mesh_pt->boundary_node_pt(boundary_id, n);
468 }
469
470 /// Sort them depending on the boundary being used
471
472 // Right boundary
473 if (quad_boundary_id == 3)
474 {
475 std::sort(ordered_boundary_node_pt.begin(),
478 }
479
480 /// Top boundary
481 if (quad_boundary_id == 0)
482 {
483 std::sort(ordered_boundary_node_pt.begin(),
486 }
487
488 /// Left boundary
489 if (quad_boundary_id == 1)
490 {
491 std::sort(ordered_boundary_node_pt.begin(),
494 }
495
496 /// Bottom boundary
497 if (quad_boundary_id == 2)
498 {
499 std::sort(ordered_boundary_node_pt.begin(),
502 }
503
504 unsigned nnode_1d = this->finite_element_pt(0)->nnode_1d();
505
506 /// Simple interior node numbering helpers
507 /// to be precomputed before the main loop
508
509 /// Top left node in element
510 unsigned interior_node_nr_helper_1 = nnode_1d * (nnode_1d - 1);
511 /// Lower right node in element
512 unsigned interior_node_nr_helper_2 = nnode_1d - 1;
513 /// Used to find nodes in top row
514 unsigned interior_node_nr_helper_3 = nnode_1d * (nnode_1d - 1) - 1;
515
516 /// Set all nodes from the PML mesh that must disappear
517 /// after merging as obsolete
518 unsigned nnod = this->nboundary_node(quad_boundary_id);
519 for (unsigned j = 0; j < nnod; j++)
520 {
521 this->boundary_node_pt(quad_boundary_id, j)->set_obsolete();
522 }
523
524 // Kill the obsolete nodes
525 this->prune_dead_nodes();
526
527 // Find the number of elements inside the PML mesh
528 unsigned n_pml_element = this->nelement();
529
530 /// Simple interior element numbering helpers
531
532 /// Last element in mesh (top right)
534
535
536 // Connect the elements in the pml mesh to the ones
537 // in the triangular mesh at element level
538 unsigned count = 0;
539
540 // Each boundary requires a specific treatment
541 // Right boundary
542 if (quad_boundary_id == 3)
543 {
544 for (unsigned e = 0; e < n_pml_element; e++)
545 {
546 // If element is on the right boundary
547 if ((e % n_pml_x) == 0)
548 {
549 // Upcast from GeneralisedElement to bulk element
550 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
551
552 // Loop over all nodes in element
553 unsigned n_node = el_pt->nnode();
554 for (unsigned inod = 0; inod < n_node; inod++)
555 {
556 if (inod % nnode_1d == 0)
557 {
558 // Get the pointer from the triangular mesh
560 count++;
561
562 // Node between two elements
564 {
565 count--;
566 }
567 }
568 }
569 }
570 }
571 }
572
573 // Top boundary
574 if (quad_boundary_id == 0)
575 {
576 for (unsigned e = 0; e < n_pml_element; e++)
577 {
578 // If element is on the right boundary
579 if ((int)(e / n_pml_x) == 0)
580 {
581 // Upcast from GeneralisedElement to bulk element
582 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
583
584 // Loop over all nodes in element
585 unsigned n_node = el_pt->nnode();
586 for (unsigned inod = 0; inod < n_node; inod++)
587 {
588 if ((int)(inod / nnode_1d) == 0)
589 {
590 // Get the pointer from the triangular mesh
592 count++;
593
594 // Node between two elements
596 {
597 count--;
598 }
599 }
600 }
601 }
602 }
603 }
604
605 // Left boundary
606 if (quad_boundary_id == 1)
607 {
608 for (unsigned e = interior_element_nr_helper_1; e < n_pml_element; e--)
609 {
610 // If element is on the right boundary
611 if ((e % n_pml_x) == (n_pml_x - 1))
612 {
613 // Upcast from GeneralisedElement to bulk element
614 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
615
616 // Loop over all nodes in element
617 unsigned n_node = el_pt->nnode();
618 unsigned starter = n_node - 1;
619 for (unsigned inod = starter; inod <= starter; inod--)
620 {
621 if (inod % nnode_1d == interior_node_nr_helper_2)
622 {
623 // Get the pointer from the triangular mesh
625 count++;
626
627 // Node between two elements
629 {
630 count--;
631 }
632 }
633 }
634 }
635 }
636 }
637
638 // Bottom boundary
639 if (quad_boundary_id == 2)
640 {
641 for (unsigned e = interior_element_nr_helper_1; e < n_pml_element; e--)
642 {
643 // If element is on the top boundary
644 if (e >= (n_pml_x * (n_pml_y - 1)))
645 {
646 // Upcast from GeneralisedElement to bulk element
647 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
648
649 // Loop over all nodes in element
650 unsigned n_node = el_pt->nnode();
651 unsigned starter = n_node - 1;
652 for (unsigned inod = starter; inod <= starter; inod--)
653 {
655 {
656 // Get the pointer from the triangular mesh
658 count++;
659
660 // Node between two elements
662 {
663 count--;
664 }
665 }
666 }
667 }
668 }
669 }
670
671 /// The alignment is done individually for each boundary
672 /// and depends on the ordering of the nodes, in this case of the
673 /// RectangularQuadMesh<2,3> for each boundary. There are operations
674 /// with mod and div 3 necessary in this case, as well as specific
675 /// mechanisms to loop over the boundary in a certain way in order
676 /// to obtain the convenient coordinates.
677
678 // Loop over all elements and make sure the coordinates are aligned
679 for (unsigned e = 0; e < n_pml_element; e++)
680 {
681 // Upcast from GeneralisedElement to bulk element
682 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
683 unsigned n_node = el_pt->nnode();
684
685 // Loop over all nodes in element
686 double temp_coordinate = 0.0;
687 for (unsigned inod = 0; inod < n_node; inod++)
688 {
689 // Check if we are looking at the left boundary of the quad mesh
690 if (quad_boundary_id == 3)
691 {
692 // If it is one of the ones on the left boundary
693 if (inod % nnode_1d == 0)
694 {
695 // Get the y-coordinate of the leftmost node in that element
697 }
698
699 // Each node's y-coordinate is reset to be the one of the leftmost
700 // node in that element on its x-coordinate
702 }
703 // End of left quad boundary check
704
705 // Check if we are looking at the top boundary of the quad mesh
706 if (quad_boundary_id == 0)
707 {
708 // If it is one of the ones on the bottom boundary
710 {
711 // Get the y-coordinate of the leftmost node in that element
712 el_pt->node_pt(inod)->x(0) =
713 el_pt->node_pt(inod - nnode_1d)->x(0);
714 }
715 }
716 // End of top quad boundary check
717 }
718 }
719
720 for (unsigned e = interior_element_nr_helper_1; e < n_pml_element; e--)
721 {
722 // Upcast from GeneralisedElement to bulk element
723 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
724 unsigned n_node = el_pt->nnode();
725
726 // Loop over all nodes in element
727 double temp_coordinate = 0.0;
728 unsigned starter = n_node - 1;
729 for (unsigned inod = starter; inod <= starter; inod--)
730 {
731 // Check if we are looking at the right boundary of the quad mesh
732 if (quad_boundary_id == 1)
733 {
734 // If it is one of the ones on the left boundary
735 if (inod % nnode_1d == interior_node_nr_helper_2)
736 {
737 // Get the y-coordinate of the leftmost node in that element
739 }
740
741 // Each node's y-coordinate is reset to be the one of the leftmost
742 // node in that element on its x-coordinate
744 }
745 // End of right quad boundary check
746
747 // Check if we are looking at the top boundary of the quad mesh
748 if (quad_boundary_id == 2)
749 {
750 // If it is one of the ones on the bottom boundary
752 {
753 // Get the y-coordinate of the leftmost node in that element
754 el_pt->node_pt(inod)->x(0) =
755 el_pt->node_pt(inod + nnode_1d)->x(0);
756 }
757 }
758 // End of top quad boundary check
759 }
760 }
761 // End of alignment
762 }
763 };
764
765
766 //////////////////////////////////////////////////////////////////////
767 //////////////////////////////////////////////////////////////////////
768 //////////////////////////////////////////////////////////////////////
769
770
771 //====================================================================
772 /// PML mesh, derived from RectangularQuadMesh.
773 //====================================================================
774 template<class ELEMENT>
775 class PMLCornerQuadMesh : public PMLQuadMeshBase<ELEMENT>
776 {
777 public:
778 /// Constructor: Pass pointer to "bulk" mesh
779 /// and the two existing PML meshes in order to construct the corner
780 /// PML mesh in between them based on their element number
781 /// and coordinates.
786 const unsigned& parent_boundary_x_id,
787 const unsigned& parent_boundary_y_id,
788 const unsigned& current_boundary_x_id,
789 const unsigned& current_boundary_y_id,
790 const unsigned& n_pml_x,
791 const unsigned& n_pml_y,
792 const double& x_pml_start,
793 const double& x_pml_end,
794 const double& y_pml_start,
795 const double& y_pml_end,
796 TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
797 : PMLQuadMeshBase<ELEMENT>(n_pml_x,
798 n_pml_y,
800 x_pml_end,
802 y_pml_end,
803 time_stepper_pt)
804 {
805 unsigned nnode_1d = this->finite_element_pt(0)->nnode_1d();
806
807 /// Simple interior node numbering helpers
808 /// to be precomputed before the main loop
809
810 /// Top left node in element
811 unsigned interior_node_nr_helper_1 = nnode_1d * (nnode_1d - 1);
812 /// Lower right node in element
813 unsigned interior_node_nr_helper_2 = nnode_1d - 1;
814 /// Top right node in element
815 unsigned interior_node_nr_helper_3 = nnode_1d * nnode_1d - 1;
816
817 // Set up top right corner element
818 //--------------------------------
819 if ((parent_boundary_x_id == 2) && (parent_boundary_y_id == 1))
820 {
821 // Get the number of nodes to be connected on the horizontal boundary
822 unsigned n_boundary_x_node =
824
825 // Create a vector of ordered boundary nodes
827
828 // Fill the vector with the nodes on the respective boundary
829 for (unsigned n = 0; n < n_boundary_x_node; n++)
830 {
832 PMLQuad_mesh_x_pt->boundary_node_pt(parent_boundary_x_id, n);
833 }
834
835 // Sort them from lowest to highest (in x coordinate)
836 if (parent_boundary_x_id == 2)
837 {
838 std::sort(ordered_boundary_x_node_pt.begin(),
841 }
842
843 // Get the number of nodes to be connected on the vertical boundary
844 unsigned n_boundary_y_node =
846
847 // Create a vector of ordered boundary nodes
849
850 // Fill the vector with the nodes on the respective boundary
851 for (unsigned n = 0; n < n_boundary_y_node; n++)
852 {
854 PMLQuad_mesh_y_pt->boundary_node_pt(parent_boundary_y_id, n);
855 }
856
857 // Sort them
858 if (parent_boundary_y_id == 1)
859 {
860 std::sort(ordered_boundary_y_node_pt.begin(),
863 }
864
865 unsigned x_nnod = this->nboundary_node(current_boundary_x_id);
866 for (unsigned j = 0; j < x_nnod; j++)
867 {
868 this->boundary_node_pt(current_boundary_x_id, j)->set_obsolete();
869 }
870
871 unsigned y_nnod = this->nboundary_node(current_boundary_y_id);
872 for (unsigned j = 0; j < y_nnod; j++)
873 {
874 this->boundary_node_pt(current_boundary_y_id, j)->set_obsolete();
875 }
876
877 // Kill the obsolete nodes
878 this->prune_dead_nodes();
879
880 unsigned n_pml_element = this->nelement();
881
882 // Connect the elements in the pml mesh to the ones
883 // in the triangular mesh at element level
884 unsigned count = 0;
885
886 if (parent_boundary_y_id == 1)
887 {
888 for (unsigned e = 0; e < n_pml_element; e++)
889 {
890 // If element is on the right boundary
891 if ((e % n_pml_x) == 0)
892 {
893 // Upcast from GeneralisedElement to bulk element
894 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
895
896 // Loop over all nodes in element
897 unsigned n_node = el_pt->nnode();
898 for (unsigned inod = 0; inod < n_node; inod++)
899 {
900 // If it is one of the ones on the left boundary
901 if (e == 0)
902 {
904 if ((inod % nnode_1d == 0) && (inod > 0))
905 {
906 // Get the pointer from the triangular mesh
908 count++;
909
910 // Node between two elements
912 {
913 count--;
914 }
915 }
916 }
917 else
918 {
919 if ((inod % nnode_1d) == 0)
920 {
921 // Get the pointer from the triangular mesh
923 count++;
924
925 // Node between two elements
927 {
928 count--;
929 }
930 }
931 }
932 }
933 }
934 }
935 }
936
937 count = 0;
938
939 if (parent_boundary_x_id == 2)
940 {
941 for (unsigned e = 0; e < n_pml_element; e++)
942 {
943 // If element is on the right boundary
944 if ((int)(e / n_pml_x) == 0)
945 {
946 // Upcast from GeneralisedElement to bulk element
947 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
948
949 // Loop over all nodes in element
950 unsigned n_node = el_pt->nnode();
951 for (unsigned inod = 0; inod < n_node; inod++)
952 {
953 if (e == 0)
954 {
955 if (((int)(inod / nnode_1d) == 0) && (inod > 0))
956 {
957 // Get the pointer from the triangular mesh
959 count++;
960
961 // Node between two elements
963 {
964 count--;
965 }
966 }
967 }
968 else
969 {
970 if ((int)(inod / nnode_1d) == 0)
971 {
972 // Get the pointer from the triangular mesh
974 count++;
975
976 // Node between two elements
978 {
979 count--;
980 }
981 }
982 }
983 }
984 }
985 }
986 }
987 }
988
989 // Set up bottom right corner element
990 //--------------------------------
991 if ((parent_boundary_x_id == 0) && (parent_boundary_y_id == 1))
992 {
993 // Get the number of nodes to be connected on the horizontal boundary
994 unsigned n_boundary_x_node =
996
997 // Create a vector of ordered boundary nodes
999
1000 // Fill the vector with the nodes on the respective boundary
1001 for (unsigned n = 0; n < n_boundary_x_node; n++)
1002 {
1004 PMLQuad_mesh_x_pt->boundary_node_pt(parent_boundary_x_id, n);
1005 }
1006
1007 // Sort them from lowest to highest (in x coordinate)
1008 if (parent_boundary_x_id == 0)
1009 {
1010 std::sort(ordered_boundary_x_node_pt.begin(),
1013 }
1014
1015 // Get the number of nodes to be connected on the vertical boundary
1016 unsigned n_boundary_y_node =
1017 PMLQuad_mesh_y_pt->nboundary_node(parent_boundary_y_id);
1018
1019 // Create a vector of ordered boundary nodes
1021
1022 // Fill the vector with the nodes on the respective boundary
1023 for (unsigned n = 0; n < n_boundary_y_node; n++)
1024 {
1026 PMLQuad_mesh_y_pt->boundary_node_pt(parent_boundary_y_id, n);
1027 }
1028
1029 // Sort them
1030 if (parent_boundary_y_id == 1)
1031 {
1032 std::sort(ordered_boundary_y_node_pt.begin(),
1035 }
1036
1037 unsigned x_nnod = this->nboundary_node(current_boundary_x_id);
1038 for (unsigned j = 0; j < x_nnod; j++)
1039 {
1040 this->boundary_node_pt(current_boundary_x_id, j)->set_obsolete();
1041 }
1042
1043 unsigned y_nnod = this->nboundary_node(current_boundary_y_id);
1044 for (unsigned j = 0; j < y_nnod; j++)
1045 {
1046 this->boundary_node_pt(current_boundary_y_id, j)->set_obsolete();
1047 }
1048
1049 // Kill the obsolete nodes
1050 this->prune_dead_nodes();
1051
1052 // Get the number of elements in the PML mesh
1053 unsigned n_pml_element = this->nelement();
1054
1055 // Connect the elements in the pml mesh to the ones
1056 // in the triangular mesh at element level
1057 unsigned count = 0;
1058
1059 if (parent_boundary_y_id == 1)
1060 {
1061 for (unsigned e = 0; e < n_pml_element; e++)
1062 {
1063 // If element is on the right boundary
1064 if ((e % n_pml_x) == 0)
1065 {
1066 // Upcast from GeneralisedElement to bulk element
1067 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
1068
1069 // Loop over all nodes in element
1070 unsigned n_node = el_pt->nnode();
1071 for (unsigned inod = 0; inod < n_node; inod++)
1072 {
1073 if (e == ((n_pml_x) * (n_pml_y - 1)))
1074 {
1076 {
1078 }
1079 if ((inod % nnode_1d == 0) &&
1081 {
1082 // Get the pointer from the triangular mesh
1084 count++;
1085
1086 // Node between two elements
1088 {
1089 count--;
1090 }
1091 }
1092 }
1093 else
1094 {
1095 if ((inod % nnode_1d) == 0)
1096 {
1097 // Get the pointer from the triangular mesh
1099 count++;
1100
1101 // Node between two elements
1103 {
1104 count--;
1105 }
1106 }
1107 }
1108 }
1109 }
1110 }
1111 }
1112
1113 count = 0;
1114
1115 if (parent_boundary_x_id == 0)
1116 {
1117 for (unsigned e = 0; e < n_pml_element; e++)
1118 {
1119 // If element is on the right boundary
1120 if (e >= ((n_pml_x - 0) * (n_pml_y - 1)))
1121 {
1122 // Upcast from GeneralisedElement to bulk element
1123 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
1124
1125 // Loop over all nodes in element
1126 unsigned n_node = el_pt->nnode();
1127 for (unsigned inod = 0; inod < n_node; inod++)
1128 {
1129 if (e == ((n_pml_x) * (n_pml_y - 1)))
1130 {
1131 if (((unsigned)(inod / nnode_1d) ==
1134 {
1135 // Get the pointer from the triangular mesh
1137 count++;
1138
1139 // Node between two elements
1141 {
1142 count--;
1143 }
1144 }
1145 }
1146 else
1147 {
1148 if ((unsigned)(inod / nnode_1d) == interior_node_nr_helper_2)
1149 {
1150 // Get the pointer from the triangular mesh
1152 count++;
1153
1154 // Node between two elements
1156 {
1157 count--;
1158 }
1159 }
1160 }
1161 }
1162 }
1163 }
1164 }
1165 }
1166
1167 // Set up top left corner element
1168 //--------------------------------
1169 if ((parent_boundary_x_id == 2) && (parent_boundary_y_id == 3))
1170 {
1171 // Get the number of nodes to be connected on the horizontal boundary
1172 unsigned n_boundary_x_node =
1173 PMLQuad_mesh_x_pt->nboundary_node(parent_boundary_x_id);
1174
1175 // Create a vector of ordered boundary nodes
1177
1178 // Fill the vector with the nodes on the respective boundary
1179 for (unsigned n = 0; n < n_boundary_x_node; n++)
1180 {
1182 PMLQuad_mesh_x_pt->boundary_node_pt(parent_boundary_x_id, n);
1183 }
1184
1185 // Sort them from lowest to highest (in x coordinate)
1186 if (parent_boundary_x_id == 2)
1187 {
1188 std::sort(ordered_boundary_x_node_pt.begin(),
1191 }
1192
1193 // Get the number of nodes to be connected on the vertical boundary
1194 unsigned n_boundary_y_node =
1195 PMLQuad_mesh_y_pt->nboundary_node(parent_boundary_y_id);
1196
1197 // Create a vector of ordered boundary nodes
1199
1200 // Fill the vector with the nodes on the respective boundary
1201 for (unsigned n = 0; n < n_boundary_y_node; n++)
1202 {
1204 PMLQuad_mesh_y_pt->boundary_node_pt(parent_boundary_y_id, n);
1205 }
1206
1207 // Sort them from lowest to highest (in x coordinate)
1208 if (parent_boundary_y_id == 1)
1209 {
1210 std::sort(ordered_boundary_y_node_pt.begin(),
1213 }
1214
1215 unsigned x_nnod = this->nboundary_node(current_boundary_x_id);
1216 for (unsigned j = 0; j < x_nnod; j++)
1217 {
1218 this->boundary_node_pt(current_boundary_x_id, j)->set_obsolete();
1219 }
1220
1221 unsigned y_nnod = this->nboundary_node(current_boundary_y_id);
1222 for (unsigned j = 0; j < y_nnod; j++)
1223 {
1224 this->boundary_node_pt(current_boundary_y_id, j)->set_obsolete();
1225 }
1226
1227 // Kill the obsolete nodes
1228 this->prune_dead_nodes();
1229
1230 // Get the number of elements in the PML mesh
1231 unsigned n_pml_element = this->nelement();
1232
1233 // Connect the elements in the pml mesh to the ones
1234 // in the triangular mesh at element level
1235 unsigned count = 0;
1236
1237 if (parent_boundary_y_id == 3)
1238 {
1239 for (unsigned e = 0; e < n_pml_element; e++)
1240 {
1241 // If element is on the right boundary
1242 if ((e % n_pml_x) == (n_pml_x - 1))
1243 {
1244 // Upcast from GeneralisedElement to bulk element
1245 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
1246
1247 // Loop over all nodes in element
1248 unsigned n_node = el_pt->nnode();
1249 for (unsigned inod = 0; inod < n_node; inod++)
1250 {
1251 if (e == (n_pml_x - 1))
1252 {
1255 if ((inod % nnode_1d == interior_node_nr_helper_2) &&
1256 (inod > (nnode_1d - 1)))
1257 {
1258 // Get the pointer from the triangular mesh
1260 count++;
1261
1262 // Node between two elements
1264 {
1265 count--;
1266 }
1267 }
1268 }
1269 else
1270 {
1271 if ((inod % nnode_1d) == interior_node_nr_helper_2)
1272 {
1273 // Get the pointer from the triangular mesh
1275 count++;
1276
1277 // Node between two elements
1279 {
1280 count--;
1281 }
1282 }
1283 }
1284 }
1285 }
1286 }
1287 }
1288
1289 count = 0;
1290
1291 if (parent_boundary_x_id == 2)
1292 {
1293 for (unsigned e = 0; e < n_pml_element; e++)
1294 {
1295 // If element is on the right boundary
1296 if ((int)(e / n_pml_x) == 0)
1297 {
1298 // Upcast from GeneralisedElement to bulk element
1299 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
1300
1301 // Loop over all nodes in element
1302 unsigned n_node = el_pt->nnode();
1303 for (unsigned inod = 0; inod < n_node; inod++)
1304 {
1305 // If it is one of the ones on the left boundary
1306 if (e == (n_pml_x - 1))
1307 {
1308 if (((int)(inod / nnode_1d) == 0) &&
1310 {
1311 // Get the pointer from the triangular mesh
1313 count++;
1314
1315 // Node between two elements
1316 if (inod == (nnode_1d - 1))
1317 {
1318 count--;
1319 }
1320 }
1321 }
1322 else
1323 {
1324 if ((int)(inod / nnode_1d) == 0)
1325 {
1326 // Get the pointer from the triangular mesh
1328 count++;
1329
1330 // Node between two elements
1332 {
1333 count--;
1334 }
1335 }
1336 }
1337 }
1338 }
1339 }
1340 }
1341 }
1342
1343 // Set up bottom left corner element
1344 //--------------------------------
1345 if ((parent_boundary_x_id == 0) && (parent_boundary_y_id == 3))
1346 {
1347 // Get the number of nodes to be connected on the horizontal boundary
1348 unsigned n_boundary_x_node =
1349 PMLQuad_mesh_x_pt->nboundary_node(parent_boundary_x_id);
1350
1351 // Create a vector of ordered boundary nodes
1353
1354 // Fill the vector with the nodes on the respective boundary
1355 for (unsigned n = 0; n < n_boundary_x_node; n++)
1356 {
1358 PMLQuad_mesh_x_pt->boundary_node_pt(parent_boundary_x_id, n);
1359 }
1360
1361 // Sort them
1362 if (parent_boundary_x_id == 0)
1363 {
1364 std::sort(ordered_boundary_x_node_pt.begin(),
1367 }
1368
1369 // Get the number of nodes to be connected on the vertical boundary
1370 unsigned n_boundary_y_node =
1371 PMLQuad_mesh_y_pt->nboundary_node(parent_boundary_y_id);
1372
1373 // Create a vector of ordered boundary nodes
1375
1376 // Fill the vector with the nodes on the respective boundary
1377 for (unsigned n = 0; n < n_boundary_y_node; n++)
1378 {
1380 PMLQuad_mesh_y_pt->boundary_node_pt(parent_boundary_y_id, n);
1381 }
1382
1383 // Sort them
1384 if (parent_boundary_y_id == 3)
1385 {
1386 std::sort(ordered_boundary_y_node_pt.begin(),
1389 }
1390
1391 unsigned x_nnod = this->nboundary_node(current_boundary_x_id);
1392 for (unsigned j = 0; j < x_nnod; j++)
1393 {
1394 this->boundary_node_pt(current_boundary_x_id, j)->set_obsolete();
1395 }
1396
1397 unsigned y_nnod = this->nboundary_node(current_boundary_y_id);
1398 for (unsigned j = 0; j < y_nnod; j++)
1399 {
1400 this->boundary_node_pt(current_boundary_y_id, j)->set_obsolete();
1401 }
1402
1403 // Kill the obsolete nodes
1404 this->prune_dead_nodes();
1405
1406 unsigned n_pml_element = this->nelement();
1407
1408 // Connect the elements in the pml mesh to the ones
1409 // in the triangular mesh at element level
1410 unsigned count = 0;
1411
1412 if (parent_boundary_y_id == 3)
1413 {
1414 for (unsigned e = 0; e < n_pml_element; e++)
1415 {
1416 // If element is on the right boundary
1417 if ((e % n_pml_x) == (n_pml_x - 1))
1418 {
1419 // Upcast from GeneralisedElement to bulk element
1420 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
1421
1422 // Loop over all nodes in element
1423 unsigned n_node = el_pt->nnode();
1424 for (unsigned inod = 0; inod < n_node; inod++)
1425 {
1426 if (e == (n_pml_element - 1))
1427 {
1429 {
1431 }
1432 if ((inod % nnode_1d == interior_node_nr_helper_2) &&
1434 {
1435 // Get the pointer from the triangular mesh
1437 count++;
1438
1439 // Node between two elements
1441 {
1442 count--;
1443 }
1444 }
1445 }
1446 else
1447 {
1448 if ((inod % nnode_1d) == interior_node_nr_helper_2)
1449 {
1450 // Get the pointer from the triangular mesh
1452 count++;
1453
1454 // Node between two elements
1456 {
1457 count--;
1458 }
1459 }
1460 }
1461 }
1462 }
1463 }
1464 }
1465
1466 count = 0;
1467
1468 if (parent_boundary_x_id == 0)
1469 {
1470 for (unsigned e = 0; e < n_pml_element; e++)
1471 {
1472 // If element is on the right boundary
1473 if (e >= ((n_pml_x) * (n_pml_y - 1)))
1474 {
1475 // Upcast from GeneralisedElement to bulk element
1476 ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(e));
1477
1478 // Loop over all nodes in element
1479 unsigned n_node = el_pt->nnode();
1480 for (unsigned inod = 0; inod < n_node; inod++)
1481 {
1482 if (e == (n_pml_element - 1))
1483 {
1484 if (((unsigned)(inod / nnode_1d) ==
1487 {
1488 // Get the pointer from the triangular mesh
1490 count++;
1491
1492 // Node between two elements
1494 {
1495 count--;
1496 }
1497 }
1498 }
1499 else
1500 {
1501 if ((unsigned)(inod / nnode_1d) == interior_node_nr_helper_2)
1502 {
1503 // Get the pointer from the triangular mesh
1505 count++;
1506
1507 // Node between two elements
1509 {
1510 count--;
1511 }
1512 }
1513 }
1514 }
1515 }
1516 }
1517 }
1518 }
1519 }
1520 };
1521
1522 ////////////////////////////////////////////////////////////////
1523 ////////////////////////////////////////////////////////////////
1524 ////////////////////////////////////////////////////////////////
1525
1526
1527 //===================================================================
1528 /// All helper routines for 2D bulk boundary mesh usage in order to
1529 /// generate PML meshes aligned to the main mesh
1530 //===================================================================
1531 namespace TwoDimensionalPMLHelper
1532 {
1533 //============================================================================
1534 /// "Constructor" for PML mesh,aligned with the right physical domain
1535 /// boundary
1536 //============================================================================
1537 template<class ASSOCIATED_PML_QUAD_ELEMENT>
1540 const unsigned& right_boundary_id,
1541 const unsigned& n_x_right_pml,
1542 const double& width_x_right_pml,
1543 TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
1544 {
1545 // Look at the right boundary of the triangular mesh
1546 unsigned n_right_boundary_node =
1547 bulk_mesh_pt->nboundary_node(right_boundary_id);
1548
1549 // Create a vector of ordered boundary nodes
1551
1552 // Fill the vector with the nodes on the respective boundary
1553 for (unsigned n = 0; n < n_right_boundary_node; n++)
1554 {
1556 bulk_mesh_pt->boundary_node_pt(right_boundary_id, n);
1557 }
1558
1559 // Sort them from lowest to highest (in y coordinate)
1560 std::sort(ordered_right_boundary_node_pt.begin(),
1563
1564 // The number of elements in y is taken from the triangular mesh
1565 unsigned n_y_right_pml =
1566 bulk_mesh_pt->nboundary_element(right_boundary_id);
1567
1568 // Specific PML sizes needed, taken directly from physical domain
1570 /// PML layer with added to the bulk mesh coordinate
1571 double l_pml_right_x_end =
1574 double l_pml_right_y_end =
1576
1577 // Rectangular boundary id to be merged with triangular mesh
1578 unsigned right_quadPML_boundary_id = 3;
1579
1580 // Create the mesh to be designated to the PML
1582
1583 // Build the right one
1594 time_stepper_pt);
1595
1596 // Enable PML damping on the entire mesh
1597 unsigned n_element_pml_right = pml_right_mesh_pt->nelement();
1598 for (unsigned e = 0; e < n_element_pml_right; e++)
1599 {
1600 // Upcast
1602 dynamic_cast<PMLElementBase<2>*>(pml_right_mesh_pt->element_pt(e));
1604 }
1605
1606 // Get the values to be pinned from the first element (which we
1607 // assume exists!)
1609 dynamic_cast<PMLElementBase<2>*>(pml_right_mesh_pt->element_pt(0));
1611 el_pt->values_to_be_pinned_on_outer_pml_boundary(values_to_pin);
1612 unsigned npin = values_to_pin.size();
1613
1614 // Exterior boundary needs to be set to Dirichlet 0
1615 // in both real and imaginary parts
1616 unsigned n_bound_pml_right = pml_right_mesh_pt->nboundary();
1617 for (unsigned b = 0; b < n_bound_pml_right; b++)
1618 {
1619 unsigned n_node = pml_right_mesh_pt->nboundary_node(b);
1620 for (unsigned n = 0; n < n_node; n++)
1621 {
1622 Node* nod_pt = pml_right_mesh_pt->boundary_node_pt(b, n);
1623 if (b == 1)
1624 {
1625 for (unsigned j = 0; j < npin; j++)
1626 {
1627 unsigned j_val = values_to_pin[j];
1628 nod_pt->pin(j_val);
1629 nod_pt->set_value(j_val, 0.0);
1630 }
1631 }
1632 }
1633 }
1634
1635 /// Return the finalized mesh, with PML enabled
1636 /// and boundary conditions added
1637 return pml_right_mesh_pt;
1638 }
1639
1640 //===========================================================================
1641 /// "Constructor" for PML mesh, aligned with the top physical domain
1642 /// boundary
1643 //===========================================================================
1644 template<class ASSOCIATED_PML_QUAD_ELEMENT>
1647 const unsigned& top_boundary_id,
1648 const unsigned& n_y_top_pml,
1649 const double& width_y_top_pml,
1650 TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
1651 {
1652 // Look at the top boundary of the triangular mesh
1653 unsigned n_top_boundary_node =
1654 bulk_mesh_pt->nboundary_node(top_boundary_id);
1655
1656 // Create a vector of ordered boundary nodes
1658
1659 // Fill the vector with the nodes on the respective boundary
1660 for (unsigned n = 0; n < n_top_boundary_node; n++)
1661 {
1663 bulk_mesh_pt->boundary_node_pt(top_boundary_id, n);
1664 }
1665
1666 // Sort them from lowest to highest (in x coordinate)
1667 std::sort(ordered_top_boundary_node_pt.begin(),
1670
1671 // The number of elements in x is taken from the triangular mesh
1672 unsigned n_x_top_pml = bulk_mesh_pt->nboundary_element(top_boundary_id);
1673
1674 // Specific PML sizes needed, taken directly from physical domain
1676 double l_pml_top_x_end =
1679 /// PML layer width added to the bulk mesh coordinate
1680 double l_pml_top_y_end =
1682
1683 unsigned top_quadPML_boundary_id = 0;
1684
1685 // Create the mesh to be designated to the PML
1686 Mesh* pml_top_mesh_pt = 0;
1687
1688 // Build the top PML mesh
1699 time_stepper_pt);
1700
1701 // Enable PML damping on the entire mesh
1702 unsigned n_element_pml_top = pml_top_mesh_pt->nelement();
1703 for (unsigned e = 0; e < n_element_pml_top; e++)
1704 {
1705 // Upcast
1707 dynamic_cast<PMLElementBase<2>*>(pml_top_mesh_pt->element_pt(e));
1708 el_pt->enable_pml(1, l_pml_top_y_start, l_pml_top_y_end);
1709 }
1710
1711 // Get the values to be pinned from the first element (which we
1712 // assume exists!)
1714 dynamic_cast<PMLElementBase<2>*>(pml_top_mesh_pt->element_pt(0));
1716 el_pt->values_to_be_pinned_on_outer_pml_boundary(values_to_pin);
1717 unsigned npin = values_to_pin.size();
1718
1719 // Exterior boundary needs to be set to Dirichlet 0
1720 // for both real and imaginary parts of all fields
1721 // in the problem
1722 unsigned n_bound_pml_top = pml_top_mesh_pt->nboundary();
1723 for (unsigned b = 0; b < n_bound_pml_top; b++)
1724 {
1725 unsigned n_node = pml_top_mesh_pt->nboundary_node(b);
1726 for (unsigned n = 0; n < n_node; n++)
1727 {
1728 Node* nod_pt = pml_top_mesh_pt->boundary_node_pt(b, n);
1729 if (b == 2)
1730 {
1731 for (unsigned j = 0; j < npin; j++)
1732 {
1733 unsigned j_val = values_to_pin[j];
1734 nod_pt->pin(j_val);
1735 nod_pt->set_value(j_val, 0.0);
1736 }
1737 }
1738 }
1739 }
1740
1741 /// Return the finalized mesh, with PML enabled
1742 /// and boundary conditions added
1743 return pml_top_mesh_pt;
1744 }
1745
1746 //============================================================================
1747 /// "Constructor" for PML mesh, aligned with the left physical domain
1748 /// boundary
1749 //============================================================================
1750 template<class ASSOCIATED_PML_QUAD_ELEMENT>
1753 const unsigned& left_boundary_id,
1754 const unsigned& n_x_left_pml,
1755 const double& width_x_left_pml,
1756 TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
1757 {
1758 // Look at the left boundary of the triangular mesh
1759 unsigned n_left_boundary_node =
1760 bulk_mesh_pt->nboundary_node(left_boundary_id);
1761
1762 // Create a vector of ordered boundary nodes
1764
1765 // Fill the vector with the nodes on the respective boundary
1766 for (unsigned n = 0; n < n_left_boundary_node; n++)
1767 {
1769 bulk_mesh_pt->boundary_node_pt(left_boundary_id, n);
1770 }
1771
1772 // Sort them from lowest to highest (in y coordinate)
1773 std::sort(ordered_left_boundary_node_pt.begin(),
1776
1777 // The number of elements in y is taken from the triangular mesh
1778 unsigned n_y_left_pml = bulk_mesh_pt->nboundary_element(left_boundary_id);
1779
1780 // Specific PML sizes needed, taken directly from physical domain
1781 /// PML layer width subtracted from left bulk mesh coordinate
1782 double l_pml_left_x_start =
1785 double l_pml_left_x_end =
1787 double l_pml_left_y_start =
1790
1791 unsigned left_quadPML_boundary_id = 1;
1792
1793 // Create the mesh to be designated to the PML
1795
1796 // Build the left PML mesh
1807 time_stepper_pt);
1808
1809 // Enable PML damping on the entire mesh
1810 unsigned n_element_pml_left = pml_left_mesh_pt->nelement();
1811 for (unsigned e = 0; e < n_element_pml_left; e++)
1812 {
1813 // Upcast
1815 dynamic_cast<PMLElementBase<2>*>(pml_left_mesh_pt->element_pt(e));
1816 el_pt->enable_pml(0, l_pml_left_x_end, l_pml_left_x_start);
1817 }
1818
1819 // Get the values to be pinned from the first element (which we
1820 // assume exists!)
1822 dynamic_cast<PMLElementBase<2>*>(pml_left_mesh_pt->element_pt(0));
1824 el_pt->values_to_be_pinned_on_outer_pml_boundary(values_to_pin);
1825 unsigned npin = values_to_pin.size();
1826
1827 // Exterior boundary needs to be set to Dirichlet 0
1828 // for both real and imaginary parts of all fields
1829 // in the problem
1830 unsigned n_bound_pml_left = pml_left_mesh_pt->nboundary();
1831 for (unsigned b = 0; b < n_bound_pml_left; b++)
1832 {
1833 unsigned n_node = pml_left_mesh_pt->nboundary_node(b);
1834 for (unsigned n = 0; n < n_node; n++)
1835 {
1836 Node* nod_pt = pml_left_mesh_pt->boundary_node_pt(b, n);
1837 if (b == 3)
1838 {
1839 for (unsigned j = 0; j < npin; j++)
1840 {
1841 unsigned j_val = values_to_pin[j];
1842 nod_pt->pin(j_val);
1843 nod_pt->set_value(j_val, 0.0);
1844 }
1845 }
1846 }
1847 }
1848
1849 /// Return the finalized mesh, with PML enabled
1850 /// and boundary conditions added
1851 return pml_left_mesh_pt;
1852 }
1853
1854 //============================================================================
1855 /// "Constructor" for PML mesh,aligned with the bottom physical domain
1856 /// boundary
1857 //============================================================================
1858 template<class ASSOCIATED_PML_QUAD_ELEMENT>
1861 const unsigned& bottom_boundary_id,
1862 const unsigned& n_y_bottom_pml,
1863 const double& width_y_bottom_pml,
1864 TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
1865 {
1866 // Look at the bottom boundary of the triangular mesh
1867 unsigned n_bottom_boundary_node =
1868 bulk_mesh_pt->nboundary_node(bottom_boundary_id);
1869
1870 // Create a vector of ordered boundary nodes
1872
1873 // Fill the vector with the nodes on the respective boundary
1874 for (unsigned n = 0; n < n_bottom_boundary_node; n++)
1875 {
1877 bulk_mesh_pt->boundary_node_pt(bottom_boundary_id, n);
1878 }
1879
1880 // Sort them from highest to lowest (in x coordinate)
1881 std::sort(ordered_bottom_boundary_node_pt.begin(),
1884
1885 // The number of elements in y is taken from the triangular mesh
1886 unsigned n_x_bottom_pml =
1887 bulk_mesh_pt->nboundary_element(bottom_boundary_id);
1888
1889 // Specific PML sizes needed, taken directly from physical domain
1890 double l_pml_bottom_x_start =
1893 /// PML layer width subtracted from the bulk mesh lower
1894 /// boundary coordinate
1895 double l_pml_bottom_y_start =
1898
1899 unsigned bottom_quadPML_boundary_id = 2;
1900
1901 // Create the mesh to be designated to the PML
1903
1904 // Build the bottom PML mesh
1915 time_stepper_pt);
1916
1917 // Enable PML damping on the entire mesh
1918 unsigned n_element_pml_bottom = pml_bottom_mesh_pt->nelement();
1919 for (unsigned e = 0; e < n_element_pml_bottom; e++)
1920 {
1921 // Upcast
1923 dynamic_cast<PMLElementBase<2>*>(pml_bottom_mesh_pt->element_pt(e));
1925 }
1926
1927 // Get the values to be pinned from the first element (which we
1928 // assume exists!)
1930 dynamic_cast<PMLElementBase<2>*>(pml_bottom_mesh_pt->element_pt(0));
1932 el_pt->values_to_be_pinned_on_outer_pml_boundary(values_to_pin);
1933 unsigned npin = values_to_pin.size();
1934
1935 // Exterior boundary needs to be set to Dirichlet 0
1936 // for both real and imaginary parts of all fields
1937 // in the problem
1938 unsigned n_bound_pml_bottom = pml_bottom_mesh_pt->nboundary();
1939 for (unsigned b = 0; b < n_bound_pml_bottom; b++)
1940 {
1941 unsigned n_node = pml_bottom_mesh_pt->nboundary_node(b);
1942 for (unsigned n = 0; n < n_node; n++)
1943 {
1944 Node* nod_pt = pml_bottom_mesh_pt->boundary_node_pt(b, n);
1945 if (b == 0)
1946 {
1947 for (unsigned j = 0; j < npin; j++)
1948 {
1949 unsigned j_val = values_to_pin[j];
1950 nod_pt->pin(j_val);
1951 nod_pt->set_value(j_val, 0.0);
1952 }
1953 }
1954 }
1955 }
1956
1957 /// Return the finalized mesh, with PML enabled
1958 /// and boundary conditions added
1959 return pml_bottom_mesh_pt;
1960 }
1961
1962 //==========================================================================
1963 /// "Constructor" for PML top right corner mesh,
1964 /// aligned with the existing PML meshes
1965 //==========================================================================
1966 template<class ASSOCIATED_PML_QUAD_ELEMENT>
1971 const unsigned& right_boundary_id,
1972 TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
1973 {
1974 /// Relevant boundary id's to be used in construction
1975 /// Parent id refers to already existing PML meshes
1976 unsigned parent_boundary_x_id = 2;
1977 unsigned parent_boundary_y_id = 1;
1978 // Current id refers to the mesh that is to be constructed
1979 unsigned current_boundary_x_id = 0;
1980 unsigned current_boundary_y_id = 3;
1981
1982 // Look at the right boundary of the triangular mesh
1983 unsigned n_right_boundary_node =
1984 bulk_mesh_pt->nboundary_node(right_boundary_id);
1985
1986 // Create a vector of ordered boundary nodes
1988
1989 // Fill the vector with the nodes on the respective boundary
1990 for (unsigned n = 0; n < n_right_boundary_node; n++)
1991 {
1993 bulk_mesh_pt->boundary_node_pt(right_boundary_id, n);
1994 }
1995
1996 // Sort them from lowest to highest (in y coordinate)
1997 std::sort(ordered_right_boundary_node_pt.begin(),
2000
2001 /// Number of elements and boundary nodes to be acted upon during
2002 /// construction are extracted from the 'parent' PML meshes
2003 unsigned n_x_right_pml =
2004 pml_right_mesh_pt->nboundary_element(parent_boundary_x_id);
2005 unsigned n_y_top_pml =
2006 pml_top_mesh_pt->nboundary_element(parent_boundary_y_id);
2007 unsigned n_x_boundary_nodes =
2008 pml_right_mesh_pt->nboundary_node(parent_boundary_x_id);
2009 unsigned n_y_boundary_nodes =
2010 pml_top_mesh_pt->nboundary_node(parent_boundary_y_id);
2011
2012 /// Specific PML sizes needed, taken directly from physical domain
2013 /// and existing PML meshes
2014 double l_pml_right_x_start =
2016 double l_pml_right_x_end =
2018 ->boundary_node_pt(parent_boundary_x_id, n_x_boundary_nodes - 1)
2019 ->x(0);
2020 double l_pml_top_y_start =
2022 double l_pml_top_y_end =
2024 ->boundary_node_pt(parent_boundary_y_id, n_y_boundary_nodes - 1)
2025 ->x(1);
2026
2027 // Create the mesh to be designated to the PML
2029
2030 // Build the top right corner PML mesh
2047 time_stepper_pt);
2048
2049 // Enable PML damping on the entire mesh
2050 /// The enabling must be perfromed in both x- and y-directions
2051 /// as this is a corner PML mesh
2052 unsigned n_element_pml_top_right = pml_top_right_mesh_pt->nelement();
2053 for (unsigned e = 0; e < n_element_pml_top_right; e++)
2054 {
2055 // Upcast
2056 PMLElementBase<2>* el_pt = dynamic_cast<PMLElementBase<2>*>(
2057 pml_top_right_mesh_pt->element_pt(e));
2059 el_pt->enable_pml(1, l_pml_top_y_start, l_pml_top_y_end);
2060 }
2061
2062 // Get the values to be pinned from the first element (which we
2063 // assume exists!)
2065 dynamic_cast<PMLElementBase<2>*>(pml_top_right_mesh_pt->element_pt(0));
2067 el_pt->values_to_be_pinned_on_outer_pml_boundary(values_to_pin);
2068 unsigned npin = values_to_pin.size();
2069
2070 // Exterior boundary needs to be set to Dirichlet 0
2071 // for both real and imaginary parts of all fields
2072 // in the problem
2073 unsigned n_bound_pml_top_right = pml_top_right_mesh_pt->nboundary();
2074 for (unsigned b = 0; b < n_bound_pml_top_right; b++)
2075 {
2076 unsigned n_node = pml_top_right_mesh_pt->nboundary_node(b);
2077 for (unsigned n = 0; n < n_node; n++)
2078 {
2079 Node* nod_pt = pml_top_right_mesh_pt->boundary_node_pt(b, n);
2080 if ((b == 1) || (b == 2))
2081 {
2082 for (unsigned j = 0; j < npin; j++)
2083 {
2084 unsigned j_val = values_to_pin[j];
2085 nod_pt->pin(j_val);
2086 nod_pt->set_value(j_val, 0.0);
2087 }
2088 }
2089 }
2090 }
2091
2092 /// Return the finalized mesh, with PML enabled
2093 /// and boundary conditions added
2094 return pml_top_right_mesh_pt;
2095 }
2096
2097 //==========================================================================
2098 /// "Constructor" for PML bottom right corner mesh,
2099 /// aligned with the existing PML meshes
2100 //==========================================================================
2101 template<class ASSOCIATED_PML_QUAD_ELEMENT>
2106 const unsigned& right_boundary_id,
2107 TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
2108 {
2109 /// Relevant boundary id's to be used in construction
2110 /// Parent id refers to already existing PML meshes
2111 unsigned parent_boundary_x_id = 0;
2112 unsigned parent_boundary_y_id = 1;
2113 // Current id refers to the mesh that is to be constructed
2114 unsigned current_boundary_x_id = 2;
2115 unsigned current_boundary_y_id = 3;
2116
2117 // Look at the right boundary of the triangular mesh
2118 unsigned n_right_boundary_node =
2119 bulk_mesh_pt->nboundary_node(right_boundary_id);
2120
2121 // Create a vector of ordered boundary nodes
2123
2124 // Fill the vector with the nodes on the respective boundary
2125 for (unsigned n = 0; n < n_right_boundary_node; n++)
2126 {
2128 bulk_mesh_pt->boundary_node_pt(right_boundary_id, n);
2129 }
2130
2131 // Sort them from lowest to highest (in y coordinate)
2132 std::sort(ordered_right_boundary_node_pt.begin(),
2135
2136 /// Number of elements and boundary nodes to be acted upon during
2137 /// construction are extracted from the 'parent' PML meshes
2138 unsigned n_x_right_pml =
2139 pml_right_mesh_pt->nboundary_element(parent_boundary_x_id);
2140 unsigned n_y_bottom_pml =
2141 pml_bottom_mesh_pt->nboundary_element(parent_boundary_y_id);
2142 unsigned n_x_boundary_nodes =
2143 pml_right_mesh_pt->nboundary_node(parent_boundary_x_id);
2144
2145 /// Specific PML sizes needed, taken directly from physical domain
2146 /// and existing PML meshes
2148 double l_pml_right_x_end =
2150 ->boundary_node_pt(parent_boundary_x_id, n_x_boundary_nodes - 1)
2151 ->x(0);
2152 double l_pml_bottom_y_start =
2153 pml_bottom_mesh_pt->boundary_node_pt(parent_boundary_y_id, 0)->x(1);
2155
2156 // Create the mesh to be designated to the PML
2158
2159 // Build the bottom right corner PML mesh
2176 time_stepper_pt);
2177
2178 // Enable PML damping on the entire mesh
2179 /// The enabling must be perfromed in both x- and y-directions
2180 /// as this is a corner PML mesh
2182 pml_bottom_right_mesh_pt->nelement();
2183
2184 for (unsigned e = 0; e < n_element_pml_bottom_right; e++)
2185 {
2186 // Upcast
2187 PMLElementBase<2>* el_pt = dynamic_cast<PMLElementBase<2>*>(
2188 pml_bottom_right_mesh_pt->element_pt(e));
2191 }
2192
2193 // Get the values to be pinned from the first element (which we
2194 // assume exists!)
2195 PMLElementBase<2>* el_pt = dynamic_cast<PMLElementBase<2>*>(
2196 pml_bottom_right_mesh_pt->element_pt(0));
2198 el_pt->values_to_be_pinned_on_outer_pml_boundary(values_to_pin);
2199 unsigned npin = values_to_pin.size();
2200
2201 // Exterior boundary needs to be set to Dirichlet 0
2202 // for both real and imaginary parts of all fields
2203 // in the problem
2204 unsigned n_bound_pml_bottom_right = pml_bottom_right_mesh_pt->nboundary();
2205 for (unsigned b = 0; b < n_bound_pml_bottom_right; b++)
2206 {
2207 unsigned n_node = pml_bottom_right_mesh_pt->nboundary_node(b);
2208 for (unsigned n = 0; n < n_node; n++)
2209 {
2210 Node* nod_pt = pml_bottom_right_mesh_pt->boundary_node_pt(b, n);
2211 if ((b == 0) || (b == 1))
2212 {
2213 for (unsigned j = 0; j < npin; j++)
2214 {
2215 unsigned j_val = values_to_pin[j];
2216 nod_pt->pin(j_val);
2217 nod_pt->set_value(j_val, 0.0);
2218 }
2219 }
2220 }
2221 }
2222
2223 /// Return the finalized mesh, with PML enabled
2224 /// and boundary conditions added
2226 }
2227
2228 //==========================================================================
2229 /// "Constructor" for PML top left corner mesh,
2230 /// aligned with the existing PML meshes
2231 //==========================================================================
2232 template<class ASSOCIATED_PML_QUAD_ELEMENT>
2237 const unsigned& left_boundary_id,
2238 TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
2239 {
2240 /// Relevant boundary id's to be used in construction
2241 /// Parent id refers to already existing PML meshes
2242 unsigned parent_boundary_x_id = 2;
2243 unsigned parent_boundary_y_id = 3;
2244 // Current id refers to the mesh that is to be constructed
2245 unsigned current_boundary_x_id = 0;
2246 unsigned current_boundary_y_id = 1;
2247
2248 // Look at the left boundary of the triangular mesh
2249 unsigned n_left_boundary_node =
2250 bulk_mesh_pt->nboundary_node(left_boundary_id);
2251
2252 // Create a vector of ordered boundary nodes
2254
2255 // Fill the vector with the nodes on the respective boundary
2256 for (unsigned n = 0; n < n_left_boundary_node; n++)
2257 {
2259 bulk_mesh_pt->boundary_node_pt(left_boundary_id, n);
2260 }
2261
2262 /// Sort them from lowest to highest (in y coordinate)
2263 /// sorter_right_boundary is still functional, as the sorting
2264 /// is performed by the same criterion
2265 std::sort(ordered_left_boundary_node_pt.begin(),
2268
2269 /// Number of elements and boundary nodes to be acted upon during
2270 /// construction are extracted from the 'parent' PML meshes
2271 unsigned n_x_left_pml =
2272 pml_left_mesh_pt->nboundary_element(parent_boundary_x_id);
2273 unsigned n_y_top_pml =
2274 pml_top_mesh_pt->nboundary_element(parent_boundary_y_id);
2275 unsigned n_y_boundary_nodes =
2276 pml_top_mesh_pt->nboundary_node(parent_boundary_y_id);
2277
2278 /// Specific PML sizes needed, taken directly from physical domain
2279 /// and existing PML meshes
2280 double l_pml_left_x_start =
2281 pml_left_mesh_pt->boundary_node_pt(parent_boundary_x_id, 0)->x(0);
2282 double l_pml_left_x_end =
2284 double l_pml_top_y_start =
2286 double l_pml_top_y_end =
2288 ->boundary_node_pt(parent_boundary_y_id, n_y_boundary_nodes - 1)
2289 ->x(1);
2290
2291 // Create the mesh to be designated to the PML
2293
2294 // Build the top left corner PML mesh
2310 time_stepper_pt);
2311
2312 // Enable PML damping on the entire mesh
2313 /// The enabling must be perfromed in both x- and y-directions
2314 /// as this is a corner PML mesh
2315 unsigned n_element_pml_top_left = pml_top_left_mesh_pt->nelement();
2316
2317 for (unsigned e = 0; e < n_element_pml_top_left; e++)
2318 {
2319 // Upcast
2321 dynamic_cast<PMLElementBase<2>*>(pml_top_left_mesh_pt->element_pt(e));
2322 el_pt->enable_pml(0, l_pml_left_x_end, l_pml_left_x_start);
2323 el_pt->enable_pml(1, l_pml_top_y_start, l_pml_top_y_end);
2324 }
2325
2326 // Get the values to be pinned from the first element (which we
2327 // assume exists!)
2329 dynamic_cast<PMLElementBase<2>*>(pml_top_left_mesh_pt->element_pt(0));
2331 el_pt->values_to_be_pinned_on_outer_pml_boundary(values_to_pin);
2332 unsigned npin = values_to_pin.size();
2333
2334 // Exterior boundary needs to be set to Dirichlet 0
2335 // for both real and imaginary parts of all fields
2336 // in the problem
2337 unsigned n_bound_pml_top_left = pml_top_left_mesh_pt->nboundary();
2338 for (unsigned b = 0; b < n_bound_pml_top_left; b++)
2339 {
2340 unsigned n_node = pml_top_left_mesh_pt->nboundary_node(b);
2341 for (unsigned n = 0; n < n_node; n++)
2342 {
2343 Node* nod_pt = pml_top_left_mesh_pt->boundary_node_pt(b, n);
2344 if ((b == 2) || (b == 3))
2345 {
2346 for (unsigned j = 0; j < npin; j++)
2347 {
2348 unsigned j_val = values_to_pin[j];
2349 nod_pt->pin(j_val);
2350 nod_pt->set_value(j_val, 0.0);
2351 }
2352 }
2353 }
2354 }
2355
2356 /// Return the finalized mesh, with PML enabled
2357 /// and boundary conditions added
2358 return pml_top_left_mesh_pt;
2359 }
2360
2361 //==========================================================================
2362 /// "Constructor" for PML bottom left corner mesh,
2363 /// aligned with the existing PML meshes
2364 //==========================================================================
2365 template<class ASSOCIATED_PML_QUAD_ELEMENT>
2370 const unsigned& left_boundary_id,
2371 TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
2372 {
2373 /// Relevant boundary id's to be used in construction
2374 /// Parent id refers to already existing PML meshes
2375 unsigned parent_boundary_x_id = 0;
2376 unsigned parent_boundary_y_id = 3;
2377 // Current id refers to the mesh that is to be constructed
2378 unsigned current_boundary_x_id = 2;
2379 unsigned current_boundary_y_id = 1;
2380
2381 // Look at the left boundary of the triangular mesh
2382 unsigned n_left_boundary_node =
2383 bulk_mesh_pt->nboundary_node(left_boundary_id);
2384
2385 // Create a vector of ordered boundary nodes
2387
2388 // Fill the vector with the nodes on the respective boundary
2389 for (unsigned n = 0; n < n_left_boundary_node; n++)
2390 {
2392 bulk_mesh_pt->boundary_node_pt(left_boundary_id, n);
2393 }
2394
2395 /// Sort them from lowest to highest (in y coordinate)
2396 /// sorter_right_boundary is still functional, as the sorting
2397 /// is performed by the same criterion
2398 std::sort(ordered_left_boundary_node_pt.begin(),
2401
2402 /// Number of elements and boundary nodes to be acted upon during
2403 /// construction are extracted from the 'parent' PML meshes
2404 unsigned n_x_left_pml =
2405 pml_left_mesh_pt->nboundary_element(parent_boundary_x_id);
2406 unsigned n_y_bottom_pml =
2407 pml_bottom_mesh_pt->nboundary_element(parent_boundary_y_id);
2408
2409 /// Specific PML sizes needed, taken directly from physical domain
2410 /// and existing PML meshes
2411 double l_pml_left_x_start =
2412 pml_left_mesh_pt->boundary_node_pt(parent_boundary_x_id, 0)->x(0);
2413 double l_pml_left_x_end =
2415 double l_pml_bottom_y_start =
2416 pml_bottom_mesh_pt->boundary_node_pt(parent_boundary_y_id, 0)->x(1);
2418
2419 // Create the mesh to be designated to the PML
2421
2422 // Build the bottom left corner PML mesh
2439 time_stepper_pt);
2440
2441 // Enable PML damping on the entire mesh
2442 /// The enabling must be perfromed in both x- and y-directions
2443 /// as this is a corner PML mesh
2445 for (unsigned e = 0; e < n_element_pml_bottom_left; e++)
2446 {
2447 // Upcast
2448 PMLElementBase<2>* el_pt = dynamic_cast<PMLElementBase<2>*>(
2449 pml_bottom_left_mesh_pt->element_pt(e));
2450 el_pt->enable_pml(0, l_pml_left_x_end, l_pml_left_x_start);
2452 }
2453
2454 // Get the values to be pinned from the first element (which we
2455 // assume exists!)
2456 PMLElementBase<2>* el_pt = dynamic_cast<PMLElementBase<2>*>(
2457 pml_bottom_left_mesh_pt->element_pt(0));
2459 el_pt->values_to_be_pinned_on_outer_pml_boundary(values_to_pin);
2460 unsigned npin = values_to_pin.size();
2461
2462 // Exterior boundary needs to be set to Dirichlet 0
2463 // for both real and imaginary parts of all fields
2464 // in the problem
2465 unsigned n_bound_pml_bottom_left = pml_bottom_left_mesh_pt->nboundary();
2466 for (unsigned b = 0; b < n_bound_pml_bottom_left; b++)
2467 {
2468 unsigned n_node = pml_bottom_left_mesh_pt->nboundary_node(b);
2469 for (unsigned n = 0; n < n_node; n++)
2470 {
2471 Node* nod_pt = pml_bottom_left_mesh_pt->boundary_node_pt(b, n);
2472 if ((b == 0) || (b == 3))
2473 {
2474 for (unsigned j = 0; j < npin; j++)
2475 {
2476 unsigned j_val = values_to_pin[j];
2477 nod_pt->pin(j_val);
2478 nod_pt->set_value(j_val, 0.0);
2479 }
2480 }
2481 }
2482 }
2483
2484 /// Return the finalized mesh, with PML enabled
2485 /// and boundary conditions added
2487 }
2488
2489 } // namespace TwoDimensionalPMLHelper
2490
2491 //////////////////////////////////////////////////////////////////////
2492 //////////////////////////////////////////////////////////////////////
2493 //////////////////////////////////////////////////////////////////////
2494
2495} // namespace oomph
2496
2497#endif
e
Definition cfortran.h:571
cstr elem_len * i
Definition cfortran.h:603
A general Finite Element class.
Definition elements.h:1317
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
unsigned nnode() const
Return the number of nodes.
Definition elements.h:2214
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition elements.h:2179
virtual unsigned nnode_1d() const
Return the number of nodes along one edge of the element Default is to return zero — must be overload...
Definition elements.h:2222
A general mesh class.
Definition mesh.h:67
unsigned long nboundary_node(const unsigned &ibound) const
Return number of nodes on a particular boundary.
Definition mesh.h:841
static Steady< 0 > Default_TimeStepper
Default Steady Timestepper, to be used in default arguments to Mesh constructors.
Definition mesh.h:75
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition mesh.h:477
FiniteElement * boundary_element_pt(const unsigned &b, const unsigned &e) const
Return pointer to e-th finite element on boundary b.
Definition mesh.h:848
unsigned long nnode() const
Return number of nodes in the mesh.
Definition mesh.h:604
const Vector< GeneralisedElement * > & element_pt() const
Return reference to the Vector of elements.
Definition mesh.h:464
Vector< Node * > prune_dead_nodes()
Prune nodes. Nodes that have been marked as obsolete are removed from the mesh (and its boundary-node...
Definition mesh.cc:966
unsigned long nelement() const
Return number of elements in the mesh.
Definition mesh.h:598
Node *& boundary_node_pt(const unsigned &b, const unsigned &n)
Return pointer to node n on boundary b.
Definition mesh.h:497
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
void set_obsolete()
Mark node as obsolete.
Definition nodes.h:1436
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition nodes.h:1060
An OomphLibError object which should be thrown when an run-time error is encountered....
PML mesh, derived from RectangularQuadMesh.
Definition pml_meshes.h:776
PMLCornerQuadMesh(Mesh *PMLQuad_mesh_x_pt, Mesh *PMLQuad_mesh_y_pt, Mesh *bulk_mesh_pt, Node *special_corner_node_pt, const unsigned &parent_boundary_x_id, const unsigned &parent_boundary_y_id, const unsigned &current_boundary_x_id, const unsigned &current_boundary_y_id, const unsigned &n_pml_x, const unsigned &n_pml_y, const double &x_pml_start, const double &x_pml_end, const double &y_pml_start, const double &y_pml_end, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass pointer to "bulk" mesh and the two existing PML meshes in order to construct the co...
Definition pml_meshes.h:782
Base class for elements with pml capabilities.
Definition pml_meshes.h:59
bool Pml_is_enabled
Boolean indicating if element is used in pml mode.
Definition pml_meshes.h:118
virtual ~PMLElementBase()
Virtual destructor.
Definition pml_meshes.h:71
void enable_pml(const int &direction, const double &interface_border_value, const double &outer_domain_border_value)
Enable pml. Specify the coordinate direction along which pml boundary is constant,...
Definition pml_meshes.h:96
void disable_pml()
Disable pml. Ensures the PML-ification in all directions has been deactivated.
Definition pml_meshes.h:75
std::vector< bool > Pml_direction_active
Coordinate direction along which pml boundary is constant; alternatively: coordinate direction in whi...
Definition pml_meshes.h:123
Vector< double > Pml_outer_boundary
Coordinate of outer pml boundary (Storage is provided for any coordinate direction; only the entries ...
Definition pml_meshes.h:133
Vector< double > Pml_inner_boundary
Coordinate of inner pml boundary (Storage is provided for any coordinate direction; only the entries ...
Definition pml_meshes.h:128
PMLElementBase()
Constructor.
Definition pml_meshes.h:62
virtual void values_to_be_pinned_on_outer_pml_boundary(Vector< unsigned > &values_to_pin)=0
Pure virtual function in which we have to specify the values to be pinned (and set to zero) on the ou...
General definition of policy class defining the elements to be used in the actual PML layers....
Definition pml_meshes.h:47
PML mesh base class. Contains a pure virtual locate_zeta function to be uploaded in PMLQuadMesh and P...
Definition pml_meshes.h:170
virtual void pml_locate_zeta(const Vector< double > &x, FiniteElement *&el_pt)=0
Pure virtual function to provide an optimised version of the locate_zeta function for PML meshes....
PML mesh class. Policy class for 2D PML meshes.
Definition pml_meshes.h:186
void pml_locate_zeta(const Vector< double > &x, FiniteElement *&coarse_mesh_el_pt)
Overloaded function to allow the user to locate an element in mesh given the (Eulerian) position of a...
Definition pml_meshes.h:211
PMLQuadMeshBase(const unsigned &n_pml_x, const unsigned &n_pml_y, const double &x_pml_start, const double &x_pml_end, const double &y_pml_start, const double &y_pml_end, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Create the underlying rectangular quad mesh.
Definition pml_meshes.h:189
PML mesh, derived from RectangularQuadMesh.
Definition pml_meshes.h:430
PMLQuadMesh(Mesh *bulk_mesh_pt, const unsigned &boundary_id, const unsigned &quad_boundary_id, const unsigned &n_pml_x, const unsigned &n_pml_y, const double &x_pml_start, const double &x_pml_end, const double &y_pml_start, const double &y_pml_end, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass pointer to "bulk" mesh, the boundary ID of axis aligned boundary to which the mesh ...
Definition pml_meshes.h:440
RectangularQuadMesh is a two-dimensional mesh of Quad elements with Nx elements in the "x" (horizonal...
const unsigned & ny() const
Return number of elements in y direction.
const double y_min() const
Return the minimum value of y coordinate.
const double x_max() const
Return the maximum value of x coordinate.
const double y_max() const
Return the maximum value of y coordinate.
const unsigned & nx() const
Return number of elements in x direction.
const double x_min() const
Return the minimum value of x coordinate.
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
bool sorter_bottom_boundary(Node *nod_i_pt, Node *nod_j_pt)
helper function for sorting the bottom boundary nodes
Definition pml_meshes.cc:57
Mesh * create_bottom_left_pml_mesh(Mesh *pml_left_mesh_pt, Mesh *pml_bottom_mesh_pt, Mesh *bulk_mesh_pt, const unsigned &left_boundary_id, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
"Constructor" for PML bottom left corner mesh, aligned with the existing PML meshes
Mesh * create_top_left_pml_mesh(Mesh *pml_left_mesh_pt, Mesh *pml_top_mesh_pt, Mesh *bulk_mesh_pt, const unsigned &left_boundary_id, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
"Constructor" for PML top left corner mesh, aligned with the existing PML meshes
Mesh * create_bottom_pml_mesh(Mesh *bulk_mesh_pt, const unsigned &bottom_boundary_id, const unsigned &n_y_bottom_pml, const double &width_y_bottom_pml, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
"Constructor" for PML mesh,aligned with the bottom physical domain boundary
Mesh * create_top_right_pml_mesh(Mesh *pml_right_mesh_pt, Mesh *pml_top_mesh_pt, Mesh *bulk_mesh_pt, const unsigned &right_boundary_id, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
"Constructor" for PML top right corner mesh, aligned with the existing PML meshes
bool sorter_top_boundary(Node *nod_i_pt, Node *nod_j_pt)
helper function for sorting the top boundary nodes
Definition pml_meshes.cc:45
Mesh * create_right_pml_mesh(Mesh *bulk_mesh_pt, const unsigned &right_boundary_id, const unsigned &n_x_right_pml, const double &width_x_right_pml, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
"Constructor" for PML mesh,aligned with the right physical domain boundary
Mesh * create_left_pml_mesh(Mesh *bulk_mesh_pt, const unsigned &left_boundary_id, const unsigned &n_x_left_pml, const double &width_x_left_pml, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
"Constructor" for PML mesh, aligned with the left physical domain boundary
Mesh * create_bottom_right_pml_mesh(Mesh *pml_right_mesh_pt, Mesh *pml_bottom_mesh_pt, Mesh *bulk_mesh_pt, const unsigned &right_boundary_id, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
"Constructor" for PML bottom right corner mesh, aligned with the existing PML meshes
bool sorter_right_boundary(Node *nod_i_pt, Node *nod_j_pt)
helper function for sorting the right boundary nodes
Definition pml_meshes.cc:39
Mesh * create_top_pml_mesh(Mesh *bulk_mesh_pt, const unsigned &top_boundary_id, const unsigned &n_y_top_pml, const double &width_y_top_pml, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
"Constructor" for PML mesh, aligned with the top physical domain boundary
bool sorter_left_boundary(Node *nod_i_pt, Node *nod_j_pt)
helper function for sorting the left boundary nodes
Definition pml_meshes.cc:51
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).