impose_parallel_outflow_element.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-2023 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_IMPOSE_PARALL_ELEMENTS_HEADER
27 #define OOMPH_IMPOSE_PARALL_ELEMENTS_HEADER
28 
29 // Config header generated by autoconfig
30 #ifdef HAVE_CONFIG_H
31 #include <oomph-lib-config.h>
32 #endif
33 
34 namespace oomph
35 {
36  //========================================================================
37  /// ImposeParallelOutflowElement
38  /// are elements that coincide with the faces of
39  /// higher-dimensional "bulk" elements. They are used on
40  /// boundaries where we would like to impose parallel outflow and
41  /// impose the pressure.
42  //========================================================================
43  template<class ELEMENT>
44  class ImposeParallelOutflowElement : public virtual FaceGeometry<ELEMENT>,
45  public virtual FaceElement
46  {
47  private:
48  /// pointer to imposed pressure -- if null then no pressure imposed.
49  double* Pressure_pt;
50 
51  /// Lagrange Id
52  unsigned Id;
53 
54 
55  public:
56  /// Constructor takes a "bulk" element, the
57  /// index that identifies which face the
58  /// ImposeParallelOutflowElement is supposed
59  /// to be attached to, and the face element ID
61  const int& face_index,
62  const unsigned& id = 0)
63  : FaceGeometry<ELEMENT>(), FaceElement()
64  {
65  // set the Id
66  Id = id;
67 
68  // Build the face element
69  element_pt->build_face_element(face_index, this);
70 
71  // dimension of the bulk element
72  unsigned dim = element_pt->dim();
73 
74  // we need dim-1 additional values for each FaceElement node
75  Vector<unsigned> n_additional_values(nnode(), dim - 1);
76 
77  // add storage for lagrange multipliers and set the map containing
78  // the position of the first entry of this face element's
79  // additional values.
80  add_additional_values(n_additional_values, id);
81 
82  // set the pressure pointer to zero
83  Pressure_pt = 0;
84  }
85 
86  /// Fill in the residuals
88  {
89  // Call the generic routine with the flag set to 0
91  residuals, GeneralisedElement::Dummy_matrix, 0);
92  }
93 
94  /// Fill in contribution from Jacobian
96  DenseMatrix<double>& jacobian)
97  {
98  // Call the generic routine with the flag set to 1
100  residuals, jacobian, 1);
101  }
102 
103  /// Overload the output function
104  void output(std::ostream& outfile)
105  {
106  FiniteElement::output(outfile);
107  }
108 
109  /// Output function: x,y,[z],u,v,[w],p in tecplot format
110  void output(std::ostream& outfile, const unsigned& nplot)
111  {
112  FiniteElement::output(outfile, nplot);
113  }
114 
115  /// The "global" intrinsic coordinate of the element when
116  /// viewed as part of a geometric object should be given by
117  /// the FaceElement representation, by default
118  /// This final over-ride is required because both SolidFiniteElements
119  /// and FaceElements overload zeta_nodal
120  double zeta_nodal(const unsigned& n,
121  const unsigned& k,
122  const unsigned& i) const
123  {
124  return FaceElement::zeta_nodal(n, k, i);
125  }
126 
127 
128  /// Access function for the pressure
129  double*& pressure_pt()
130  {
131  return Pressure_pt;
132  }
133 
134  protected:
135  /// Helper function to compute the residuals and, if flag==1, the
136  /// Jacobian
138  Vector<double>& residuals,
139  DenseMatrix<double>& jacobian,
140  const unsigned& flag)
141  {
142  // Find out how many nodes there are
143  unsigned n_node = nnode();
144 
145  // Dimension of element
146  unsigned dim_el = dim();
147 
148  // Set up memory for the shape functions
149  Shape psi(n_node);
150 
151  // Set the value of n_intpt
152  unsigned n_intpt = integral_pt()->nweight();
153 
154  // to store local equation number
155  int local_eqn = 0;
156  int local_unknown = 0;
157 
158  // to store normal vector
159  Vector<double> norm_vec(dim_el + 1);
160 
161  // to store tangantial vectors
162  Vector<Vector<double>> tang_vec(dim_el, Vector<double>(dim_el + 1));
163 
164  // get the value at which the velocities are stored
165  Vector<unsigned> u_index(dim_el + 1);
166  ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->bulk_element_pt());
167  for (unsigned i = 0; i < dim_el + 1; i++)
168  {
169  u_index[i] = el_pt->u_index_nst(i);
170  }
171 
172  // Loop over the integration points
173  for (unsigned ipt = 0; ipt < n_intpt; ipt++)
174  {
175  // Get the integral weight
176  double w = integral_pt()->weight(ipt);
177 
178  // Jacobian of mapping
179  double J = J_eulerian_at_knot(ipt);
180 
181  // Premultiply the weights and the Jacobian
182  double W = w * J;
183 
184  // Calculate the shape functions
185  shape_at_knot(ipt, psi);
186 
187  // compute the velocity and the Lagrange multiplier
188  Vector<double> interpolated_u(dim_el + 1, 0.0);
189  Vector<double> lambda(dim_el, 0.0);
190  // Loop over nodes
191  for (unsigned j = 0; j < n_node; j++)
192  {
193  // Assemble the velocity component
194  for (unsigned i = 0; i < dim_el + 1; i++)
195  {
196  interpolated_u[i] += nodal_value(j, u_index[i]) * psi(j);
197  }
198 
199  // Cast to a boundary node
200  BoundaryNodeBase* bnod_pt =
201  dynamic_cast<BoundaryNodeBase*>(node_pt(j));
202 
203  // get the node
204  Node* nod_pt = node_pt(j);
205 
206  // Get the index of the first nodal value associated with
207  // this FaceElement
208  unsigned first_index =
210 
211  // Assemble the Lagrange multiplier
212  for (unsigned l = 0; l < dim_el; l++)
213  {
214  lambda[l] += nod_pt->value(first_index + l) * psi(j);
215  }
216  }
217 
218  this->continuous_tangent_and_outer_unit_normal(ipt, tang_vec, norm_vec);
219 
220  // Assemble residuals and jacobian
221 
222  // Loop over the nodes
223  for (unsigned j = 0; j < n_node; j++)
224  {
225  // Cast to a boundary node
226  BoundaryNodeBase* bnod_pt =
227  dynamic_cast<BoundaryNodeBase*>(node_pt(j));
228 
229  // Get the index of the first nodal value associated with
230  // this FaceElement
231  unsigned first_index =
233 
234  // loop over the lagrange multiplier components
235  for (unsigned l = 0; l < dim_el; l++)
236  {
237  // Local eqn number for the l-th component of lamdba
238  // in the j-th element
239  local_eqn = nodal_local_eqn(j, first_index + l);
240 
241  if (local_eqn >= 0)
242  {
243  for (unsigned i = 0; i < dim_el + 1; i++)
244  {
245  // Assemble residual for lagrange multiplier
246  residuals[local_eqn] +=
247  interpolated_u[i] * tang_vec[l][i] * psi(j) * W;
248 
249  // Assemble Jacobian for Lagrange multiplier:
250  if (flag == 1)
251  {
252  // Loop over the nodes again for unknowns
253  for (unsigned jj = 0; jj < n_node; jj++)
254  {
255  // Local eqn number for the i-th component
256  // of the velocity in the jj-th element
257  local_unknown = nodal_local_eqn(jj, u_index[i]);
258  if (local_unknown >= 0)
259  {
260  jacobian(local_eqn, local_unknown) +=
261  tang_vec[l][i] * psi(jj) * psi(j) * W;
262  }
263  }
264  }
265  }
266  }
267  }
268 
269  // Loop over the directions
270  for (unsigned i = 0; i < dim_el + 1; i++)
271  {
272  // Local eqn number for the i-th component of the
273  // velocity in the j-th element
274  local_eqn = nodal_local_eqn(j, u_index[i]);
275 
276  if (local_eqn >= 0)
277  {
278  // Add the contribution of the imposed pressure
279  if (Pressure_pt != 0)
280  {
281  residuals[local_eqn] -=
282  (*Pressure_pt) * norm_vec[i] * psi(j) * W;
283  }
284 
285  // Add lagrange multiplier contribution to the bulk equation
286  for (unsigned l = 0; l < dim_el; l++)
287  {
288  // Add to residual
289  residuals[local_eqn] += tang_vec[l][i] * psi(j) * lambda[l] * W;
290 
291  // Do Jacobian too?
292  if (flag == 1)
293  {
294  // Loop over the nodes again for unknowns
295  for (unsigned jj = 0; jj < n_node; jj++)
296  {
297  // Cast to a boundary node
298  BoundaryNodeBase* bnode_pt =
299  dynamic_cast<BoundaryNodeBase*>(node_pt(jj));
300 
301  // Local eqn number for the l-th component of lamdba
302  // in the jj-th element
303  local_unknown = nodal_local_eqn(
304  jj,
306  Id) +
307  l);
308  if (local_unknown >= 0)
309  {
310  jacobian(local_eqn, local_unknown) +=
311  tang_vec[l][i] * psi(jj) * psi(j) * W;
312  }
313  }
314  }
315  }
316  }
317  }
318  }
319  }
320  }
321 
322  /// The number of degrees of freedom types that this element is
323  /// divided into: The ImposeParallelOutflowElement is responsible for 1(2)
324  /// of it's own degrees of freedom. In addition to this, it also classifies
325  /// the 2(3) constrained velocity bulk degrees of freedom which this face
326  /// element is attached to.
327  unsigned ndof_types() const
328  {
329  // The the dof types in this element is 1 in 2D, 2 in 3D, so we use the
330  // elemental dimension function this->dim().
331  // In addition, we have the number of constrained velocity dof types, this
332  // is this->dim()+1 dof types.
333  // In total we have 2*(this->dim()) + 1 dof types.
334  return 2 * (this->dim()) + 1;
335  }
336 
337  /// Create a list of pairs for all unknowns in this element,
338  /// so that the first entry in each pair contains the global equation
339  /// number of the unknown, while the second one contains the LOCAL number
340  /// of the "DOF types" that this unknown is associated with.
341  /// (Function can obviously only be called if the equation numbering
342  /// scheme has been set up.)
343  ///
344  /// For the ImposeParallelOutflowElement, we need to classify both the
345  /// constrained velocity from the bulk element and the dof types from this
346  /// element. In 3D, let up, vp, wp be the constrained velocity dof types
347  /// associated with the ImposeParallelOutflowElement. Let Lp1 and Lp2 be the
348  /// two Lagrange multiplier dof types associated with the
349  /// ImposeParallelOutflowElement. Then we have:
350  ///
351  /// 0 1 2 3 4
352  /// up vp wp L1 L2
354  std::list<std::pair<unsigned long, unsigned>>& dof_lookup_list) const
355  {
356  // Temporary pair (used to store dof lookup prior to being added to list)
357  std::pair<unsigned, unsigned> dof_lookup;
358 
359  // Number of nodes in this element.
360  const unsigned n_node = this->nnode();
361 
362  // The elemental dimension.
363  const unsigned dim_el = this->dim();
364 
365  // The spatial dimension, since this is a face element, as assume that the
366  // spatial dimension is one higher than the elemental dimension.
367  const unsigned dim_bulk = dim_el + 1;
368 
369  // The classification of the constrained bulk velocity dof types is
370  // i, i = 0, ..., dim_bulk.
371  //
372  // The classification of the lagrange multiplier dof types is
373  // i + dim_bulk, i = 0, ..., dim_el
374  // as it is offset by the spatial dimension.
375 
376  // Loop over the nodes
377  for (unsigned node_i = 0; node_i < n_node; node_i++)
378  {
379  // First we classify the constrained velocity dof types from the bulk
380  // element. NOTE: This is a RE-CLASSIFICATION, since the dof types we
381  // are classifying should already be classified at least once by the
382  // bulk element. Furthermore, we assume that the velocity dof types
383  // comes first, then the pressure dof types. This is indeed true with
384  // OOMPH-LIB's implementation of Navier-Stokes elements.
385  for (unsigned velocity_i = 0; velocity_i < dim_bulk; velocity_i++)
386  {
387  // We only concern ourselves with the nodes of the "bulk" element
388  // that are associated with this "face" element. Bulk_node_number
389  // gives us the mapping from the face element's node to the bulk
390  // element's node. The local equation number is required to check if
391  // the value is pinned, if it is not pinned, the local equation number
392  // is required to get the global equation number.
393  int local_eqn = Bulk_element_pt->nodal_local_eqn(
394  Bulk_node_number[node_i], velocity_i);
395 
396  // Ignore pinned values.
397  if (local_eqn >= 0)
398  {
399  // Store the dof lookup in temporary pair: First entry in pair
400  // is the global equation number; second entry is the LOCAL dof
401  // type. It is assumed that the velocity dof types comes first. We
402  // do not re-classify the pressure dof types as they are not
403  // constrained.
404  dof_lookup.first = Bulk_element_pt->eqn_number(local_eqn);
405  dof_lookup.second = velocity_i;
406  dof_lookup_list.push_front(dof_lookup);
407  } // ignore pinned nodes "if(local-eqn>=0)"
408  } // for loop over the velocity components
409 
410  // Now we classify the dof types of this face element.
411  // Cast to a boundary node
412  BoundaryNodeBase* bnod_pt =
413  dynamic_cast<BoundaryNodeBase*>(node_pt(node_i));
414 
415  // Loop over directions in this Face(!)Element
416  for (unsigned dim_i = 0; dim_i < dim_el; dim_i++)
417  {
418  // Local eqn number:
419  int local_eqn = nodal_local_eqn(
420  node_i,
422 
423  if (local_eqn >= 0)
424  {
425  // Store dof lookup in temporary pair: First entry in pair
426  // is global equation number; second entry is dof type.
427  // We assume that the first 2(3) types are fluid dof types in
428  // 2(3) spatial dimensions (classified above):
429  //
430  // 0 1 2 3 4
431  // up vp wp L1 L2
432  //
433  // Thus the offset is the dim_bulk.
434  dof_lookup.first = this->eqn_number(local_eqn);
435  dof_lookup.second = dim_i + dim_bulk;
436 
437  // Add to list
438  dof_lookup_list.push_front(dof_lookup);
439  } // if local_eqn > 0
440  } // for: loop over the directions in the face element.
441  } // for: loop over the nodes in the face element.
442 
443  } // get_dof_numbers_for_unknowns
444 
445  }; // End of ImposeParallelOutflowElement class
446 
447 } // namespace oomph
448 
449 #endif
cstr elem_len * i
Definition: cfortran.h:603
A class that contains the information required by Nodes that are located on Mesh boundaries....
Definition: nodes.h:1996
unsigned index_of_first_value_assigned_by_face_element(const unsigned &face_id=0) const
Return the index of the first value associated with the i-th face element value. If no argument is sp...
Definition: nodes.h:2061
FaceElements are elements that coincide with the faces of higher-dimensional "bulk" elements....
Definition: elements.h:4338
Vector< unsigned > Bulk_node_number
List of indices of the local node numbers in the "bulk" element that correspond to the local node num...
Definition: elements.h:4403
FiniteElement * Bulk_element_pt
Pointer to the associated higher-dimensional "bulk" element.
Definition: elements.h:4399
int & face_index()
Index of the face (a number that uniquely identifies the face in the element)
Definition: elements.h:4626
double zeta_nodal(const unsigned &n, const unsigned &k, const unsigned &i) const
In a FaceElement, the "global" intrinsic coordinate of the element along the boundary,...
Definition: elements.h:4497
void add_additional_values(const Vector< unsigned > &nadditional_values, const unsigned &id)
Helper function adding additional values for the unknowns associated with the FaceElement....
Definition: elements.h:4428
FiniteElement *& bulk_element_pt()
Pointer to higher-dimensional "bulk" element.
Definition: elements.h:4735
double J_eulerian_at_knot(const unsigned &ipt) const
Return the Jacobian of the mapping from local to global coordinates at the ipt-th integration point O...
Definition: elements.cc:5328
void continuous_tangent_and_outer_unit_normal(const Vector< double > &s, Vector< Vector< double >> &tang_vec, Vector< double > &unit_normal) const
Compute the tangent vector(s) and the outer unit normal vector at the specified local coordinate....
Definition: elements.cc:5512
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
Definition: elements.h:4998
A general Finite Element class.
Definition: elements.h:1313
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2175
double nodal_value(const unsigned &n, const unsigned &i) const
Return the i-th value stored at local node n. Produces suitably interpolated values for hanging nodes...
Definition: elements.h:2593
virtual void output(std::ostream &outfile)
Output the element data — typically the values at the nodes in a format suitable for post-processing.
Definition: elements.h:3050
int nodal_local_eqn(const unsigned &n, const unsigned &i) const
Return the local equation number corresponding to the i-th value at the n-th local node.
Definition: elements.h:1432
unsigned dim() const
Return the spatial dimension of the element, i.e. the number of local coordinates required to paramet...
Definition: elements.h:2611
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2210
Integral *const & integral_pt() const
Return the pointer to the integration scheme (const version)
Definition: elements.h:1963
virtual void build_face_element(const int &face_index, FaceElement *face_element_pt)
Function for building a lower dimensional FaceElement on the specified face of the FiniteElement....
Definition: elements.cc:5132
virtual void shape_at_knot(const unsigned &ipt, Shape &psi) const
Return the geometric shape function at the ipt-th integration point.
Definition: elements.cc:3220
unsigned long eqn_number(const unsigned &ieqn_local) const
Return the global equation number corresponding to the ieqn_local-th local equation number.
Definition: elements.h:704
static DenseMatrix< double > Dummy_matrix
Empty dense matrix used as a dummy argument to combined residual and jacobian functions in the case w...
Definition: elements.h:227
ImposeParallelOutflowElement are elements that coincide with the faces of higher-dimensional "bulk" e...
void fill_in_generic_contribution_to_residuals_parall_lagr_multiplier(Vector< double > &residuals, DenseMatrix< double > &jacobian, const unsigned &flag)
Helper function to compute the residuals and, if flag==1, the Jacobian.
unsigned ndof_types() const
The number of degrees of freedom types that this element is divided into: The ImposeParallelOutflowEl...
void get_dof_numbers_for_unknowns(std::list< std::pair< unsigned long, unsigned >> &dof_lookup_list) const
Create a list of pairs for all unknowns in this element, so that the first entry in each pair contain...
void fill_in_contribution_to_residuals(Vector< double > &residuals)
Fill in the residuals.
double * Pressure_pt
pointer to imposed pressure – if null then no pressure imposed.
void output(std::ostream &outfile, const unsigned &nplot)
Output function: x,y,[z],u,v,[w],p in tecplot format.
void fill_in_contribution_to_jacobian(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Fill in contribution from Jacobian.
double *& pressure_pt()
Access function for the pressure.
void output(std::ostream &outfile)
Overload the output function.
ImposeParallelOutflowElement(FiniteElement *const &element_pt, const int &face_index, const unsigned &id=0)
Constructor takes a "bulk" element, the index that identifies which face the ImposeParallelOutflowEle...
double zeta_nodal(const unsigned &n, const unsigned &k, const unsigned &i) const
The "global" intrinsic coordinate of the element when viewed as part of a geometric object should be ...
virtual unsigned nweight() const =0
Return the number of integration points of the scheme.
virtual double weight(const unsigned &i) const =0
Return weight of i-th integration point.
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition: nodes.h:906
double value(const unsigned &i) const
Return i-th value (dofs or pinned) at this node either directly or via hanging node representation....
Definition: nodes.cc:2408
A Class for shape functions. In simple cases, the shape functions have only one index that can be tho...
Definition: shape.h:76
//////////////////////////////////////////////////////////////////// ////////////////////////////////...