impose_impenetrability_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-2024 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_IMPENETRABLE_ELEMENTS_HEADER
27 #define OOMPH_IMPOSE_IMPENETRABLE_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  /// ImposeImpenetrabilityElement
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 impenetrability.
41  //========================================================================
42  template<class ELEMENT>
43  class ImposeImpenetrabilityElement : public virtual FaceGeometry<ELEMENT>,
44  public virtual FaceElement
45  {
46  private:
47  /// Lagrange Id
48  unsigned Id;
49 
50  public:
51  /// Constructor takes a "bulk" element, the
52  /// index that identifies which face the
53  /// ImposeImpenetrabilityElement is supposed
54  /// to be attached to, and the face element ID
56  const int& face_index,
57  const unsigned& id = 0)
58  : FaceGeometry<ELEMENT>(), FaceElement()
59  {
60  // set the Id
61  Id = id;
62 
63  // Build the face element
64  element_pt->build_face_element(face_index, this);
65 
66  // we need 1 additional values for each FaceElement node
67  Vector<unsigned> n_additional_values(nnode(), 1);
68 
69  // add storage for lagrange multipliers and set the map containing
70  // the position of the first entry of this face element's
71  // additional values.
72  add_additional_values(n_additional_values, id);
73  }
74 
75 
76  /// Fill in the residuals
78  {
79  // Call the generic routine with the flag set to 0
81  residuals, GeneralisedElement::Dummy_matrix, 0);
82  }
83 
84  /// Fill in contribution from Jacobian
86  DenseMatrix<double>& jacobian)
87  {
88  // Call the generic routine with the flag set to 1
90  residuals, jacobian, 1);
91  }
92 
93  /// Overload the output function
94  void output(std::ostream& outfile)
95  {
96  FiniteElement::output(outfile);
97  }
98 
99  /// Output function: x,y,[z],u,v,[w],p in tecplot format
100  void output(std::ostream& outfile, const unsigned& nplot)
101  {
102  FiniteElement::output(outfile, nplot);
103  }
104 
105  /// The "global" intrinsic coordinate of the element when
106  /// viewed as part of a geometric object should be given by
107  /// the FaceElement representation, by default
108  /// This final over-ride is required because both SolidFiniteElements
109  /// and FaceElements overload zeta_nodal
110  double zeta_nodal(const unsigned& n,
111  const unsigned& k,
112  const unsigned& i) const
113  {
114  return FaceElement::zeta_nodal(n, k, i);
115  }
116 
117  protected:
118  /// Helper function to compute the residuals and, if flag==1, the
119  /// Jacobian
121  Vector<double>& residuals,
122  DenseMatrix<double>& jacobian,
123  const unsigned& flag)
124  {
125  // Find out how many nodes there are
126  unsigned n_node = nnode();
127 
128  // Dimension of element
129  unsigned dim_el = dim();
130 
131  // Set up memory for the shape functions
132  Shape psi(n_node);
133 
134  // Set the value of n_intpt
135  unsigned n_intpt = integral_pt()->nweight();
136 
137  // to store local equation number
138  int local_eqn = 0;
139  int local_unknown = 0;
140 
141  // to store normal vector
142  Vector<double> norm_vec(dim_el + 1);
143 
144  // get the value at which the velocities are stored
145  Vector<unsigned> u_index(dim_el + 1);
146  ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->bulk_element_pt());
147  for (unsigned i = 0; i < dim_el + 1; i++)
148  {
149  u_index[i] = el_pt->u_index_nst(i);
150  }
151 
152  // Loop over the integration points
153  for (unsigned ipt = 0; ipt < n_intpt; ipt++)
154  {
155  // Get the integral weight
156  double w = integral_pt()->weight(ipt);
157 
158  // Jacobian of mapping
159  double J = J_eulerian_at_knot(ipt);
160 
161  // Premultiply the weights and the Jacobian
162  double W = w * J;
163 
164  // Calculate the shape functions
165  shape_at_knot(ipt, psi);
166 
167  // compute the velocity and the Lagrange multiplier
168  Vector<double> interpolated_u(dim_el + 1, 0.0);
169  double lambda = 0.0;
170 
171  // Loop over nodes
172  for (unsigned j = 0; j < n_node; j++)
173  {
174  // Assemble the velocity component
175  for (unsigned i = 0; i < dim_el + 1; i++)
176  {
177  interpolated_u[i] += nodal_value(j, u_index[i]) * psi(j);
178  }
179 
180  // Cast to a boundary node
181  BoundaryNodeBase* bnod_pt =
182  dynamic_cast<BoundaryNodeBase*>(node_pt(j));
183 
184  // get the node
185  Node* nod_pt = node_pt(j);
186 
187  // Get the index of the first nodal value associated with
188  // this FaceElement
189  unsigned first_index =
191 
192  // Assemble the Lagrange multiplier
193  lambda += nod_pt->value(first_index) * psi(j);
194  }
195 
196  // compute the normal vector
197  outer_unit_normal(ipt, norm_vec);
198 
199  // Assemble residuals and jacobian
200 
201  // Loop over the nodes
202  for (unsigned j = 0; j < n_node; j++)
203  {
204  // Cast to a boundary node
205  BoundaryNodeBase* bnod_pt =
206  dynamic_cast<BoundaryNodeBase*>(node_pt(j));
207 
208  // Get the index of the first nodal value associated with
209  // this FaceElement
210  unsigned first_index =
212 
213  // Local eqn number for the l-th component of lamdba
214  // in the j-th element
215  local_eqn = nodal_local_eqn(j, first_index);
216 
217  if (local_eqn >= 0)
218  {
219  for (unsigned i = 0; i < dim_el + 1; i++)
220  {
221  // Assemble residual for lagrange multiplier
222  residuals[local_eqn] +=
223  interpolated_u[i] * norm_vec[i] * psi(j) * W;
224 
225  // Assemble Jacobian for Lagrange multiplier:
226  if (flag == 1)
227  {
228  // Loop over the nodes again for unknowns
229  for (unsigned jj = 0; jj < n_node; jj++)
230  {
231  // Local eqn number for the i-th component
232  // of the velocity in the jj-th element
233  local_unknown = nodal_local_eqn(jj, u_index[i]);
234  if (local_unknown >= 0)
235  {
236  jacobian(local_eqn, local_unknown) +=
237  norm_vec[i] * psi(jj) * psi(j) * W;
238  }
239  }
240  }
241  }
242  }
243 
244  // Loop over the directions
245  for (unsigned i = 0; i < dim_el + 1; i++)
246  {
247  // Local eqn number for the i-th component of the
248  // velocity in the j-th element
249  local_eqn = nodal_local_eqn(j, u_index[i]);
250 
251  if (local_eqn >= 0)
252  {
253  // Add lagrange multiplier contribution to the bulk equation
254  // Add to residual
255  residuals[local_eqn] += norm_vec[i] * psi(j) * lambda * W;
256 
257  // Do Jacobian too?
258  if (flag == 1)
259  {
260  // Loop over the nodes again for unknowns
261  for (unsigned jj = 0; jj < n_node; jj++)
262  {
263  // Cast to a boundary node
264  BoundaryNodeBase* bnode_pt =
265  dynamic_cast<BoundaryNodeBase*>(node_pt(jj));
266 
267  // Local eqn number for the l-th component of lamdba
268  // in the jj-th element
269  local_unknown = nodal_local_eqn(
270  jj,
272  Id));
273  if (local_unknown >= 0)
274  {
275  jacobian(local_eqn, local_unknown) +=
276  norm_vec[i] * psi(jj) * psi(j) * W;
277  }
278  }
279  }
280  }
281  }
282  }
283  }
284  }
285 
286 
287  /// The number of "DOF types" that degrees of freedom in this element
288  /// are sub-divided into: Just the solid degrees of freedom themselves.
289  unsigned ndof_types() const
290  {
291  // There is only ever one normal. Plus the constrained velocities.
292  // unsigned ndofndof = 1 + additional_ndof_types();
293  // std::cout << "ndof: " << ndofndof << std::endl;
294 
295  return (1 + additional_ndof_types());
296  }
297 
298 
299  unsigned additional_ndof_types() const
300  {
301  // Additional dof types for the constained bulk velocities
302  // two velocities for a 2D problem, 3 for 3D.
303  return (this->dim() + 1);
304  }
305 
306  /// Create a list of pairs for all unknowns in this element,
307  /// so that the first entry in each pair contains the global equation
308  /// number of the unknown, while the second one contains the number
309  /// of the "DOF type" that this unknown is associated with.
310  /// (Function can obviously only be called if the equation numbering
311  /// scheme has been set up.)
313  std::list<std::pair<unsigned long, unsigned>>& dof_lookup_list) const
314  {
315  // temporary pair (used to store dof lookup prior to
316  // being added to list)
317  std::pair<unsigned, unsigned> dof_lookup;
318 
319  // number of nodes
320  const unsigned n_node = this->nnode();
321 
322  // Loop over directions in this Face(!)Element
323  unsigned dim_el = this->dim();
324  // for(unsigned i=0;i<dim_el;i++)
325  {
326  unsigned i = 0;
327  // Loop over the nodes
328  for (unsigned j = 0; j < n_node; j++)
329  {
330  // Cast to a boundary node
331  BoundaryNodeBase* bnod_pt =
332  dynamic_cast<BoundaryNodeBase*>(node_pt(j));
333 
334  // Local eqn number:
335  int local_eqn = nodal_local_eqn(
337  if (local_eqn >= 0)
338  {
339  // store dof lookup in temporary pair: First entry in pair
340  // is global equation number; second entry is dof type
341  dof_lookup.first = this->eqn_number(local_eqn);
342  dof_lookup.second = i + additional_ndof_types();
343 
344  // add to list
345  dof_lookup_list.push_front(dof_lookup);
346  }
347  }
348  }
349 
350  //*
351  // Now we do the bulk elements. Each velocity component of a constrained
352  // dof of a different type of FaceElement has a different dof_type. E.g.
353  // Consider the Navier Stokes equations in three spatial dimensions with
354  // parallel outflow (using ImposeParallelOutflowElement with Boundary_id =
355  // 1) and tangential flow (using ImposeTangentialFlowElement with
356  // Boundary_id = 2) imposed along two different boundaries. There will be
357  // 10 dof types: 0 1 2 3 4 5 6 7 8 9 u v w p u1 v1 w1 u2 v2 w2
358 
359  // Loop over only the nodes of the "bulk" element that are associated
360  // with this "face" element.
361  // cout << "n_node: " << n_node << endl;
362  unsigned const bulk_dim = dim_el + 1;
363  // cout << "bulk_dim: " << bulk_dim << endl;
364  for (unsigned node_i = 0; node_i < n_node; node_i++)
365  {
366  // Loop over the velocity components
367  for (unsigned velocity_i = 0; velocity_i < bulk_dim; velocity_i++)
368  {
369  // Calculating the offset for this Boundary_id.
370  // 0 1 2 3 4 5 6 7 8 9
371  // u v w p u1 v1 w1 u2 v2 w2
372  //
373  // for the first surface mesh, offset = 4
374  // for the second surface mesh, offset = 7
375  // unsigned offset = bulk_dim * Boundary_id + 1;
376 
377  // The local equation number is required to check if the value is
378  // pinned, if it is not pinned, the local equation number is required
379  // to get the global equation number.
380  int local_eqn = Bulk_element_pt->nodal_local_eqn(
381  Bulk_node_number[node_i], velocity_i);
382 
383  // ignore pinned values
384  if (local_eqn >= 0)
385  {
386  // store the dof lookup in temporary pair: First entry in pair
387  // is the global equation number; second entry is the dof type
388  dof_lookup.first = Bulk_element_pt->eqn_number(local_eqn);
389  dof_lookup.second = velocity_i;
390  dof_lookup_list.push_front(dof_lookup);
391 
392  // RRRcout << "Face v: " << dof_lookup.first
393  // RRR << ", doftype: " << dof_lookup.second << endl;
394 
395  } // ignore pinned nodes "if(local-eqn>=0)"
396  } // for loop over the velocity components
397  } // for loop over bulk nodes only
398 
399  } // get unknowns...
400  };
401 } // namespace oomph
402 #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:4342
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:4407
FiniteElement * Bulk_element_pt
Pointer to the associated higher-dimensional "bulk" element.
Definition: elements.h:4403
int & face_index()
Index of the face (a number that uniquely identifies the face in the element)
Definition: elements.h:4630
void outer_unit_normal(const Vector< double > &s, Vector< double > &unit_normal) const
Compute outer unit normal at the specified local coordinate.
Definition: elements.cc:6036
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:4501
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:4432
FiniteElement *& bulk_element_pt()
Pointer to higher-dimensional "bulk" element.
Definition: elements.h:4739
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:5358
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
Definition: elements.h:5002
A general Finite Element class.
Definition: elements.h:1317
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2179
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:2597
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:3054
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:1436
unsigned dim() const
Return the spatial dimension of the element, i.e. the number of local coordinates required to paramet...
Definition: elements.h:2615
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2214
Integral *const & integral_pt() const
Return the pointer to the integration scheme (const version)
Definition: elements.h:1967
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:5162
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:3250
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:708
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
ImposeImpenetrabilityElement are elements that coincide with the faces of higher-dimensional "bulk" e...
unsigned ndof_types() const
The number of "DOF types" that degrees of freedom in this element are sub-divided into: Just the soli...
void fill_in_contribution_to_residuals(Vector< double > &residuals)
Fill in the residuals.
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 output(std::ostream &outfile)
Overload the output function.
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.
void fill_in_contribution_to_jacobian(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Fill in contribution from Jacobian.
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 ...
void output(std::ostream &outfile, const unsigned &nplot)
Output function: x,y,[z],u,v,[w],p in tecplot format.
ImposeImpenetrabilityElement(FiniteElement *const &element_pt, const int &face_index, const unsigned &id=0)
Constructor takes a "bulk" element, the index that identifies which face the ImposeImpenetrabilityEle...
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
//////////////////////////////////////////////////////////////////// ////////////////////////////////...