pml_fourier_decomposed_helmholtz_elements.cc
Go to the documentation of this file.
1 //LIC// ====================================================================
2 //LIC// This file forms part of oomph-lib, the object-oriented,
3 //LIC// multi-physics finite-element library, available
4 //LIC// at http://www.oomph-lib.org.
5 //LIC//
6 //LIC// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 //Non-inline functions for Helmholtz elements
32 
33 //////////////////////////////////////////////////////////////////////
34 //////////////////////////////////////////////////////////////////////
35 //////////////////////////////////////////////////////////////////////
36 
37 
38 namespace oomph
39 {
40 
41 
42  //========================================================================
43  /// Helper namespace for functions required for Helmholtz computations
44  //========================================================================
45  namespace Legendre_functions_helper
46  {
47 
48  //========================================================================
49  // Factorial
50  //========================================================================
51  double factorial(const unsigned& l)
52  {
53  if(l==0) return 1.0;
54  return double(l*factorial(l-1));
55  }
56 
57 
58  //========================================================================
59  /// Legendre polynomials depending on one parameter
60  //========================================================================
61  double plgndr1(const unsigned& n, const double& x)
62  {
63  unsigned i;
64  double pmm,pmm1;
65  double pmm2=0;
66 
67  #ifdef PARANOID
68  // Shout if things went wrong
69  if (std::fabs(x) > 1.0)
70  {
71  std::ostringstream error_stream;
72  error_stream << "Bad arguments in routine plgndr1: x=" << x
73  << " but should be less than 1 in absolute value.\n";
74  throw OomphLibError(error_stream.str(),
75  OOMPH_CURRENT_FUNCTION,
76  OOMPH_EXCEPTION_LOCATION);
77  }
78  #endif
79 
80  // Compute pmm : if l=m it's finished
81  pmm=1.0;
82  if (n == 0)
83  {
84  return pmm;
85  }
86 
87  pmm1=x*1.0;
88  if (n == 1)
89  {
90  return pmm1;
91  }
92  else
93  {
94  for (i=2;i<=n;i++)
95  {
96  pmm2=(x*(2*i-1)*pmm1-(i-1)*pmm)/i;
97  pmm=pmm1;
98  pmm1=pmm2;
99  }
100  return pmm2;
101  }
102 
103  }//end of plgndr1
104 
105 
106 
107  //========================================================================
108  // Legendre polynomials depending on two parameters
109  //========================================================================
110  double plgndr2(const unsigned& l, const unsigned& m, const double& x)
111  {
112  unsigned i,ll;
113  double fact,pmm,pmmp1,somx2;
114  double pll=0.0;
115 
116  #ifdef PARANOID
117  // Shout if things went wrong
118  if (std::fabs(x) > 1.0)
119  {
120  std::ostringstream error_stream;
121  error_stream << "Bad arguments in routine plgndr2: x=" << x
122  << " but should be less than 1 in absolute value.\n";
123  throw OomphLibError(error_stream.str(),
124  OOMPH_CURRENT_FUNCTION,
125  OOMPH_EXCEPTION_LOCATION);
126  }
127  #endif
128 
129  // This one is easy...
130  if (m > l)
131  {
132  return 0.0;
133  }
134 
135  // Compute pmm : if l=m it's finished
136  pmm=1.0;
137  if (m > 0)
138  {
139  somx2=sqrt((1.0-x)*(1.0+x));
140  fact=1.0;
141  for (i=1;i<=m;i++)
142  {
143  pmm *= -fact*somx2;
144  fact += 2.0;
145  }
146  }
147  if (l == m) return pmm;
148 
149  // Compute pmmp1 : if l=m+1 it's finished
150  else
151  {
152  pmmp1=x*(2*m+1)*pmm;
153  if (l == (m+1))
154  {
155  return pmmp1;
156  }
157  // Compute pll : if l>m+1 it's finished
158  else
159  {
160  for (ll=m+2;ll<=l;ll++)
161  {
162  pll=(x*(2*ll-1)*pmmp1-(ll+m-1)*pmm)/(ll-m);
163  pmm=pmmp1;
164  pmmp1=pll;
165  }
166  return pll;
167  }
168  }
169  }//end of plgndr2
170 
171  } // end namespace
172 
173 
174 
175 ///////////////////////////////////////////////////////////////////////
176 ///////////////////////////////////////////////////////////////////////
177 ///////////////////////////////////////////////////////////////////////
178 
179 
180 //======================================================================
181 /// Set the data for the number of Variables at each node, always two
182 /// (real and imag part) in every case
183 //======================================================================
184  template<unsigned NNODE_1D, class PML_ELEMENT>
185  const unsigned QPMLFourierDecomposedHelmholtzElement<NNODE_1D,PML_ELEMENT>::
186  Initial_Nvalue=2;
187 
188  /// PML Helmholtz equations static data, so that by default we can point to a 0
189  double PMLFourierDecomposedHelmholtzEquationsBase::
190  Default_Physical_Constant_Value = 0.0;
191 
192 //======================================================================
193 /// Compute element residual Vector and/or element Jacobian matrix
194 ///
195 /// flag=1: compute both
196 /// flag=0: compute only residual Vector
197 ///
198 /// Pure version without hanging nodes
199 //======================================================================
200  template<class PML_ELEMENT>
203  (Vector<double> &residuals,
204  DenseMatrix<double> &jacobian,
205  const unsigned& flag)
206  {
207  //Find out how many nodes there are
208  const unsigned n_node = nnode();
209 
210  //Set up memory for the shape and test functions
211  Shape psi(n_node), test(n_node);
212  DShape dpsidx(n_node,2), dtestdx(n_node,2);
213 
214  //Set the value of n_intpt
215  const unsigned n_intpt = integral_pt()->nweight();
216 
217  //Integers to store the local equation and unknown numbers
218  int local_eqn_real=0, local_unknown_real=0;
219  int local_eqn_imag=0, local_unknown_imag=0;
220 
221  //Loop over the integration points
222  for(unsigned ipt=0;ipt<n_intpt;ipt++)
223  {
224  //Get the integral weight
225  double w = integral_pt()->weight(ipt);
226 
227  //Call the derivatives of the shape and test functions
228  double J =
229  this->dshape_and_dtest_eulerian_at_knot_pml_fourier_decomposed_helmholtz
230  (ipt,psi,dpsidx,test,dtestdx);
231 
232  //Premultiply the weights and the Jacobian
233  double W = w*J;
234 
235  //Calculate local values of unknown
236  //Allocate and initialise to zero
237  std::complex<double> interpolated_u(0.0,0.0);
238  Vector<double> interpolated_x(2,0.0);
239  Vector< std::complex<double> > interpolated_dudx(2);
240 
241  //Calculate function value and derivatives:
242  //-----------------------------------------
243  // Loop over nodes
244  for(unsigned l=0;l<n_node;l++)
245  {
246  // Loop over directions
247  for(unsigned j=0;j<2;j++)
248  {
249  interpolated_x[j] += this->raw_nodal_position(l,j)*psi(l);
250  }
251 
252  //Get the nodal value of the helmholtz unknown
253  const std::complex<double> u_value(
254  this->raw_nodal_value
255  (l,this->u_index_pml_fourier_decomposed_helmholtz().real()),
256  this->raw_nodal_value
257  (l,this->u_index_pml_fourier_decomposed_helmholtz().imag()));
258 
259  //Add to the interpolated value
260  interpolated_u += u_value*psi(l);
261 
262  // Loop over directions
263  for(unsigned j=0;j<2;j++)
264  {
265  interpolated_dudx[j] += u_value*dpsidx(l,j);
266  }
267  }
268 
269  //Get source function
270  //-------------------
271  std::complex<double> source(0.0,0.0);
272  this->get_source_pml_fourier_decomposed_helmholtz
273  (ipt,interpolated_x,source);
274 
275  double n = (double)pml_fourier_wavenumber();
276  double n_squared = n*n;
277 
278  // Declare a complex number for pml weights on the mass matrix bit
279  std::complex<double> pml_k_squared_factor = std::complex<double>(1.0,0.0);
280 
281  // All the PML weights that participate in the assemby process
282  // are computed here. laplace_matrix will contain the entries
283  // for the Laplace bit, while pml_k_squared_factor contains the contributions
284  // to the Helmholtz bit. Both default to 1.0, should the PML not be
285  // enabled via enable_pml.
286  Vector<double> s(2);
287  DiagonalComplexMatrix pml_jacobian(2);
288  Vector<std::complex<double> > transformed_x(2);
289  this->pml_transformation_jacobian(ipt, s, interpolated_x, pml_jacobian,
290  transformed_x);
291 
292  DiagonalComplexMatrix laplace_matrix(2);
293  this->compute_laplace_matrix_and_det(pml_jacobian, laplace_matrix,
294  pml_k_squared_factor);
295 
296  // Determine the complex r variable. The variable is
297  // only complex once it enters the right pml domain or either
298  // of the two corner pml domains, otherwise it acts like the
299  // variable r.
300  std::complex<double> complex_r = transformed_x[0];
301 
302  // Calculate Jacobian
303  // ------------------
304  std::complex<double> pml_k_squared_jacobian =
305  - pml_k_squared_factor * ( k_squared() - n_squared / (complex_r * complex_r))
306  * complex_r * W;
307 
308  // Term from the Laplace operator
309  Vector<std::complex<double> > pml_laplace_jacobian(2);
310  for (unsigned k=0;k<2;k++)
311  {
312  pml_laplace_jacobian[k] = laplace_matrix(k,k)* complex_r * W;
313  }
314 
315  // Calculate residuals
316  // -------------------
317  // Note: it is a linear equation so residual = jacobian * u
318  std::complex<double> pml_k_squared_residual = pml_k_squared_jacobian
319  * interpolated_u;
320 
321  // Term from the Laplace operator
322  Vector<std::complex<double> > pml_laplace_residual(2);
323  for (unsigned k=0;k<2;k++)
324  {
325  pml_laplace_residual[k] = pml_laplace_jacobian[k] * interpolated_dudx[k];
326  }
327 
328  // Assemble residuals and Jacobian
329  //--------------------------------
330  // Loop over the test functions
331  for(unsigned l=0;l<n_node;l++)
332  {
333  //Get the equation numbers
334  local_eqn_real =
335  nodal_local_eqn
336  (l,u_index_pml_fourier_decomposed_helmholtz().real());
337  local_eqn_imag =
338  nodal_local_eqn
339  (l,u_index_pml_fourier_decomposed_helmholtz().imag());
340 
341  // first, add the real contributions
342  //-------------------------------------------
343 
344  /*IF it's not a boundary condition*/
345  if(local_eqn_real >= 0)
346  {
347  // Add k squared term of equation
348  residuals[local_eqn_real] += pml_k_squared_residual.real()*test(l);
349 
350  // Add the term from the Laplace operator
351  for (unsigned k=0;k<2;k++)
352  {
353  residuals[local_eqn_real]+=pml_laplace_residual[k].real()*dtestdx(l,k);
354  }
355 
356  // Add contributions to the jacobian
357  //----------------------------------
358  if(flag)
359  {
360  //Loop over the velocity shape functions again
361  for(unsigned l2=0;l2<n_node;l2++)
362  {
363  // Get the unknown numbers
364  local_unknown_real =
365  nodal_local_eqn
366  (l2,u_index_pml_fourier_decomposed_helmholtz().real());
367  local_unknown_imag =
368  nodal_local_eqn
369  (l2,u_index_pml_fourier_decomposed_helmholtz().imag());
370 
371  //If at a non-zero degree of freedom add in the entry
372  if(local_unknown_real >= 0)
373  {
374  // Add the helmholtz contribution to the jacobian
375  jacobian(local_eqn_real,local_unknown_real) +=
376  pml_k_squared_jacobian.real() * psi(l2)*test(l);
377 
378  // Add the term from the Laplace operator to the jacobian
379  for (unsigned k=0;k<2;k++)
380  {
381  jacobian(local_eqn_real,local_unknown_real) +=
382  pml_laplace_jacobian[k].real()*dpsidx(l2,k)*dtestdx(l,k);
383  }
384  }
385  //If at a non-zero degree of freedom add in the entry
386  if(local_unknown_imag >= 0)
387  {
388  // Add k squared term to jacobian
389  jacobian(local_eqn_real,local_unknown_imag) +=
390  - pml_k_squared_jacobian.imag()*psi(l2)*test(l);
391 
392  //Add contribution to elemental Matrix
393  for (unsigned k=0;k<2;k++)
394  {
395  jacobian(local_eqn_real,local_unknown_imag) +=
396  - pml_laplace_jacobian[k].imag()*dpsidx(l2,k)*dtestdx(l,k);
397  }
398  }
399 
400  } // end of loop over velocity shape functions
401  } // end of if(flag)
402  }
403 
404  // Second, add the imaginary contribution
405  //------------------------------------------------
406 
407  // IF it's not a boundary condition
408  if(local_eqn_imag >= 0)
409  {
410  // Add k squared term of equation
411  residuals[local_eqn_imag] += pml_k_squared_residual.imag()*test(l);
412 
413  // Add the term from the Laplace operator
414  for (unsigned k=0;k<2;k++)
415  {
416  residuals[local_eqn_imag]+=pml_laplace_residual[k].imag()*dtestdx(l,k);
417  }
418 
419  // Add the contribution to the jacobian
420  //-----------------------
421  if(flag)
422  {
423  //Loop over the velocity shape functions again
424  for(unsigned l2=0;l2<n_node;l2++)
425  {
426  local_unknown_imag = nodal_local_eqn
427  (l2,u_index_pml_fourier_decomposed_helmholtz().imag());
428  local_unknown_real =
429  nodal_local_eqn
430  (l2,u_index_pml_fourier_decomposed_helmholtz().real());
431 
432  //If at a non-zero degree of freedom add in the entry
433  if(local_unknown_imag >= 0)
434  {
435  //Add the helmholtz contribution
436  jacobian(local_eqn_imag,local_unknown_imag) +=
437  pml_k_squared_jacobian.real()*psi(l2)*test(l);
438 
439  //Add contribution to elemental Matrix
440  for (unsigned k=0;k<2;k++)
441  {
442  jacobian(local_eqn_imag,local_unknown_imag) +=
443  pml_laplace_jacobian[k].real()*dpsidx(l2,k)*dtestdx(l,k);
444  }
445  }
446  //If at a non-zero degree of freedom add in the entry
447  if(local_unknown_real >= 0)
448  {
449  //Add the helmholtz contribution
450  jacobian(local_eqn_imag,local_unknown_real) +=
451  pml_k_squared_jacobian.imag()*psi(l2)*test(l);
452 
453  //Add contribution to elemental Matrix
454  for (unsigned k=0;k<2;k++)
455  {
456  jacobian(local_eqn_imag,local_unknown_real) +=
457  pml_laplace_jacobian[k].imag()*dpsidx(l2,k)*dtestdx(l,k);
458  }
459  }
460  }
461  }
462  }
463  }
464  } // End of loop over integration points
465  }
466 
467 
468 
469 
470 
471 
472 //======================================================================
473 /// Self-test: Return 0 for OK
474 //======================================================================
476  {
477 
478  bool passed=true;
479 
480  // Check lower-level stuff
481  if (FiniteElement::self_test()!=0)
482  {
483  passed=false;
484  }
485 
486  // Return verdict
487  if (passed)
488  {
489  return 0;
490  }
491  else
492  {
493  return 1;
494  }
495  }
496 
497 
498 
499 //======================================================================
500 /// Output function:
501 ///
502 /// r,z,u_re,u_imag
503 ///
504 /// nplot points in each coordinate direction
505 //======================================================================
507  (std::ostream &outfile,
508  const unsigned &nplot)
509  {
510 
511  //Vector of local coordinates
512  Vector<double> s(2);
513 
514  // Tecplot header info
515  outfile << tecplot_zone_string(nplot);
516 
517  // Loop over plot points
518  unsigned num_plot_points=nplot_points(nplot);
519  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
520  {
521 
522  // Get local coordinates of plot point
523  get_s_plot(iplot,nplot,s);
524  std::complex<double>
525  u(interpolated_u_pml_fourier_decomposed_helmholtz(s));
526  for(unsigned i=0;i<2;i++)
527  {
528  outfile << interpolated_x(s,i) << " ";
529  }
530  outfile << u.real() << " " << u.imag() << std::endl;
531 
532  }
533 
534  // Write tecplot footer (e.g. FE connectivity lists)
535  write_tecplot_zone_footer(outfile,nplot);
536 
537  }
538 
539 
540 
541 
542 //======================================================================
543 /// Output function for real part of full time-dependent solution
544 ///
545 /// u = Re( (u_r +i u_i) exp(-i omega t)
546 ///
547 /// at phase angle omega t = phi.
548 ///
549 /// r,z,u
550 ///
551 /// Output at nplot points in each coordinate direction
552 //======================================================================
554  (std::ostream &outfile,
555  const double& phi,
556  const unsigned &nplot)
557  {
558  //Vector of local coordinates
559  Vector<double> s(2);
560 
561  // Tecplot header info
562  outfile << tecplot_zone_string(nplot);
563 
564  // Loop over plot points
565  unsigned num_plot_points=nplot_points(nplot);
566  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
567  {
568 
569  // Get local coordinates of plot point
570  get_s_plot(iplot,nplot,s);
571  std::complex<double>
572  u(interpolated_u_pml_fourier_decomposed_helmholtz(s));
573  for(unsigned i=0;i<2;i++)
574  {
575  outfile << interpolated_x(s,i) << " ";
576  }
577  outfile << u.real()*cos(phi)+u.imag()*sin(phi) << std::endl;
578 
579  }
580 
581  // Write tecplot footer (e.g. FE connectivity lists)
582  write_tecplot_zone_footer(outfile,nplot);
583 
584  }
585 
586 
587 //======================================================================
588 /// C-style output function:
589 ///
590 /// r,z,u
591 ///
592 /// nplot points in each coordinate direction
593 //======================================================================
595  const unsigned &nplot)
596  {
597  //Vector of local coordinates
598  Vector<double> s(2);
599 
600  // Tecplot header info
601  fprintf(file_pt,"%s",tecplot_zone_string(nplot).c_str());
602 
603  // Loop over plot points
604  unsigned num_plot_points=nplot_points(nplot);
605  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
606  {
607  // Get local coordinates of plot point
608  get_s_plot(iplot,nplot,s);
609  std::complex<double>
610  u(interpolated_u_pml_fourier_decomposed_helmholtz(s));
611 
612  for(unsigned i=0;i<2;i++)
613  {
614  fprintf(file_pt,"%g ",interpolated_x(s,i));
615  }
616 
617  for(unsigned i=0;i<2;i++)
618  {
619  fprintf(file_pt,"%g ",interpolated_x(s,i));
620  }
621  fprintf(file_pt,"%g ",u.real());
622  fprintf(file_pt,"%g \n",u.imag());
623 
624  }
625 
626  // Write tecplot footer (e.g. FE connectivity lists)
627  write_tecplot_zone_footer(file_pt,nplot);
628  }
629 
630 
631 
632 //======================================================================
633  /// Output exact solution
634  ///
635  /// Solution is provided via function pointer.
636  /// Plot at a given number of plot points.
637  ///
638  /// r,z,u_exact
639 //======================================================================
641  std::ostream &outfile,
642  const unsigned &nplot,
644  {
645  //Vector of local coordinates
646  Vector<double> s(2);
647 
648  // Vector for coordintes
649  Vector<double> x(2);
650 
651  // Tecplot header info
652  outfile << tecplot_zone_string(nplot);
653 
654  // Exact solution Vector
655  Vector<double> exact_soln(2);
656 
657  // Loop over plot points
658  unsigned num_plot_points=nplot_points(nplot);
659  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
660  {
661  // Get local coordinates of plot point
662  get_s_plot(iplot,nplot,s);
663 
664  // Get x position as Vector
665  interpolated_x(s,x);
666 
667  // Get exact solution at this point
668  (*exact_soln_pt)(x,exact_soln);
669 
670  //Output r,z,u_exact
671  for(unsigned i=0;i<2;i++)
672  {
673  outfile << x[i] << " ";
674  }
675  outfile << exact_soln[0] << " " << exact_soln[1] << std::endl;
676  }
677 
678  // Write tecplot footer (e.g. FE connectivity lists)
679  write_tecplot_zone_footer(outfile,nplot);
680  }
681 
682 
683 
684 //======================================================================
685 /// Output function for real part of full time-dependent fct
686 ///
687 /// u = Re( (u_r +i u_i) exp(-i omega t)
688 ///
689 /// at phase angle omega t = phi.
690 ///
691 /// r,z,u
692 ///
693 /// Output at nplot points in each coordinate direction
694 //======================================================================
696  std::ostream &outfile,
697  const double& phi,
698  const unsigned &nplot,
700  {
701  //Vector of local coordinates
702  Vector<double> s(2);
703 
704  // Vector for coordintes
705  Vector<double> x(2);
706 
707  // Tecplot header info
708  outfile << tecplot_zone_string(nplot);
709 
710  // Exact solution Vector
711  Vector<double> exact_soln(2);
712 
713  // Loop over plot points
714  unsigned num_plot_points=nplot_points(nplot);
715  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
716  {
717  // Get local coordinates of plot point
718  get_s_plot(iplot,nplot,s);
719 
720  // Get x position as Vector
721  interpolated_x(s,x);
722 
723  // Get exact solution at this point
724  (*exact_soln_pt)(x,exact_soln);
725 
726  //Output x,y,...,u_exact
727  for(unsigned i=0;i<2;i++)
728  {
729  outfile << x[i] << " ";
730  }
731  outfile << exact_soln[0]*cos(phi)+ exact_soln[1]*sin(phi) << std::endl;
732  }
733 
734  // Write tecplot footer (e.g. FE connectivity lists)
735  write_tecplot_zone_footer(outfile,nplot);
736  }
737 
738 
739 
740 
741 //======================================================================
742  /// Validate against exact solution
743  ///
744  /// Solution is provided via function pointer.
745  /// Plot error at a given number of plot points.
746  ///
747 //======================================================================
749  std::ostream &outfile,
751  double& error, double& norm)
752  {
753 
754  // Initialise
755  error=0.0;
756  norm=0.0;
757 
758  //Vector of local coordinates
759  Vector<double> s(2);
760 
761  // Vector for coordintes
762  Vector<double> x(2);
763 
764  //Find out how many nodes there are in the element
765  unsigned n_node = nnode();
766 
767  Shape psi(n_node);
768 
769  //Set the value of n_intpt
770  unsigned n_intpt = integral_pt()->nweight();
771 
772  // Tecplot
773  outfile << "ZONE" << std::endl;
774 
775  // Exact solution Vector
776  Vector<double> exact_soln(2);
777 
778  //Loop over the integration points
779  for(unsigned ipt=0;ipt<n_intpt;ipt++)
780  {
781 
782  //Assign values of s
783  for(unsigned i=0;i<2;i++)
784  {
785  s[i] = integral_pt()->knot(ipt,i);
786  }
787 
788  //Get the integral weight
789  double w = integral_pt()->weight(ipt);
790 
791  // Get jacobian of mapping
792  double J=J_eulerian(s);
793 
794  //Premultiply the weights and the Jacobian
795  double W = w*J;
796 
797  // Get x position as Vector
798  interpolated_x(s,x);
799 
800  // Get FE function value
801  std::complex<double> u_fe=
802  interpolated_u_pml_fourier_decomposed_helmholtz(s);
803 
804  // Get exact solution at this point
805  (*exact_soln_pt)(x,exact_soln);
806 
807  //Output r,z,error
808  for(unsigned i=0;i<2;i++)
809  {
810  outfile << x[i] << " ";
811  }
812  outfile << exact_soln[0] << " " << exact_soln[1] << " "
813  << exact_soln[0]-u_fe.real() << " " << exact_soln[1]-u_fe.imag()
814  << std::endl;
815 
816  // Add to error and norm
817  norm+=(exact_soln[0]*exact_soln[0]+exact_soln[1]*exact_soln[1])*W;
818  error+=( (exact_soln[0]-u_fe.real())*(exact_soln[0]-u_fe.real())+
819  (exact_soln[1]-u_fe.imag())*(exact_soln[1]-u_fe.imag()) )*W;
820 
821  }
822  }
823 
824 
825 
826 //======================================================================
827  /// Compute norm of fe solution
828 //======================================================================
830  (double& norm)
831  {
832 
833  // Initialise
834  norm=0.0;
835 
836  //Vector of local coordinates
837  Vector<double> s(2);
838 
839  // Vector for coordintes
840  Vector<double> x(2);
841 
842  //Find out how many nodes there are in the element
843  unsigned n_node = nnode();
844 
845  Shape psi(n_node);
846 
847  //Set the value of n_intpt
848  unsigned n_intpt = integral_pt()->nweight();
849 
850  //Loop over the integration points
851  for(unsigned ipt=0;ipt<n_intpt;ipt++)
852  {
853 
854  //Assign values of s
855  for(unsigned i=0;i<2;i++)
856  {
857  s[i] = integral_pt()->knot(ipt,i);
858  }
859 
860  //Get the integral weight
861  double w = integral_pt()->weight(ipt);
862 
863  // Get jacobian of mapping
864  double J=J_eulerian(s);
865 
866  //Premultiply the weights and the Jacobian
867  double W = w*J;
868 
869  // Get FE function value
870  std::complex<double> u_fe=
871  interpolated_u_pml_fourier_decomposed_helmholtz(s);
872 
873  // Add to norm
874  norm+=(u_fe.real()*u_fe.real()+u_fe.imag()*u_fe.imag())*W;
875 
876  }
877  }
878 
879 
880 
881 
882 
883 //====================================================================
884 // Force build of templates
885 //====================================================================
886 
887 template<class PML_ELEMENT>
889 
890 
894 
895 }
double factorial(const unsigned &l)
Factorial.
void output_real_fct(std::ostream &outfile, const double &phi, const unsigned &n_plot, FiniteElement::SteadyExactSolutionFctPt exact_soln_pt)
Output function for real part of full time-dependent fct u = Re( (u_r +i u_i) exp(-i omega t) at phas...
cstr elem_len * i
Definition: cfortran.h:607
virtual void fill_in_generic_residual_contribution_pml_fourier_decomposed_helmholtz(Vector< double > &residuals, DenseMatrix< double > &jacobian, const unsigned &flag)
Compute element residual Vector only (if flag=and/or element Jacobian matrix.
virtual unsigned self_test()
Self-test: Check inversion of element & do self-test for GeneralisedElement. Return 0 if OK...
Definition: elements.cc:4333
double plgndr1(const unsigned &n, const double &x)
Legendre polynomials depending on one parameter.
void(* SteadyExactSolutionFctPt)(const Vector< double > &, Vector< double > &)
Function pointer for function that computes vector-valued steady "exact solution" as ...
Definition: elements.h:1723
void output_fct(std::ostream &outfile, const unsigned &n_plot, FiniteElement::SteadyExactSolutionFctPt exact_soln_pt)
static char t char * s
Definition: cfortran.h:572
void compute_error(std::ostream &outfile, FiniteElement::SteadyExactSolutionFctPt exact_soln_pt, double &error, double &norm)
Get error against and norm of exact solution.
void output_real(std::ostream &outfile, const double &phi, const unsigned &n_plot)
Output function for real part of full time-dependent solution u = Re( (u_r +i u_i) exp(-i omega t) at...
void output(std::ostream &outfile)
Output with default number of plot points.
double plgndr2(const unsigned &l, const unsigned &m, const double &x)
Legendre polynomials depending on two parameters.