young_laplace_contact_angle_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// 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//====================================================================
27 #include "young_laplace_elements.h"
29 
30 
31 namespace oomph
32 {
33  //===========================================================================
34  /// Constructor, takes the pointer to the "bulk" element and the
35  /// index of the face to which the element is attached.
36  //===========================================================================
37  template<class ELEMENT>
39  FiniteElement* const& bulk_el_pt, const int& face_index)
40  : FaceGeometry<ELEMENT>(), FaceElement()
41  {
42  // Let the bulk element build the FaceElement, i.e. setup the pointers
43  // to its nodes (by referring to the appropriate nodes in the bulk
44  // element), etc.
45  bulk_el_pt->build_face_element(face_index, this);
46 
47  // Initialise the prescribed contact angle pointer to zero
49 
50 #ifdef PARANOID
51  // Extract the dimension of the problem from the dimension of
52  // the first node
53  unsigned dim = node_pt(0)->ndim();
54  if (dim != 2)
55  {
56  throw OomphLibError(
57  "YoungLaplaceContactAngleElement only work with 2D nodes",
58  OOMPH_CURRENT_FUNCTION,
59  OOMPH_EXCEPTION_LOCATION);
60  }
61 #endif
62  }
63 
64 
65  //================================================================
66  /// Compute the element's contribution to the residual vector
67  //================================================================
68  template<class ELEMENT>
70  ELEMENT>::fill_in_contribution_to_residuals(Vector<double>& residuals)
71  {
72  // Find out how many nodes there are
73  unsigned n_node = nnode();
74 
75  // Set up memory for the shape functions
76  Shape psi(n_node);
77 
78  // Number of integration points
79  unsigned n_intpt = integral_pt()->nweight();
80 
81  // Set the Vector to hold local coordinates
82  // Note: We need the coordinate itself below for the evaluation
83  // of the contact line vectors even though we use the *at_knot
84  // version for the various shape-function-related functions
85  Vector<double> s(1);
86 
87  // Integers to hold the local equation and unknown numbers
88  int local_eqn = 0;
89 
90  // Loop over the integration points
91  for (unsigned ipt = 0; ipt < n_intpt; ipt++)
92  {
93  // Assign value of s
94  s[0] = integral_pt()->knot(ipt, 0);
95 
96  // Get the integral weight
97  double w = integral_pt()->weight(ipt);
98 
99  // Find the shape functions
100  shape_at_knot(ipt, psi);
101 
102  // Get the prescribed cos_gamma
103  double cos_gamma = prescribed_cos_gamma();
104 
105  // Get the various contact line vectors
106  Vector<double> tangent(3);
107  Vector<double> normal(3);
108  Vector<double> spine(3);
109  double norm_of_drds;
110  contact_line_vectors(s, tangent, normal, spine, norm_of_drds);
111 
112  // Get beta factor:
113 
114  // Cross product of spine and tangent to contact line is
115  // the wall normal
116  Vector<double> wall_normal(3);
117  ELEMENT::cross_product(spine, tangent, wall_normal);
118 
119  // Normalise
120  double norm = ELEMENT::two_norm(wall_normal);
121  for (unsigned i = 0; i < 3; i++) wall_normal[i] /= norm;
122 
123  // Take cross product with tangent to get the normal to the
124  // contact line parallel to wall
125  Vector<double> normal_to_contact_line_parallel_to_wall(3);
126  ELEMENT::cross_product(
127  tangent, wall_normal, normal_to_contact_line_parallel_to_wall);
128 
129  double beta =
130  ELEMENT::scalar_product(spine, normal_to_contact_line_parallel_to_wall);
131 
132 
133  // Now add to the appropriate equations
134 
135  // Loop over the test functions
136  for (unsigned l = 0; l < n_node; l++)
137  {
138  local_eqn = u_local_eqn(l);
139  /*IF it's not a boundary condition*/
140  if (local_eqn >= 0)
141  {
142  // Add to residual:
143  residuals[local_eqn] -= beta * cos_gamma * psi[l] * norm_of_drds * w;
144  }
145  }
146  }
147  }
148 
149 
150  //========================================================================
151  /// Get the actual contact angle
152  //========================================================================
153  template<class ELEMENT>
155  const Vector<double>& s)
156  {
157  // Get pointer to bulk element
158  ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(this->bulk_element_pt());
159 
160  double cos_gamma = 0.0;
161 
162  // Spine case
163  if (bulk_elem_pt->use_spines())
164  {
165  // Get various contact line vectors
166  Vector<double> tangent(3);
167  Vector<double> normal(3);
168  Vector<double> spine(3);
169  double norm_of_drds;
170  contact_line_vectors(s, tangent, normal, spine, norm_of_drds);
171 
172  // Get the wall normal: Both the tangent to the contact line
173  // and the spine vector are tangential to the wall:
174  Vector<double> wall_normal(3);
175  Vector<double> axe_ez(3, 0.0);
176  axe_ez[2] = 1.0;
177  ELEMENT::cross_product(axe_ez, tangent, wall_normal);
178 
179  // Normalise
180  double norm = 0.0;
181  for (unsigned i = 0; i < 3; i++) norm += wall_normal[i] * wall_normal[i];
182  for (unsigned i = 0; i < 3; i++)
183  {
184  wall_normal[i] /= sqrt(norm);
185  }
186 
187  // Get the auxiliary unit vector that's normal to
188  // the contact line and tangent to the wall
189  Vector<double> aux(3);
190  ELEMENT::cross_product(tangent, wall_normal, aux);
191 
192  // Cos of contact angle is dot product with wall normal
193  cos_gamma = ELEMENT::scalar_product(aux, normal);
194  }
195 
196  // Cartesian case
197  else
198  {
199  // Get local coordinates in bulk element by copy construction
200  Vector<double> s_bulk(local_coordinate_in_bulk(s));
201 
202  // Number of nodes in bulk element
203  unsigned nnode_bulk = bulk_elem_pt->nnode();
204 
205 
206 #ifdef PARANOID
207  // Dimension of (= number of local coordinates in) bulk element
208  unsigned dim_bulk = bulk_elem_pt->dim();
209 
210  if (dim_bulk != 2)
211  {
212  throw OomphLibError(
213  "YoungLaplaceContactAngleElements only work with 2D bulk elements",
214  OOMPH_CURRENT_FUNCTION,
215  OOMPH_EXCEPTION_LOCATION);
216  }
217 #endif
218 
219  // Set up memory for the shape functions
220  Shape psi(nnode_bulk);
221  DShape dpsidzeta(nnode_bulk, 2);
222 
223  // Call the derivatives of the shape and test functions
224  // in the bulk -- must do this via s because this point
225  // is not an integration point the bulk element!
226  (void)bulk_elem_pt->dshape_eulerian(s_bulk, psi, dpsidzeta);
227 
228  // Get the gradient at s
229  Vector<double> gradient_u(2, 0.0);
230 
231  // Calculate function value and derivatives:
232  //-----------------------------------------
233  // Loop over nodes
234  for (unsigned l = 0; l < nnode_bulk; l++)
235  {
236  // Loop over directions
237  for (unsigned j = 0; j < 2; j++)
238  {
239  gradient_u[j] += bulk_elem_pt->u(l) * dpsidzeta(l, j);
240  }
241  }
242 
243  // Get the outer unit normal to boundary
244  Vector<double> outer_normal(2, 0.0);
245  outer_unit_normal(s, outer_normal);
246 
247  // Compute the cosinus of the angle
248  double gradient_norm_2 =
249  ELEMENT::two_norm(gradient_u) * ELEMENT::two_norm(gradient_u);
250  cos_gamma = ELEMENT::scalar_product(gradient_u, outer_normal) /
251  sqrt(1 + gradient_norm_2);
252  }
253 
254  return cos_gamma;
255  }
256 
257 
258  //========================================================================
259  /// Get unit tangent and normal to contact line and the spine itself (this
260  /// allows the wall normal to be worked out by a cross product). Final
261  /// argument gives the norm of dR/ds where R is the vector to the
262  /// contact line and s the local coordinate in the element.
263  //========================================================================
264  template<class ELEMENT>
266  const Vector<double>& s,
267  Vector<double>& tangent,
268  Vector<double>& normal,
269  Vector<double>& spine,
270  double& norm_of_drds)
271  {
272  // Get pointer to bulk element
273  ELEMENT* bulk_elem_pt = dynamic_cast<ELEMENT*>(this->bulk_element_pt());
274 
275  // Dimension of (= number of local coordinates in) bulk element
276  unsigned dim_bulk = bulk_elem_pt->dim();
277 
278 #ifdef PARANOID
279  if (dim_bulk != 2)
280  {
281  throw OomphLibError(
282  "YoungLaplaceContactAngleElements only work with 2D bulk elements",
283  OOMPH_CURRENT_FUNCTION,
284  OOMPH_EXCEPTION_LOCATION);
285  }
286 #endif
287 
288  // Which local coordinate is const in bulk element?
289  unsigned s_fixed_index_in_bulk;
290 
291  DenseMatrix<double> ds_bulk_ds_face(2, 1);
292  this->get_ds_bulk_ds_face(s, ds_bulk_ds_face, s_fixed_index_in_bulk);
293 
294  // Get local coordinates in bulk element by copy construction
295  Vector<double> s_bulk(local_coordinate_in_bulk(s));
296 
297  // Number of nodes in bulk element
298  unsigned nnode_bulk = bulk_elem_pt->nnode();
299 
300  // Get the shape functions and their derivatives w.r.t.
301  // to the local coordinates in the bulk element
302  Shape psi_bulk(nnode_bulk);
303  DShape dpsi_bulk(nnode_bulk, dim_bulk);
304  bulk_elem_pt->dshape_local(s_bulk, psi_bulk, dpsi_bulk);
305 
306  // Displacement along spine
307  double interpolated_u = 0.0;
308 
309  // Derivative of u w.r.t. tangential and pseudo-normal bulk coordinate
310  double interpolated_du_ds_tangent = 0.0;
311  double interpolated_du_ds_pseudo_normal = 0.0;
312 
313  // Global intrinsic coordinates of current point for evaluation of
314  // spine and spine base
315  Vector<double> interpolated_zeta(dim_bulk, 0.0);
316 
317 
318  // Derivatives of zeta (in bulk) w.r.t. to tangent and pseudo-normal
319  // local coordinate
320  Vector<double> interpolated_dzeta_ds_tangent(dim_bulk);
321  Vector<double> interpolated_dzeta_ds_pseudo_normal(dim_bulk);
322 
323  // Loop over nodes
324  for (unsigned l = 0; l < nnode_bulk; l++)
325  {
326  interpolated_u += bulk_elem_pt->u(l) * psi_bulk(l);
327 
328  // Chain rule for tangent derivative
329  double aux = 0.0;
330  for (unsigned i = 0; i < dim_bulk; i++)
331  {
332  aux += dpsi_bulk(l, i) * ds_bulk_ds_face(i, 0);
333  }
334  interpolated_du_ds_tangent += bulk_elem_pt->u(l) * aux;
335 
336  // Straight derivative w.r.t. one of the bulk coordinates
337  // that's fixed along the face
338  interpolated_du_ds_pseudo_normal +=
339  bulk_elem_pt->u(l) * dpsi_bulk(l, s_fixed_index_in_bulk);
340 
341  // Loop over directions
342  for (unsigned j = 0; j < dim_bulk; j++)
343  {
344  interpolated_zeta[j] +=
345  bulk_elem_pt->nodal_position(l, j) * psi_bulk(l);
346 
347  // Chain rule for tangent derivative
348  double aux = 0.0;
349  for (unsigned i = 0; i < dim_bulk; i++)
350  {
351  aux += dpsi_bulk(l, i) * ds_bulk_ds_face(i, 0);
352  }
353  interpolated_dzeta_ds_tangent[j] +=
354  bulk_elem_pt->nodal_position(l, j) * aux;
355 
356  // Straight derivative w.r.t. one of the bulk coordinates
357  // that's fixed along the face
358  interpolated_dzeta_ds_pseudo_normal[j] +=
359  bulk_elem_pt->nodal_position(l, j) *
360  dpsi_bulk(l, s_fixed_index_in_bulk);
361  }
362  }
363 
364  // Auxiliary vector (tangent to non-fixed bulk coordinate but
365  // not necessarily normal to contact line)
366  Vector<double> aux_vector(3);
367  double tang_norm = 0.0;
368  double aux_norm = 0.0;
369 
370  if (bulk_elem_pt->use_spines())
371  {
372  // Get the spine and spine base vector at this point from the bulk element
373  Vector<double> spine_base(3, 0.0);
374  Vector<Vector<double>> dspine;
375  ELEMENT::allocate_vector_of_vectors(2, 3, dspine);
376  Vector<Vector<double>> dspine_base;
377  ELEMENT::allocate_vector_of_vectors(2, 3, dspine_base);
378  bulk_elem_pt->get_spine(interpolated_zeta, spine, dspine);
379  bulk_elem_pt->get_spine_base(interpolated_zeta, spine_base, dspine_base);
380 
381  // Derivative of spine and spine base w.r.t. local coordinate in
382  // FaceElement:
383  Vector<double> dspine_ds_tangent(3, 0.0);
384  Vector<double> dspine_base_ds_tangent(3, 0.0);
385  Vector<double> dspine_ds_pseudo_normal(3, 0.0);
386  Vector<double> dspine_base_ds_pseudo_normal(3, 0.0);
387  for (unsigned i = 0; i < 3; i++)
388  {
389  dspine_ds_tangent[i] +=
390  dspine[0][i] * interpolated_dzeta_ds_tangent[0] +
391  dspine[1][i] * interpolated_dzeta_ds_tangent[1];
392 
393  dspine_base_ds_tangent[i] +=
394  dspine_base[0][i] * interpolated_dzeta_ds_tangent[0] +
395  dspine_base[1][i] * interpolated_dzeta_ds_tangent[1];
396 
397  dspine_ds_pseudo_normal[i] +=
398  dspine[0][i] * interpolated_dzeta_ds_pseudo_normal[0] +
399  dspine[1][i] * interpolated_dzeta_ds_pseudo_normal[1];
400 
401  dspine_base_ds_pseudo_normal[i] +=
402  dspine_base[0][i] * interpolated_dzeta_ds_pseudo_normal[0] +
403  dspine_base[1][i] * interpolated_dzeta_ds_pseudo_normal[1];
404  }
405 
406  // Auxiliary vector (tangent to non-fixed bulk coordinate but
407  // not necessarily normal to contact line)
408  for (unsigned i = 0; i < 3; i++)
409  {
410  tangent[i] = dspine_base_ds_tangent[i] +
411  interpolated_du_ds_tangent * spine[i] +
412  interpolated_u * dspine_ds_tangent[i];
413  tang_norm += tangent[i] * tangent[i];
414 
415 
416  aux_vector[i] = dspine_base_ds_pseudo_normal[i] +
417  interpolated_du_ds_pseudo_normal * spine[i] +
418  interpolated_u * dspine_ds_pseudo_normal[i];
419  aux_norm += aux_vector[i] * aux_vector[i];
420  }
421  }
422 
423  // Cartesian case
424  else
425  {
426  for (unsigned i = 0; i < 2; i++)
427  {
428  tangent[i] = interpolated_dzeta_ds_tangent[i];
429  tang_norm += tangent[i] * tangent[i];
430  aux_vector[i] = interpolated_dzeta_ds_pseudo_normal[i];
431  aux_norm += aux_vector[i] * aux_vector[i];
432  }
433 
434  tangent[2] = interpolated_du_ds_tangent;
435  tang_norm += tangent[2] * tangent[2];
436  aux_vector[2] = interpolated_du_ds_pseudo_normal;
437  aux_norm += aux_vector[2] * aux_vector[2];
438  }
439 
440  // Norm of the rate of change
441  norm_of_drds = sqrt(tang_norm);
442 
443  // Normalise
444  double tang_norm_fact = 1.0 / sqrt(tang_norm);
445  double aux_norm_fact = 1.0 / sqrt(aux_norm);
446  for (unsigned i = 0; i < 3; i++)
447  {
448  tangent[i] *= tang_norm_fact;
449  aux_vector[i] *= aux_norm_fact;
450  }
451 
452 
453  // Normal to meniscus is the cross product between the
454  // two contact line vectors:
455  Vector<double> meniscus_normal(3);
456  ELEMENT::cross_product(tangent, aux_vector, meniscus_normal);
457 
458  // Calculate the norm
459  double men_norm_fact = 0.0;
460  for (unsigned i = 0; i < 3; i++)
461  {
462  men_norm_fact += meniscus_normal[i] * meniscus_normal[i];
463  }
464 
465  // Normalise and adjust direction
466  double sign = -double(normal_sign());
467  for (unsigned i = 0; i < 3; i++)
468  {
469  meniscus_normal[i] *= sign / sqrt(men_norm_fact);
470  }
471 
472  // The actual (bi) normal to the contact line is given
473  // by another cross product
474  ELEMENT::cross_product(meniscus_normal, tangent, normal);
475  }
476 
477 
478  //============================================================
479  // Build the required elements
480  //============================================================
484 
485  template class YoungLaplaceContactAngleElement<
487  template class YoungLaplaceContactAngleElement<
489  template class YoungLaplaceContactAngleElement<
491 
492 } // namespace oomph
static char t char * s
Definition: cfortran.h:568
cstr elem_len * i
Definition: cfortran.h:603
A Class for the derivatives of shape functions The class design is essentially the same as Shape,...
Definition: shape.h:278
FaceElements are elements that coincide with the faces of higher-dimensional "bulk" elements....
Definition: elements.h:4338
int & face_index()
Index of the face (a number that uniquely identifies the face in the element)
Definition: elements.h:4626
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
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
unsigned dim() const
Return the spatial dimension of the element, i.e. the number of local coordinates required to paramet...
Definition: elements.h:2611
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
unsigned ndim() const
Return (Eulerian) spatial dimension of the node.
Definition: nodes.h:1054
An OomphLibError object which should be thrown when an run-time error is encountered....
Refineable version of 2D QYoungLaplaceElement elements.
A Class for shape functions. In simple cases, the shape functions have only one index that can be tho...
Definition: shape.h:76
A class for elements that allow the imposition of an contact angle bcs for Young Laplace elements....
double actual_cos_contact_angle(const Vector< double > &s)
Compute cosinus of actual contact angle.
void contact_line_vectors(const Vector< double > &s, Vector< double > &tangent, Vector< double > &normal)
Get unit tangent and normal vectors to contact line.
double * Prescribed_cos_gamma_pt
Pointer to prescribed cos gamma.
//////////////////////////////////////////////////////////////////// ////////////////////////////////...