refineable_pml_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//====================================================================
31 
32 
33 namespace oomph
34 {
35 
36 
37 
38 //======================================================================
39 /// Compute element residual Vector and/or element Jacobian matrix
40 ///
41 /// flag=1: compute both
42 /// flag=0: compute only residual Vector
43 ///
44 /// Pure version without hanging nodes
45 //======================================================================
46 template <unsigned DIM, class PML_ELEMENT>
49  DenseMatrix<double> &jacobian,
50  const unsigned& flag)
51 {
52  //Find out how many nodes there are
53  const unsigned n_node = nnode();
54 
55  //Set up memory for the shape and test functions
56  Shape psi(n_node), test(n_node);
57  DShape dpsidx(n_node,DIM), dtestdx(n_node,DIM);
58 
59  // Local storage for pointers to hang_info objects
60  HangInfo *hang_info_pt=0, *hang_info2_pt=0;
61 
62  //Set the value of n_intpt
63  const unsigned n_intpt = integral_pt()->nweight();
64 
65  //Integers to store the local equation and unknown numbers
66  int local_eqn_real=0, local_unknown_real=0;
67  int local_eqn_imag=0, local_unknown_imag=0;
68 
69  //Loop over the integration points
70  for(unsigned ipt=0;ipt<n_intpt;ipt++)
71  {
72  //Get the integral weight
73  double w = integral_pt()->weight(ipt);
74 
75  //Call the derivatives of the shape and test functions
76  double J = this->dshape_and_dtest_eulerian_at_knot_helmholtz(ipt,psi,dpsidx,
77  test,dtestdx);
78 
79  //Premultiply the weights and the Jacobian
80  double W = w*J;
81 
82  //Calculate local values of unknown
83  //Allocate and initialise to zero
84  std::complex<double> interpolated_u(0.0,0.0);
85  Vector<double> interpolated_x(DIM,0.0);
86  Vector< std::complex<double> > interpolated_dudx(DIM);
87 
88  //Calculate function value and derivatives:
89  //-----------------------------------------
90  // Loop over nodes
91  for(unsigned l=0;l<n_node;l++)
92  {
93  // Loop over directions
94  for(unsigned j=0;j<DIM;j++)
95  {
96  interpolated_x[j] += nodal_position(l,j)*psi(l);
97  }
98 
99  //Get the nodal value of the helmholtz unknown
100  const std::complex<double>
101  u_value(this->nodal_value(l,this->u_index_helmholtz().real()),
102  this->nodal_value(l,this->u_index_helmholtz().imag()));
103 
104  //Add to the interpolated value
105  interpolated_u += u_value*psi(l);
106 
107  // Loop over directions
108  for(unsigned j=0;j<DIM;j++)
109  {
110  interpolated_dudx[j] += u_value*dpsidx(l,j);
111  }
112  }
113 
114  //Get source function
115  //-------------------
116  std::complex<double> source(0.0,0.0);
117  this->get_source_helmholtz(ipt,interpolated_x,source);
118 
119 
120  // Declare a vector of complex numbers for pml weights on the Laplace bit
121  Vector< std::complex<double> > pml_laplace_factor(DIM);
122  // Declare a complex number for pml weights on the mass matrix bit
123  std::complex<double> pml_k_squared_factor = std::complex<double>(1.0,0.0);
124 
125  // All the PML weights that participate in the assemby process
126  // are computed here. pml_laplace_factor will contain the entries
127  // for the Laplace bit, while pml_k_squared_factor contains the contributions
128  // to the Helmholtz bit. Both default to 1.0, should the PML not be
129  // enabled via enable_pml.
130  Vector<double> s(DIM);
131  DiagonalComplexMatrix pml_jacobian(DIM);
132  this->pml_transformation_jacobian(ipt, s, interpolated_x, pml_jacobian);
133 
134  DiagonalComplexMatrix laplace_matrix(DIM);
135  this->compute_laplace_matrix_and_det(pml_jacobian, laplace_matrix, pml_k_squared_factor);
136 
137  for (unsigned i=0; i<DIM; i++)
138  {
139  pml_laplace_factor[i] = laplace_matrix(i,i);
140  }
141 
142  //Alpha adjusts the pml factors, the imaginary part produces cross terms
143  std::complex<double> alpha_pml_k_squared_factor = std::complex<double>(
144  pml_k_squared_factor.real() - this->alpha() * pml_k_squared_factor.imag(),
145  this->alpha() * pml_k_squared_factor.real() + pml_k_squared_factor.imag()
146  );
147 
148 
149  // std::complex<double> alpha_pml_k_squared_factor
150  // if(alpha_pt() == 0)
151  // {
152  // std::complex<double> alpha_pml_k_squared_factor = std::complex<double>(
153  // pml_k_squared_factor.real() - alpha() * pml_k_squared_factor.imag(),
154  // alpha() * pml_k_squared_factor.real() + pml_k_squared_factor.imag()
155  // );
156  // }
157  // Assemble residuals and Jacobian
158  //--------------------------------
159  // Loop over the test functions
160  for(unsigned l=0;l<n_node;l++)
161  {
162 
163  //Local variables used to store the number of master nodes and the
164  //weight associated with the shape function if the node is hanging
165  unsigned n_master=1; double hang_weight=1.0;
166 
167  //Local bool (is the node hanging)
168  bool is_node_hanging = this->node_pt(l)->is_hanging();
169 
170  //If the node is hanging, get the number of master nodes
171  if(is_node_hanging)
172  {
173  hang_info_pt = this->node_pt(l)->hanging_pt();
174  n_master = hang_info_pt->nmaster();
175  }
176  //Otherwise there is just one master node, the node itself
177  else
178  {
179  n_master = 1;
180  }
181 
182  //Loop over the master nodes
183  for(unsigned m=0;m<n_master;m++)
184  {
185  //Get the local equation number and hang_weight
186  //If the node is hanging
187  if(is_node_hanging)
188  {
189  //Read out the local equation number from the m-th master node
190  local_eqn_real =
191  this->local_hang_eqn(hang_info_pt->master_node_pt(m),
192  this->u_index_helmholtz().real());
193 
194  local_eqn_imag =
195  this->local_hang_eqn(hang_info_pt->master_node_pt(m),
196  this->u_index_helmholtz().imag());
197 
198  //Read out the weight from the master node
199  hang_weight = hang_info_pt->master_weight(m);
200  }
201  //If the node is not hanging
202  else
203  {
204  //The local equation number comes from the node itself
205  local_eqn_real =
206  this->nodal_local_eqn(l,this->u_index_helmholtz().real());
207  local_eqn_imag =
208  this->nodal_local_eqn(l,this->u_index_helmholtz().imag());
209 
210  //The hang weight is one
211  hang_weight = 1.0;
212  }
213 
214  // first, compute the real part contribution
215  //-------------------------------------------
216 
217  /*IF it's not a boundary condition*/
218  if(local_eqn_real >= 0)
219  {
220  // Add body force/source term and Helmholtz bit
221  residuals[local_eqn_real] +=
222  ( source.real() -
223  (
224  alpha_pml_k_squared_factor.real() *
225  this->k_squared() * interpolated_u.real()
226  -alpha_pml_k_squared_factor.imag() *
227  this->k_squared() * interpolated_u.imag()
228  )
229  )*test(l)*W*hang_weight;
230 
231  // The Laplace bit
232  for(unsigned k=0;k<DIM;k++)
233  {
234  residuals[local_eqn_real] +=
235  (
236  pml_laplace_factor[k].real() * interpolated_dudx[k].real()
237  -pml_laplace_factor[k].imag() * interpolated_dudx[k].imag()
238  )*dtestdx(l,k)*W*hang_weight;
239  }
240 
241  // Calculate the jacobian
242  //-----------------------
243  if(flag)
244  {
245  //Local variables to store the number of master nodes
246  //and the weights associated with each hanging node
247  unsigned n_master2=1; double hang_weight2=1.0;
248 
249  //Loop over the nodes for the variables
250  for(unsigned l2=0;l2<n_node;l2++)
251  {
252  //Local bool (is the node hanging)
253  bool is_node2_hanging = this->node_pt(l2)->is_hanging();
254 
255  //If the node is hanging, get the number of master nodes
256  if(is_node2_hanging)
257  {
258  hang_info2_pt = this->node_pt(l2)->hanging_pt();
259  n_master2 = hang_info2_pt->nmaster();
260  }
261  //Otherwise there is one master node, the node itself
262  else
263  {
264  n_master2 = 1;
265  }
266 
267  //Loop over the master nodes
268  for(unsigned m2=0;m2<n_master2;m2++)
269  {
270  //Get the local unknown and weight
271  //If the node is hanging
272  if(is_node2_hanging)
273  {
274  //Read out the local unknown from the master node
275  local_unknown_real =
276  this->local_hang_eqn(hang_info2_pt->master_node_pt(m2),
277  this->u_index_helmholtz().real());
278  local_unknown_imag =
279  this->local_hang_eqn(hang_info2_pt->master_node_pt(m2),
280  this->u_index_helmholtz().imag());
281 
282  //Read out the hanging weight from the master node
283  hang_weight2 = hang_info2_pt->master_weight(m2);
284  }
285  //If the node is not hanging
286  else
287  {
288  //The local unknown number comes from the node
289  local_unknown_real =
290  this->nodal_local_eqn(l2,this->u_index_helmholtz().real());
291 
292  local_unknown_imag =
293  this->nodal_local_eqn(l2,this->u_index_helmholtz().imag());
294 
295  //The hang weight is one
296  hang_weight2 = 1.0;
297  }
298 
299 
300  //If at a non-zero degree of freedom add in the entry
301  if(local_unknown_real >= 0)
302  {
303  //Add contribution to Elemental Matrix
304  for(unsigned i=0;i<DIM;i++)
305  {
306  jacobian(local_eqn_real,local_unknown_real)
307  += pml_laplace_factor[i].real() *
308  dpsidx(l2,i)*dtestdx(l,i)*
309  W*hang_weight*hang_weight2;
310  }
311  // Add the helmholtz contribution
312  jacobian(local_eqn_real,local_unknown_real)
313  += -alpha_pml_k_squared_factor.real() *
314  this->k_squared()*psi(l2)*test(l)*
315  W*hang_weight*hang_weight2;
316  }
317  //If at a non-zero degree of freedom add in the entry
318  if(local_unknown_imag >= 0)
319  {
320  //Add contribution to Elemental Matrix
321  for(unsigned i=0;i<DIM;i++)
322  {
323  jacobian(local_eqn_real,local_unknown_imag)
324  -= pml_laplace_factor[i].imag() *
325  dpsidx(l2,i)*dtestdx(l,i)*
326  W*hang_weight*hang_weight2;
327  }
328  // Add the helmholtz contribution
329  jacobian(local_eqn_real,local_unknown_imag)
330  += alpha_pml_k_squared_factor.imag() *
331  this->k_squared()*psi(l2)*test(l)*
332  W*hang_weight*hang_weight2;
333  }
334  }
335  }
336  }
337  }
338 
339  // Second, compute the imaginary part contribution
340  //------------------------------------------------
341 
342  /*IF it's not a boundary condition*/
343  if(local_eqn_imag >= 0)
344  {
345  // Add body force/source term and Helmholtz bit
346  residuals[local_eqn_imag] +=
347  ( source.imag() -
348  (
349  alpha_pml_k_squared_factor.imag() *
350  this->k_squared()*interpolated_u.real()
351  + alpha_pml_k_squared_factor.real() *
352  this->k_squared()*interpolated_u.imag()
353  )
354  )*test(l)*W*hang_weight;
355 
356  // The Laplace bit
357  for(unsigned k=0;k<DIM;k++)
358  {
359  residuals[local_eqn_imag] += (
360  pml_laplace_factor[k].imag() * interpolated_dudx[k].real()
361  +pml_laplace_factor[k].real() * interpolated_dudx[k].imag()
362  )*dtestdx(l,k)*W*hang_weight;
363  }
364 
365  // Calculate the jacobian
366  //-----------------------
367  if(flag)
368  {
369  //Local variables to store the number of master nodes
370  //and the weights associated with each hanging node
371  unsigned n_master2=1; double hang_weight2=1.0;
372 
373  //Loop over the nodes for the variables
374  for(unsigned l2=0;l2<n_node;l2++)
375  {
376  //Local bool (is the node hanging)
377  bool is_node2_hanging = this->node_pt(l2)->is_hanging();
378 
379  //If the node is hanging, get the number of master nodes
380  if(is_node2_hanging)
381  {
382  hang_info2_pt = this->node_pt(l2)->hanging_pt();
383  n_master2 = hang_info2_pt->nmaster();
384  }
385  //Otherwise there is one master node, the node itself
386  else
387  {
388  n_master2 = 1;
389  }
390 
391  //Loop over the master nodes
392  for(unsigned m2=0;m2<n_master2;m2++)
393  {
394  //Get the local unknown and weight
395  //If the node is hanging
396  if(is_node2_hanging)
397  {
398  //Read out the local unknown from the master node
399  local_unknown_real =
400  this->local_hang_eqn(hang_info2_pt->master_node_pt(m2),
401  this->u_index_helmholtz().real());
402  local_unknown_imag =
403  this->local_hang_eqn(hang_info2_pt->master_node_pt(m2),
404  this->u_index_helmholtz().imag());
405 
406  //Read out the hanging weight from the master node
407  hang_weight2 = hang_info2_pt->master_weight(m2);
408  }
409  //If the node is not hanging
410  else
411  {
412  //The local unknown number comes from the node
413  local_unknown_real =
414  this->nodal_local_eqn(l2,this->u_index_helmholtz().real());
415 
416  local_unknown_imag =
417  this->nodal_local_eqn(l2,this->u_index_helmholtz().imag());
418 
419  //The hang weight is one
420  hang_weight2 = 1.0;
421  }
422 
423  //If at a non-zero degree of freedom add in the entry
424  if(local_unknown_imag >= 0)
425  {
426  //Add contribution to Elemental Matrix
427  for(unsigned i=0;i<DIM;i++)
428  {
429  jacobian(local_eqn_imag,local_unknown_imag)
430  += pml_laplace_factor[i].real() *
431  dpsidx(l2,i)*dtestdx(l,i)*
432  W*hang_weight*hang_weight2;
433  }
434  // Add the helmholtz contribution
435  jacobian(local_eqn_imag,local_unknown_imag)
436  += -alpha_pml_k_squared_factor.real()*
437  this->k_squared() * psi(l2)*test(l)*
438  W*hang_weight*hang_weight2;
439  }
440  if(local_unknown_real >= 0)
441  {
442  //Add contribution to Elemental Matrix
443  for(unsigned i=0;i<DIM;i++)
444  {
445  jacobian(local_eqn_imag,local_unknown_real)
446  +=pml_laplace_factor[i].imag()*dpsidx(l2,i)*dtestdx(l,i)*
447  W*hang_weight*hang_weight2;
448  }
449  // Add the helmholtz contribution
450  jacobian(local_eqn_imag,local_unknown_real)
451  += -alpha_pml_k_squared_factor.imag()*
452  this->k_squared() * psi(l2)*test(l)*
453  W*hang_weight*hang_weight2;
454  }
455  }
456  }
457  }
458  }
459  }
460  }
461 
462  } // End of loop over integration points
463 }
464 
465 
466 
467 
468 
469 
470 //====================================================================
471 // Force build of templates
472 //====================================================================
476 
480 
484 
485 }
cstr elem_len * i
Definition: cfortran.h:607
double const & master_weight(const unsigned &i) const
Return weight for dofs on i-th master node.
Definition: nodes.h:753
unsigned nmaster() const
Return the number of master nodes.
Definition: nodes.h:733
void fill_in_generic_residual_contribution_helmholtz(Vector< double > &residuals, DenseMatrix< double > &jacobian, const unsigned &flag)
Add element&#39;s contribution to elemental residual vector and/or Jacobian matrix flag=1: compute both f...
static char t char * s
Definition: cfortran.h:572
Node *const & master_node_pt(const unsigned &i) const
Return a pointer to the i-th master node.
Definition: nodes.h:736
Class that contains data for hanging nodes.
Definition: nodes.h:684