fsi_driven_cavity_mesh.template.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-2025 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_FSI_DRIVEN_CAVITY_MESH_TEMPLATE_HEADER
27#define OOMPH_FSI_DRIVEN_CAVITY_MESH_TEMPLATE_HEADER
28
29#ifndef OOMPH_FSI_DRIVEN_CAVITY_MESH_HEADER
30#error __FILE__ should only be included from fsi_driven_cavity_mesh.h.
31#endif // OOMPH_FSI_DRIVEN_CAVITY_MESH_HEADER
32
33// Include the headers file for collapsible channel
34
35namespace oomph
36{
37 //========================================================================
38 /// Constructor: Pass number of elements, lengths, pointer to GeomObject
39 /// that defines the collapsible segment and pointer to TimeStepper
40 /// (defaults to the default timestepper, Steady).
41 //========================================================================
42 template<class ELEMENT>
44 const unsigned& nx,
45 const unsigned& ny,
46 const double& lx,
47 const double& ly,
48 const double& gap_fraction,
49 GeomObject* wall_pt,
52 Nx(nx),
53 Ny(ny),
54 Gap_fraction(gap_fraction),
55 Wall_pt(wall_pt)
56 {
57 // Mesh can only be built with 2D Qelements.
58 MeshChecker::assert_geometric_element<QElementGeometricBase, ELEMENT>(2);
59
60 // Update the boundary numbering scheme and set boundary coordinate
61 //-----------------------------------------------------------------
62
63 // (Note: The original SimpleRectangularQuadMesh had four boundaries.
64 // We need to overwrite the boundary lookup scheme for the current
65 // mesh so that the collapsible segment becomes identifiable).
66 // While we're doing this, we're also setting up a boundary
67 // coordinate for the nodes located on the collapsible segment.
68 // The boundary coordinate can be used to setup FSI.
69
70 // How many boundaries does the mesh have now?
71 unsigned nbound = this->nboundary();
72 for (unsigned b = 0; b < nbound; b++)
73 {
74 // Remove all nodes on this boundary from the mesh's lookup scheme
75 // and also delete the reverse lookup scheme held by the nodes
77 }
78
79#ifdef PARANOID
80 // Sanity check
81 unsigned nnod = this->nnode();
82 for (unsigned j = 0; j < nnod; j++)
83 {
84 if (this->node_pt(j)->is_on_boundary())
85 {
86 std::ostringstream error_message;
87 error_message << "Node " << j << "is still on boundary " << std::endl;
88
89 throw OomphLibError(error_message.str(),
92 }
93 }
94#endif
95
96 // Change the numbers of boundaries
97 this->set_nboundary(6);
98
99 // Get the number of nodes along the element edge from first element
100 unsigned nnode_1d = this->finite_element_pt(0)->nnode_1d();
101
102 // Vector of Lagrangian coordinates used as boundary coordinate
104
105 // Zeta increment over elements (used for assignment of
106 // boundary coordinate)
107 double dzeta = lx / double(nx);
108
109 // Manually loop over the elements near the boundaries and
110 // assign nodes to boundaries. Also set up boundary coordinate
111 unsigned nelem = this->nelement();
112 for (unsigned e = 0; e < nelem; e++)
113 {
114 // Bottom row of elements
115 if (e < nx)
116 {
117 for (unsigned i = 0; i < nnode_1d; i++)
118 {
119 this->add_boundary_node(0, this->finite_element_pt(e)->node_pt(i));
120 }
121 }
122 // Collapsible bit
123 if (e > ((ny - 1) * nx) - 1)
124 {
125 for (unsigned i = 0; i < nnode_1d; i++)
126 {
127 this->add_boundary_node(
128 3, this->finite_element_pt(e)->node_pt(2 * nnode_1d + i));
129
130 // What column of elements are we in?
131 unsigned ix = e - (ny - 1) * nx;
132
133 // Zeta coordinate
134 zeta[0] =
135 double(ix) * dzeta + double(i) * dzeta / double(nnode_1d - 1);
136
137 // Set boundary coordinate
138 this->finite_element_pt(e)
139 ->node_pt(2 * nnode_1d + i)
140 ->set_coordinates_on_boundary(3, zeta);
141 }
142 }
143 // Left end
144 if (e % (nx) == 0)
145 {
146 for (unsigned i = 0; i < nnode_1d; i++)
147 {
148 Node* nod_pt = this->finite_element_pt(e)->node_pt(i * nnode_1d);
149
150 // Rigid bit?
151 if (nod_pt->x(1) >= ly * Gap_fraction)
152 {
153 this->add_boundary_node(4, nod_pt);
154 }
155 // Free bit
156 else
157 {
158 this->add_boundary_node(5, nod_pt);
159 }
160 }
161 }
162 // Right end
163 if (e % nx == nx - 1)
164 {
165 for (unsigned i = 0; i < nnode_1d; i++)
166 {
167 Node* nod_pt =
168 this->finite_element_pt(e)->node_pt((i + 1) * nnode_1d - 1);
169
170 // Rigid bit?
171 if (nod_pt->x(1) >= ly * Gap_fraction)
172 {
173 this->add_boundary_node(2, nod_pt);
174 }
175 // Free bit
176 else
177 {
178 this->add_boundary_node(1, nod_pt);
179 }
180 }
181 }
182 }
183
184 // Re-setup lookup scheme that establishes which elements are located
185 // on the mesh boundaries (doesn't need to be wiped)
186 this->setup_boundary_element_info();
187
188 // We have only bothered to parametrise boundary 3
190 }
191
192
193 ///////////////////////////////////////////////////////////////////////////
194 ///////////////////////////////////////////////////////////////////////////
195 ///////////////////////////////////////////////////////////////////////////
196
197 //=================================================================
198 /// Perform algebraic mesh update at time level t (t=0: present;
199 /// t>0: previous)
200 //=================================================================
201 template<class ELEMENT>
203 const unsigned& t, AlgebraicNode*& node_pt)
204 {
205#ifdef PARANOID
206 // We're updating the nodal positions (!) at time level t
207 // and determine them by evaluating the wall GeomObject's
208 // position at that gime level. I believe this only makes sense
209 // if the t-th history value in the positional timestepper
210 // actually represents previous values (rather than some
211 // generalised quantity). Hence if this function is called with
212 // t>nprev_values(), we issue a warning and terminate the execution.
213 // It *might* be possible that the code still works correctly
214 // even if this condition is violated (e.g. if the GeomObject's
215 // position() function returns the appropriate "generalised"
216 // position value that is required by the timestepping scheme but it's
217 // probably worth flagging this up and forcing the user to manually switch
218 // off this warning if he/she is 100% sure that this is kosher.
219 if (t > node_pt->position_time_stepper_pt()->nprev_values())
220 {
221 std::string error_message =
222 "Trying to update the nodal position at a time level";
223 error_message += "beyond the number of previous values in the nodes'";
224 error_message += "position timestepper. This seems highly suspect!";
225 error_message += "If you're sure the code behaves correctly";
226 error_message += "in your application, remove this warning ";
227 error_message += "or recompile with PARNOID switched off.";
228
229 std::string function_name = "AlgebraicFSIDrivenCavityMesh::";
230 function_name += "algebraic_node_update()";
231
232 throw OomphLibError(
234 }
235#endif
236
237 // Extract references for update by copy construction
238 Vector<double> ref_value(node_pt->vector_ref_value());
239
240 // First reference value: Original x-position. Used as the start point
241 // for the lines connecting the nodes in the vertical direction
242 double x_bottom = ref_value[0];
243
244 // Second reference value: Fractional position along
245 // straight line from the bottom (at the original x position)
246 // to the reference point on the upper wall
247 double fract = ref_value[1];
248
249 // Third reference value: Reference local coordinate
250 // in GeomObject that represents the upper wall (local coordinate
251 // in finite element if the wall GeomObject is a finite element mesh)
252 Vector<double> s(1);
253 s[0] = ref_value[2];
254
255 // Fourth reference value: zeta coordinate on the upper wall
256 // If the wall is a simple GeomObject, zeta[0]=s[0]
257 // but if it's a compound GeomObject (e.g. a finite element mesh)
258 // zeta scales during mesh refinement, whereas s[0] and the
259 // pointer to the geom object have to be re-computed.
260 // double zeta=ref_value[3]; // not needed here
261
262 // Extract geometric objects for update by copy construction
263 Vector<GeomObject*> geom_object_pt(node_pt->vector_geom_object_pt());
264
265 // Pointer to actual wall geom object (either the same as the wall object
266 // or the pointer to the actual finite element)
268
269 // Get position vector to wall at previous timestep t!
271 geom_obj_pt->position(t, s, r_wall);
272
273 // Assign new nodal coordinate
274 node_pt->x(t, 0) = x_bottom + fract * (r_wall[0] - x_bottom);
275 node_pt->x(t, 1) = fract * r_wall[1];
276 }
277
278 //=====start_setup=================================================
279 /// Setup algebraic mesh update -- assumes that mesh has
280 /// initially been set up with a flush upper wall.
281 //=================================================================
282 template<class ELEMENT>
284 {
285 // Loop over all nodes in mesh
286 unsigned nnod = this->nnode();
287 for (unsigned j = 0; j < nnod; j++)
288 {
289 // Get pointer to node -- recall that that Mesh::node_pt(...) has been
290 // overloaded in the AlgebraicMesh class to return a pointer to
291 // an AlgebraicNode.
293
294 // Get coordinates
295 double x = nod_pt->x(0);
296 double y = nod_pt->x(1);
297
298 // Get zeta coordinate on the undeformed wall
300 zeta[0] = x;
301
302 // Get pointer to geometric (sub-)object and Lagrangian coordinate
303 // on that sub-object. For a wall that is represented by
304 // a single geom object, this simply returns the input.
305 // If the geom object consists of sub-objects (e.g.
306 // if it is a finite element mesh representing a wall,
307 // then we'll obtain the pointer to the finite element
308 // (in its incarnation as a GeomObject) and the
309 // local coordinate in that element.
311 Vector<double> s(1);
312 this->Wall_pt->locate_zeta(zeta, geom_obj_pt, s);
313
314 // Get position vector to wall:
316 geom_obj_pt->position(s, r_wall);
317
318 // Sanity check: Confirm that the wall is in its undeformed position
319#ifdef PARANOID
320 if ((std::fabs(r_wall[0] - x) > 1.0e-15) &&
321 (std::fabs(r_wall[1] - y) > 1.0e-15))
322 {
323 std::ostringstream error_stream;
324 error_stream << "Wall must be in its undeformed position when\n"
325 << "algebraic node update information is set up!\n "
326 << "x-discrepancy: " << std::fabs(r_wall[0] - x)
327 << std::endl
328 << "y-discrepancy: " << std::fabs(r_wall[1] - y)
329 << std::endl;
330
331 throw OomphLibError(
333 }
334#endif
335
336 // One geometric object is involved in update operation
338
339 // The actual geometric object (If the wall is simple GeomObject
340 // this is the same as Wall_pt; if it's a compound GeomObject
341 // this points to the sub-object)
343
344 // The update function requires four parameters:
346
347 // First reference value: Original x-position
348 ref_value[0] = r_wall[0];
349
350 // Second reference value: fractional position along
351 // straight line from the bottom (at the original x position)
352 // to the point on the wall)
353 ref_value[1] = y / r_wall[1];
354
355 // Third reference value: Reference local coordinate
356 // in wall element (local coordinate in FE if we're dealing
357 // with a wall mesh)
358 ref_value[2] = s[0];
359
360 // Fourth reference value: zeta coordinate on wall
361 // If the wall is a simple GeomObject, zeta[0]=s[0]
362 // but if it's a compound GeomObject (e.g. a finite element mesh)
363 // zeta scales during mesh refinement, whereas s[0] and the
364 // pointer to the geom object have to be re-computed.
365 ref_value[3] = zeta[0];
366
367 // Setup algebraic update for node: Pass update information
368 nod_pt->add_node_update_info(this, // mesh
369 geom_object_pt, // vector of geom objects
370 ref_value); // vector of ref. values
371 }
372
373 } // end of setup_algebraic_node_update
374
375
376 ////////////////////////////////////////////////////////////////////
377 ////////////////////////////////////////////////////////////////////
378 ////////////////////////////////////////////////////////////////////
379
380 //========start_update_node_update=================================
381 /// Update the geometric references that are used
382 /// to update node after mesh adaptation.
383 //=================================================================
384 template<class ELEMENT>
387 {
388 // Extract reference values for node update by copy construction
389 Vector<double> ref_value(node_pt->vector_ref_value());
390
391 // First reference value: Original x-position
392 // double x_bottom=ref_value[0]; // not needed here
393
394 // Second reference value: fractional position along
395 // straight line from the bottom (at the original x position)
396 // to the point on the wall)
397 // double fract=ref_value[1]; // not needed here
398
399 // Third reference value: Reference local coordinate
400 // in GeomObject (local coordinate in finite element if the wall
401 // GeomObject is a finite element mesh)
402 // Vector<double> s(1);
403 // s[0]=ref_value[2]; // This needs to be re-computed!
404
405 // Fourth reference value: intrinsic coordinate on the (possibly
406 // compound) wall.
407 double zeta = ref_value[3];
408
409 // Extract geometric objects for update by copy construction
410 Vector<GeomObject*> geom_object_pt(node_pt->vector_geom_object_pt());
411
412 // Pointer to actual wall geom object (either the same as wall object
413 // or the pointer to the actual finite element)
414 // GeomObject* geom_obj_pt=geom_object_pt[0]; // This needs to be
415 // re-computed!
416
417 // Get zeta coordinate on wall (as vector)
419 zeta_wall[0] = zeta;
420
421 // Get pointer to geometric (sub-)object and Lagrangian coordinate
422 // on that sub-object. For a wall that is represented by
423 // a single geom object, this simply returns the input.
424 // If the geom object consists of sub-objects (e.g.
425 // if it is a finite element mesh representing a wall,
426 // then we'll obtain the pointer to the finite element
427 // (in its incarnation as a GeomObject) and the
428 // local coordinate in that element.
429 Vector<double> s(1);
431 this->Wall_pt->locate_zeta(zeta_wall, geom_obj_pt, s);
432
433 // Update the pointer to the (sub-)GeomObject within which the
434 // reference point is located. (If the wall is simple GeomObject
435 // this is the same as Wall_pt; if it's a compound GeomObject
436 // this points to the sub-object)
438
439 // First reference value: Original x-position
440 // ref_value[0]=r_wall[0]; // unchanged
441
442 // Second reference value: fractional position along
443 // straight line from the bottom (at the original x position)
444 // to the point on the wall)
445 // ref_value[1]=y/r_wall[1]; // unchanged
446
447 // Update third reference value: Reference local coordinate
448 // in wall element (local coordinate in FE if we're dealing
449 // with a wall mesh)
450 ref_value[2] = s[0];
451
452 // Fourth reference value: zeta coordinate on wall
453 // If the wall is a simple GeomObject, zeta[0]=s[0]
454 // but if it's a compound GeomObject (e.g. a finite element mesh)
455 // zeta scales during mesh refinement, whereas s[0] and the
456 // pointer to the geom object have to be re-computed.
457 // ref_value[3]=zeta[0]; //unchanged
458
459 // Kill the existing node update info
460 node_pt->kill_node_update_info();
461
462 // Setup algebraic update for node: Pass update information
463 node_pt->add_node_update_info(this, // mesh
464 geom_object_pt, // vector of geom objects
465 ref_value); // vector of ref. values
466 }
467
468} // namespace oomph
469#endif
void algebraic_node_update(const unsigned &t, AlgebraicNode *&node_pt)
Update nodal position at time level t (t=0: present; t>0: previous)
void setup_algebraic_node_update()
Function to setup the algebraic node update.
double Gap_fraction
Fraction of the gap next to moving lid, relative to the height of the domain.
FSIDrivenCavityMesh(const unsigned &nx, const unsigned &ny, const double &lx, const double &ly, const double &gap_fraction, GeomObject *wall_pt, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass number of elements, number of elements, fractional height of the gap above the movi...
Quadrilateral mesh generator; Uses input from Geompack++. See: http://members.shaw....
void update_node_update(AlgebraicNode *&node_pt)
Update the node update data for specified node following any mesh adapation.
Simple rectangular 2D Quad mesh class. Nx : number of elements in the x direction.
const unsigned & ny() const
Access function for number of elements in y directions.
const unsigned & nx() const
Access function for number of elements in x directions.