unstructured_adaptive_2d_fsi.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// Driver code for a simple unstructured FSI problem using a mesh
27// generated from within the driver.
28// The problem is the flow past a (thin) solid leaflet.
29// The fluid mesh is of the form:
30//
31// 5
32// (x_i,H) -----------------------------------------------------(x_i+L,H)
33// | 1 |
34// | (x_l-w/2,h)----(x_l+w/2,h) |
35// 6 | | | |4
36// | 0 | | 2 |
37// | | | |
38// (x_i,0)-------------------| |------------------------------(x_i+L,0)
39// 7 (x_l-w/2,0) (x_l+w/2,0) 3
40//
41// where the key variables are x_i = x_inlet, H = channel_height,
42// L = channel_length, x_l = x_leaflet, w = leaflet_width, h = leaflet_height
43// and the boundaries are labelled as shown.
44//
45// The solid domain is (initially) a simple rectangle with the following
46// boundary labels and positions:
47// 1
48// (x_l-w/2,h)------(x_l+w/2,h)
49// | |
50// 0 | |2
51// | |
52// | |
53// (x_l-w/2,0)------(x_l+w/2,0)
54// 3
55// For convenience, the coincident solid and fluid boundaries have the same
56// boundary labels in each mesh, but this is not required.
57
58//Generic routines
59#include "generic.h"
60#include "navier_stokes.h"
61#include "solid.h"
62#include "constitutive.h"
63
64
65// The mesh
66#include "meshes/triangle_mesh.h"
67
68using namespace std;
69using namespace oomph;
70
71///////////////////////////////////////////////////////////////////////
72///////////////////////////////////////////////////////////////////////
73///////////////////////////////////////////////////////////////////////
74
75
76
77//=======start_namespace==========================================
78/// Global variables
79//================================================================
81{
82 /// Reynolds number
83 double Re = 0.0;
84
85 /// FSI parameter
86 double Q = 0.0;
87
88 /// Poisson's ratio
89 double Nu=0.3;
90
91 /// Pointer to constitutive law
92 ConstitutiveLaw* Constitutive_law_pt=0;
93
94 /// Mesh poisson ratio
95 double Mesh_Nu = 0.1;
96
97 /// Pointer to constitutive law for the mesh
98 ConstitutiveLaw* Mesh_constitutive_law_pt=0;
99
100} //end namespace
101
102
103
104//==============start_problem=========================================
105/// Unstructured FSI problem
106//====================================================================
107template<class FLUID_ELEMENT, class SOLID_ELEMENT>
108class UnstructuredFSIProblem : public Problem
109{
110
111public:
112
113 /// Constructor:
115
116 /// Destructor (empty)
118
119 /// Doc the solution
120 void doc_solution(DocInfo& doc_info);
121
122 /// Actions before adapt
124 {
125 //Delete the boundary meshes
128
129 //Rebuild the global mesh
130 this->rebuild_global_mesh();
131 }
132
133
134 /// Actions after adapt
136 {
137 //Ensure that the lagrangian coordinates of the mesh are set to be
138 //the same as the eulerian
139 Fluid_mesh_pt->set_lagrangian_nodal_coordinates();
140
141 //Apply boundary conditions again
142
143 // Pin both positions at lower boundary (boundary 3)
144 unsigned ibound=3;
145 unsigned num_nod= Solid_mesh_pt->nboundary_node(ibound);
146 for (unsigned inod=0;inod<num_nod;inod++)
147 {
148
149 // Get node
150 SolidNode* nod_pt=Solid_mesh_pt->boundary_node_pt(ibound,inod);
151
152 // Pin both directions
153 for (unsigned i=0;i<2;i++)
154 {
155 nod_pt->pin_position(i);
156 }
157 }
158
159 // Complete the build of all elements so they are fully functional
160 unsigned n_element = Solid_mesh_pt->nelement();
161 for(unsigned i=0;i<n_element;i++)
162 {
163 //Cast to a solid element
164 SOLID_ELEMENT *el_pt =
165 dynamic_cast<SOLID_ELEMENT*>(Solid_mesh_pt->element_pt(i));
166
167 // Set the constitutive law
168 el_pt->constitutive_law_pt() =
170 } // end complete solid build
171
172
173 // Set the boundary conditions for fluid problem: All nodes are
174 // free by default
175 // --- just pin the ones that have Dirichlet conditions here.
176
177 //Pin velocity everywhere apart from parallel outflow (boundary 4)
178 unsigned nbound=Fluid_mesh_pt->nboundary();
179 for(unsigned ibound=0;ibound<nbound;ibound++)
180 {
181 unsigned num_nod=Fluid_mesh_pt->nboundary_node(ibound);
182 for (unsigned inod=0;inod<num_nod;inod++)
183 {
184 // Pin velocity everywhere apart from outlet where we
185 // have parallel outflow
186 if (ibound!=4)
187 {
188 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->pin(0);
189 }
190 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->pin(1);
191
192 // Pin pseudo-solid positions everywhere apart from boundaries 0, 1, 2
193 // the fsi boundaries
194 if(ibound > 2)
195 {
196 for(unsigned i=0;i<2;i++)
197 {
198 // Pin the node
199 SolidNode* nod_pt=Fluid_mesh_pt->boundary_node_pt(ibound,inod);
200 nod_pt->pin_position(i);
201 }
202 }
203 }
204 } // end loop over boundaries
205
206
207 // Complete the build of the fluid elements so they are fully functional
208 n_element = Fluid_mesh_pt->nelement();
209 for(unsigned e=0;e<n_element;e++)
210 {
211 // Upcast from GeneralisedElement to the present element
212 FLUID_ELEMENT* el_pt =
213 dynamic_cast<FLUID_ELEMENT*>(Fluid_mesh_pt->element_pt(e));
214
215 //Set the Reynolds number
216 el_pt->re_pt() = &Global_Physical_Variables::Re;
217
218 // Set the constitutive law for pseudo-elastic mesh deformation
219 el_pt->constitutive_law_pt() =
221
222 } // end loop over elements
223
224
225 // Apply fluid boundary conditions: Poiseuille at inflow
226 const unsigned n_boundary = Fluid_mesh_pt->nboundary();
227 for (unsigned ibound=0;ibound<n_boundary;ibound++)
228 {
229 const unsigned num_nod= Fluid_mesh_pt->nboundary_node(ibound);
230 for (unsigned inod=0;inod<num_nod;inod++)
231 {
232 // Parabolic inflow at the left boundary (boundary 6)
233 if(ibound==6)
234 {
235 double y=Fluid_mesh_pt->boundary_node_pt(ibound,inod)->x(1);
236 double veloc = y*(1.0-y);
237 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(0,veloc);
238 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(1,0.0);
239 }
240 // Zero flow elsewhere
241 else
242 {
243 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(0,0.0);
244 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(1,0.0);
245 }
246 }
247 } // end Poiseuille
248
249 //Recreate the boundary elements
252
253 //Rebuild the global mesh
254 this->rebuild_global_mesh();
255
256 // Setup FSI (again)
257 //------------------
258 // Work out which fluid dofs affect the residuals of the wall elements:
259 // We pass the boundary between the fluid and solid meshes and
260 // pointers to the meshes. The interaction boundary are boundaries 0, 1 and 2
261 // of the 2D fluid mesh.
262 for(unsigned b=0;b<3;b++)
263 {
264 FSI_functions::setup_fluid_load_info_for_solid_elements<FLUID_ELEMENT,2>
266 }
267
268 }
269
270 /// Output function to compute the strain energy in the solid and the
271 /// dissipation in the fluid and write to the output stream trace
272 void output_strain_and_dissipation(std::ostream &trace)
273 {
274 double strain_energy = this->get_solid_strain_energy();
275 double dissipation = this->get_fluid_dissipation();
276
278 " " << dissipation << " " << strain_energy << std::endl;
279 }
280
281
282private:
283
284 /// Create the traction element
286 {
287 // Traction elements are located on boundaries 0 1 and 2 of solid bulk mesh
288 for(unsigned b=0;b<3;++b)
289 {
290 // How many bulk elements are adjacent to boundary b?
291 const unsigned n_element = Solid_mesh_pt->nboundary_element(b);
292
293 // Loop over the bulk elements adjacent to boundary b
294 for(unsigned e=0;e<n_element;e++)
295 {
296 // Get pointer to the bulk element that is adjacent to boundary b
297 SOLID_ELEMENT* bulk_elem_pt = dynamic_cast<SOLID_ELEMENT*>(
298 Solid_mesh_pt->boundary_element_pt(b,e));
299
300 //What is the index of the face of the element e along boundary b
301 int face_index = Solid_mesh_pt->face_index_at_boundary(b,e);
302
303 // Create new element
304 FSISolidTractionElement<SOLID_ELEMENT,2>* el_pt=
305 new FSISolidTractionElement<SOLID_ELEMENT,2>(bulk_elem_pt,face_index);
306
307 // Add it to the mesh
308 Traction_mesh_pt[b]->add_element_pt(el_pt);
309
310 // Specify boundary number
311 el_pt->set_boundary_number_in_bulk_mesh(b);
312
313 // Function that specifies the load ratios
314 el_pt->q_pt() = &Global_Physical_Variables::Q;
315 }
316 }
317 }
318
319 /// Delete the traction elements
321 {
322 //There are three traction element boundaries
323 for(unsigned b=0;b<3;++b)
324 {
325 const unsigned n_element = Traction_mesh_pt[b]->nelement();
326 //Delete the elements
327 for(unsigned e=0;e<n_element;e++)
328 {
329 delete Traction_mesh_pt[b]->element_pt(e);
330 }
331 //Wipe the mesh
332 Traction_mesh_pt[b]->flush_element_and_node_storage();
333 //Also wipe out the Mesh as Geometric objects
335 }
336 }
337
338/// Create the multipliers that add lagrange multipliers to the fluid
339/// elements that apply the solid displacement conditions
341 {
342 //Loop over the FSI boundaries
343 for(unsigned b=0;b<3;b++)
344 {
345 // Create GeomObject incarnation of fsi boundary in solid mesh
346 Solid_fsi_boundary_pt[b] = new MeshAsGeomObject(Traction_mesh_pt[b]);
347
348 // How many bulk fluid elements are adjacent to boundary b?
349 unsigned n_element = Fluid_mesh_pt->nboundary_element(b);
350
351 // Loop over the bulk fluid elements adjacent to boundary b?
352 for(unsigned e=0;e<n_element;e++)
353 {
354 // Get pointer to the bulk fluid element that is adjacent to boundary b
355 FLUID_ELEMENT* bulk_elem_pt = dynamic_cast<FLUID_ELEMENT*>(
356 Fluid_mesh_pt->boundary_element_pt(b,e));
357
358 //Find the index of the face of element e along boundary b
359 int face_index = Fluid_mesh_pt->face_index_at_boundary(b,e);
360
361 // Create new element
362 ImposeDisplacementByLagrangeMultiplierElement<FLUID_ELEMENT>* el_pt =
363 new ImposeDisplacementByLagrangeMultiplierElement<FLUID_ELEMENT>(
364 bulk_elem_pt,face_index);
365
366 // Add it to the mesh
367 Lagrange_multiplier_mesh_pt[b]->add_element_pt(el_pt);
368
369 // Set the GeomObject that defines the boundary shape and set
370 // which bulk boundary we are attached to (needed to extract
371 // the boundary coordinate from the bulk nodes)
372 el_pt->set_boundary_shape_geom_object_pt(Solid_fsi_boundary_pt[b],b);
373
374 // Loop over the nodes to apply boundary conditions
375 unsigned nnod=el_pt->nnode();
376 for (unsigned j=0;j<nnod;j++)
377 {
378 Node* nod_pt = el_pt->node_pt(j);
379
380 // How many nodal values were used by the "bulk" element
381 // that originally created this node?
382 unsigned n_bulk_value=el_pt->nbulk_value(j);
383
384 // The remaining ones are Lagrange multipliers
385 unsigned nval=nod_pt->nvalue();
386 //If we have more than two Lagrange multipliers, pin the rest
387 if(nval > n_bulk_value + 2)
388 {
389 for (unsigned j=n_bulk_value+2;j<nval;j++)
390 {
391 nod_pt->pin(j);
392 }
393 }
394
395 //If I'm also on one of the base boundaries, pin the Lagrange
396 //Multipliers
397 if((nod_pt->is_on_boundary(7)) || (nod_pt->is_on_boundary(3)))
398 {
399 for(unsigned j=n_bulk_value;j<nval;j++)
400 {
401 nod_pt->pin(j);
402 }
403 }
404
405 }
406 }
407 }
408 } // end of create_lagrange_multiplier_elements
409
410
411 /// Delete the traction elements
413 {
414 //There are three Lagrange-multiplier element boundaries
415 for(unsigned b=0;b<3;++b)
416 {
417 const unsigned n_element = Lagrange_multiplier_mesh_pt[b]->nelement();
418 //Delete the elements
419 for(unsigned e=0;e<n_element;e++)
420 {
421 delete Lagrange_multiplier_mesh_pt[b]->element_pt(e);
422 }
423 //Wipe the mesh
424 Lagrange_multiplier_mesh_pt[b]->flush_element_and_node_storage();
425 }
426 }
427
428
429
430 /// Calculate the strain energy of the solid
432 {
433 double strain_energy=0.0;
434 const unsigned n_element = Solid_mesh_pt->nelement();
435 for(unsigned e=0;e<n_element;e++)
436 {
437 //Cast to a solid element
438 SOLID_ELEMENT *el_pt =
439 dynamic_cast<SOLID_ELEMENT*>(Solid_mesh_pt->element_pt(e));
440
441 double pot_en, kin_en;
442 el_pt->get_energy(pot_en,kin_en);
443 strain_energy += pot_en;
444 }
445 return strain_energy;
446 }
447
448 /// Calculate the fluid dissipation
450 {
451 double dissipation=0.0;
452 const unsigned n_element = Fluid_mesh_pt->nelement();
453 for(unsigned e=0;e<n_element;e++)
454 {
455 //Cast to a fluid element
456 FLUID_ELEMENT *el_pt =
457 dynamic_cast<FLUID_ELEMENT*>(Fluid_mesh_pt->element_pt(e));
458 //Add to the dissipation
459 dissipation += el_pt->dissipation();
460 }
461 return dissipation;
462 }
463
464 /// Bulk solid mesh
465 RefineableSolidTriangleMesh<SOLID_ELEMENT>* Solid_mesh_pt;
466
467public:
468 /// Bulk fluid mesh
469 RefineableSolidTriangleMesh<FLUID_ELEMENT>* Fluid_mesh_pt;
470
471private:
472
473 /// Vector of pointers to mesh of Lagrange multiplier elements
474 Vector<SolidMesh*> Lagrange_multiplier_mesh_pt;
475
476 /// Vectors of pointers to mesh of traction elements
477 Vector<SolidMesh*> Traction_mesh_pt;
478
479 /// Triangle mesh polygon for outer boundary
480 TriangleMeshPolygon* Solid_outer_boundary_polyline_pt;
481
482 /// Triangle mesh polygon for outer boundary
483 TriangleMeshPolygon* Fluid_outer_boundary_polyline_pt;
484
485 // GeomObject incarnation of fsi boundaries in solid mesh
486 Vector<MeshAsGeomObject*> Solid_fsi_boundary_pt;
487
488};
489
490
491
492//===============start_constructor========================================
493/// Constructor for unstructured solid problem
494//========================================================================
495template<class FLUID_ELEMENT, class SOLID_ELEMENT>
497{
498
499 //Some geometric parameters
500 double x_inlet = 0.0;
501 double channel_height = 1.0;
502 double channel_length = 4.0;
503 double x_leaflet = 1.0;
504 double leaflet_width = 0.2;
505 double leaflet_height = 0.5;
506
507 // Solid Mesh
508 //---------------
509
510 // Build the boundary segments for outer boundary, consisting of
511 //--------------------------------------------------------------
512 // four separeate polyline segments
513 //---------------------------------
514 Vector<TriangleMeshCurveSection*> solid_boundary_segment_pt(4);
515
516 // Initialize boundary segment
517 Vector<Vector<double> > bound_seg(2);
518 for(unsigned i=0;i<2;i++) {bound_seg[i].resize(2);}
519
520 // First boundary segment
521 bound_seg[0][0]=x_leaflet - 0.5*leaflet_width;
522 bound_seg[0][1]=0.0;
523 bound_seg[1][0]=x_leaflet - 0.5*leaflet_width;
524 bound_seg[1][1]=leaflet_height;
525
526 // Specify 1st boundary id
527 unsigned bound_id = 0;
528
529 // Build the 1st boundary segment
530 solid_boundary_segment_pt[0] = new TriangleMeshPolyLine(bound_seg,bound_id);
531
532 // Second boundary segment
533 bound_seg[0][0]=x_leaflet - 0.5*leaflet_width;
534 bound_seg[0][1]=leaflet_height;
535 bound_seg[1][0]=x_leaflet + 0.5*leaflet_width;
536 bound_seg[1][1]=leaflet_height;
537
538 // Specify 2nd boundary id
539 bound_id = 1;
540
541 // Build the 2nd boundary segment
542 solid_boundary_segment_pt[1] = new TriangleMeshPolyLine(bound_seg,bound_id);
543
544 // Third boundary segment
545 bound_seg[0][0]=x_leaflet + 0.5*leaflet_width;
546 bound_seg[0][1]=leaflet_height;
547 bound_seg[1][0]=x_leaflet + 0.5*leaflet_width;
548 bound_seg[1][1]=0.0;
549
550 // Specify 3rd boundary id
551 bound_id = 2;
552
553 // Build the 3rd boundary segment
554 solid_boundary_segment_pt[2] = new TriangleMeshPolyLine(bound_seg,bound_id);
555
556 // Fourth boundary segment
557 bound_seg[0][0]=x_leaflet + 0.5*leaflet_width;
558 bound_seg[0][1]=0.0;
559 bound_seg[1][0]=x_leaflet - 0.5*leaflet_width;
560 bound_seg[1][1]=0.0;
561
562 // Specify 4th boundary id
563 bound_id = 3;
564
565 // Build the 4th boundary segment
566 solid_boundary_segment_pt[3] = new TriangleMeshPolyLine(bound_seg,bound_id);
567
568 // Create the triangle mesh polygon for outer boundary using boundary segment
569 Solid_outer_boundary_polyline_pt =
570 new TriangleMeshPolygon(solid_boundary_segment_pt);
571
572 // There are no holes
573 //-------------------------------
574
575 // Now build the mesh, based on the boundaries specified by
576 //---------------------------------------------------------
577 // polygons just created
578 //----------------------
579 double uniform_element_area= leaflet_width*leaflet_height/20.0;
580
581 TriangleMeshClosedCurve* solid_closed_curve_pt=
582 Solid_outer_boundary_polyline_pt;
583
584 // Use the TriangleMeshParameters object for gathering all
585 // the necessary arguments for the TriangleMesh object
586 TriangleMeshParameters triangle_mesh_parameters_solid(
587 solid_closed_curve_pt);
588
589 // Define the maximum element area
590 triangle_mesh_parameters_solid.element_area() =
591 uniform_element_area;
592
593 // Create the mesh
594 Solid_mesh_pt =
595 new RefineableSolidTriangleMesh<SOLID_ELEMENT>(
596 triangle_mesh_parameters_solid);
597
598 // Set error estimator for bulk mesh
599 Z2ErrorEstimator* error_estimator_pt=new Z2ErrorEstimator;
600 Solid_mesh_pt->spatial_error_estimator_pt()=error_estimator_pt;
601
602 // Set targets for spatial adaptivity
603 Solid_mesh_pt->max_permitted_error()=0.0001;
604 Solid_mesh_pt->min_permitted_error()=0.001;
605 Solid_mesh_pt->max_element_size()=0.2;
606 Solid_mesh_pt->min_element_size()=0.001;
607
608 // Output boundary and mesh
609 this->Solid_mesh_pt->output_boundaries("solid_boundaries.dat");
610 this->Solid_mesh_pt->output("solid_mesh.dat");
611
612 // Pin both positions at lower boundary (boundary 3)
613 unsigned ibound=3;
614 unsigned num_nod= Solid_mesh_pt->nboundary_node(ibound);
615 for (unsigned inod=0;inod<num_nod;inod++)
616 {
617
618 // Get node
619 SolidNode* nod_pt=Solid_mesh_pt->boundary_node_pt(ibound,inod);
620
621 // Pin both directions
622 for (unsigned i=0;i<2;i++)
623 {
624 nod_pt->pin_position(i);
625 }
626 } // end_solid_boundary_conditions
627
628 // Complete the build of all elements so they are fully functional
629 unsigned n_element = Solid_mesh_pt->nelement();
630 for(unsigned i=0;i<n_element;i++)
631 {
632 //Cast to a solid element
633 SOLID_ELEMENT *el_pt =
634 dynamic_cast<SOLID_ELEMENT*>(Solid_mesh_pt->element_pt(i));
635
636 // Set the constitutive law
637 el_pt->constitutive_law_pt() =
639 }
640
641
642 // Fluid Mesh
643 //--------------
644
645 // Build the boundary segments for outer boundary, consisting of
646 //--------------------------------------------------------------
647 // four separeate polyline segments
648 //---------------------------------
649 Vector<TriangleMeshCurveSection*> fluid_boundary_segment_pt(8);
650
651 //The first three boundaries should be in common with the solid
652 for(unsigned b=0;b<3;b++)
653 {
654 fluid_boundary_segment_pt[b] = solid_boundary_segment_pt[b];
655 }
656
657 //Now fill in the rest
658 // Fourth boundary segment
659 bound_seg[0][0]=x_leaflet + 0.5*leaflet_width;
660 bound_seg[0][1]=0.0;
661 bound_seg[1][0]=x_inlet + channel_length;
662 bound_seg[1][1]=0.0;
663
664 // Specify 4th boundary id
665 bound_id = 3;
666
667 // Build the 4th boundary segment
668 fluid_boundary_segment_pt[3] = new TriangleMeshPolyLine(bound_seg,bound_id);
669
670 // Fifth boundary segment
671 bound_seg[0][0]=x_inlet + channel_length;
672 bound_seg[0][1]=0.0;
673 bound_seg[1][0]=x_inlet + channel_length;
674 bound_seg[1][1]=channel_height;
675
676 // Specify 5th boundary id
677 bound_id = 4;
678
679 // Build the 4th boundary segment
680 fluid_boundary_segment_pt[4] = new TriangleMeshPolyLine(bound_seg,bound_id);
681
682 // Sixth boundary segment
683 bound_seg[0][0]=x_inlet + channel_length;
684 bound_seg[0][1]=channel_height;
685 bound_seg[1][0]=x_inlet;
686 bound_seg[1][1]=channel_height;
687
688 // Specify 6th boundary id
689 bound_id = 5;
690
691 // Build the 6th boundary segment
692 fluid_boundary_segment_pt[5] = new TriangleMeshPolyLine(bound_seg,bound_id);
693
694 // Seventh boundary segment
695 bound_seg[0][0]=x_inlet;
696 bound_seg[0][1]=channel_height;
697 bound_seg[1][0]=x_inlet;
698 bound_seg[1][1]=0.0;
699
700 // Specify 7th boundary id
701 bound_id = 6;
702
703 // Build the 7th boundary segment
704 fluid_boundary_segment_pt[6] = new TriangleMeshPolyLine(bound_seg,bound_id);
705
706 // Eighth boundary segment
707 bound_seg[0][0]=x_inlet;
708 bound_seg[0][1]=0.0;
709 bound_seg[1][0]=x_leaflet - 0.5*leaflet_width;
710 bound_seg[1][1]=0.0;
711
712 // Specify 8th boundary id
713 bound_id = 7;
714
715 // Build the 8th boundary segment
716 fluid_boundary_segment_pt[7] = new TriangleMeshPolyLine(bound_seg,bound_id);
717
718 // Create the triangle mesh polygon for outer boundary using boundary segment
719 Fluid_outer_boundary_polyline_pt =
720 new TriangleMeshPolygon(fluid_boundary_segment_pt);
721
722 // There are no holes
723 //-------------------------------
724
725 // Now build the mesh, based on the boundaries specified by
726 //---------------------------------------------------------
727 // polygons just created
728 //----------------------
729 uniform_element_area= channel_length*channel_height/40.0;;
730
731 TriangleMeshClosedCurve* fluid_closed_curve_pt=
732 Fluid_outer_boundary_polyline_pt;
733
734 // Use the TriangleMeshParameters object for gathering all
735 // the necessary arguments for the TriangleMesh object
736 TriangleMeshParameters triangle_mesh_parameters_fluid(
737 fluid_closed_curve_pt);
738
739 // Define the maximum element area
740 triangle_mesh_parameters_fluid.element_area() =
741 uniform_element_area;
742
743 // Create the mesh
744 Fluid_mesh_pt =
745 new RefineableSolidTriangleMesh<FLUID_ELEMENT>(
746 triangle_mesh_parameters_fluid);
747
748 // Set error estimator for bulk mesh
749 Z2ErrorEstimator* fluid_error_estimator_pt=new Z2ErrorEstimator;
750 Fluid_mesh_pt->spatial_error_estimator_pt()=fluid_error_estimator_pt;
751
752 // Set targets for spatial adaptivity
753 Fluid_mesh_pt->max_permitted_error()=0.0001;
754 Fluid_mesh_pt->min_permitted_error()=0.001;
755 Fluid_mesh_pt->max_element_size()=0.2;
756 Fluid_mesh_pt->min_element_size()=0.001;
757
758 // Output boundary and mesh
759 this->Fluid_mesh_pt->output_boundaries("fluid_boundaries.dat");
760 this->Fluid_mesh_pt->output("fluid_mesh.dat");
761
762 // Set the boundary conditions for fluid problem: All nodes are
763 // free by default
764 // --- just pin the ones that have Dirichlet conditions here.
765
766 //Pin velocity everywhere apart from parallel outflow (boundary 4)
767 unsigned nbound=Fluid_mesh_pt->nboundary();
768 for(unsigned ibound=0;ibound<nbound;ibound++)
769 {
770 unsigned num_nod=Fluid_mesh_pt->nboundary_node(ibound);
771 for (unsigned inod=0;inod<num_nod;inod++)
772 {
773 // Pin velocity everywhere apart from outlet where we
774 // have parallel outflow
775 if (ibound!=4)
776 {
777 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->pin(0);
778 }
779 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->pin(1);
780
781 // Pin pseudo-solid positions everywhere apart from boundaries 0, 1, 2
782 // the fsi boundaries
783 if(ibound > 2)
784 {
785 for(unsigned i=0;i<2;i++)
786 {
787 // Pin the node
788 SolidNode* nod_pt=Fluid_mesh_pt->boundary_node_pt(ibound,inod);
789 nod_pt->pin_position(i);
790 }
791 }
792 }
793 } // end loop over boundaries
794
795
796 // Complete the build of the fluid elements so they are fully functional
797 n_element = Fluid_mesh_pt->nelement();
798 for(unsigned e=0;e<n_element;e++)
799 {
800 // Upcast from GeneralisedElement to the present element
801 FLUID_ELEMENT* el_pt =
802 dynamic_cast<FLUID_ELEMENT*>(Fluid_mesh_pt->element_pt(e));
803
804 //Set the Reynolds number
805 el_pt->re_pt() = &Global_Physical_Variables::Re;
806
807 // Set the constitutive law for pseudo-elastic mesh deformation
808 el_pt->constitutive_law_pt() =
810
811 } // end loop over elements
812
813
814 // Apply fluid boundary conditions: Poiseuille at inflow
815 const unsigned n_boundary = Fluid_mesh_pt->nboundary();
816 for (unsigned ibound=0;ibound<n_boundary;ibound++)
817 {
818 const unsigned num_nod= Fluid_mesh_pt->nboundary_node(ibound);
819 for (unsigned inod=0;inod<num_nod;inod++)
820 {
821 // Parabolic inflow at the left boundary (boundary 6)
822 if(ibound==6)
823 {
824 double y=Fluid_mesh_pt->boundary_node_pt(ibound,inod)->x(1);
825 double veloc = y*(1.0-y);
826 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(0,veloc);
827 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(1,0.0);
828 }
829 // Zero flow elsewhere
830 else
831 {
832 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(0,0.0);
833 Fluid_mesh_pt->boundary_node_pt(ibound,inod)->set_value(1,0.0);
834 }
835 }
836 } // end Poiseuille
837
838
839 // Make traction mesh
840 //(This must be done first because the resulting meshes are used
841 // as the geometric objects that set the boundary locations of the fluid
842 // mesh, as enforced by the Lagrange multipliers)
843 Traction_mesh_pt.resize(3);
844 for(unsigned m=0;m<3;m++) {Traction_mesh_pt[m] = new SolidMesh;}
845 this->create_fsi_traction_elements();
846
847 //Make the Lagrange multiplier mesh
848 Lagrange_multiplier_mesh_pt.resize(3);
849 Solid_fsi_boundary_pt.resize(3);
850 for(unsigned m=0;m<3;m++) {Lagrange_multiplier_mesh_pt[m] = new SolidMesh;}
851 this->create_lagrange_multiplier_elements();
852
853 // Add sub meshes
854 add_sub_mesh(Fluid_mesh_pt);
855 add_sub_mesh(Solid_mesh_pt);
856 for(unsigned m=0;m<3;m++)
857 {
858 add_sub_mesh(Traction_mesh_pt[m]);
859 add_sub_mesh(Lagrange_multiplier_mesh_pt[m]);
860 }
861
862 // Build global mesh
863 build_global_mesh();
864
865 // Setup FSI
866 //----------
867 // Work out which fluid dofs affect the residuals of the wall elements:
868 // We pass the boundary between the fluid and solid meshes and
869 // pointers to the meshes. The interaction boundary are boundaries 0, 1 and 2
870 // of the 2D fluid mesh.
871 for(unsigned b=0;b<3;b++)
872 {
873 FSI_functions::setup_fluid_load_info_for_solid_elements<FLUID_ELEMENT,2>
874 (this,b,Fluid_mesh_pt,Traction_mesh_pt[b]);
875 }
876
877 // Setup equation numbering scheme
878 cout <<"Number of equations: " << assign_eqn_numbers() << std::endl;
879
880} //end constructor
881
882
883//========================================================================
884/// Doc the solution
885//========================================================================
886template<class FLUID_ELEMENT, class SOLID_ELEMENT>
888doc_solution(DocInfo& doc_info)
889{
890
891 ofstream some_file;
892 char filename[100];
893
894 // Number of plot points
895 unsigned npts;
896 npts=5;
897
898 // Output solution
899 //----------------
900 snprintf(filename, sizeof(filename), "%s/solid_soln%i.dat",doc_info.directory().c_str(),
901 doc_info.number());
902 some_file.open(filename);
903 Solid_mesh_pt->output(some_file,npts);
904 some_file.close();
905
906 //----------------
907 snprintf(filename, sizeof(filename), "%s/fluid_soln%i.dat",doc_info.directory().c_str(),
908 doc_info.number());
909 some_file.open(filename);
910 Fluid_mesh_pt->output(some_file,npts);
911 some_file.close();
912
913}
914
915
916
917
918
919//===========start_main===================================================
920/// Demonstrate how to solve an unstructured solid problem
921//========================================================================
922int main(int argc, char **argv)
923{
924
925 // Store command line arguments
926 CommandLineArgs::setup(argc,argv);
927
928 // Define possible command line arguments and parse the ones that
929 // were actually specified
930
931 // Validation?
932 CommandLineArgs::specify_command_line_flag("--validation");
933
934 // Parse command line
935 CommandLineArgs::parse_and_assign();
936
937 // Doc what has actually been specified on the command line
938 CommandLineArgs::doc_specified_flags();
939
940 DocInfo doc_info;
941
942 // Output directory
943 doc_info.set_directory("RESLT");
944
945 //Create a trace file
946 std::ofstream trace("RESLT/trace.dat");
947
948 // Create generalised Hookean constitutive equations
950 new GeneralisedHookean(&Global_Physical_Variables::Nu);
951
952 // Create generalised Hookean constitutive equations for the mesh as well
954 new GeneralisedHookean(&Global_Physical_Variables::Mesh_Nu);
955
956 //Set up the problem
958 ProjectableTaylorHoodElement<
959 PseudoSolidNodeUpdateElement<TTaylorHoodElement<2>, TPVDElement<2,3> > >,
960 ProjectablePVDElement<TPVDElement<2,3> > > problem;
961
962//Output initial configuration
963problem.doc_solution(doc_info);
964doc_info.number()++;
965
966// Solve the problem
967problem.newton_solve();
968
969//Output solution
970problem.doc_solution(doc_info);
971doc_info.number()++;
972
973//Calculate the strain energy of the solid and dissipation in the
974//fluid as global output measures of the solution for validation purposes
975problem.output_strain_and_dissipation(trace);
976
977//Number of steps to be taken
978unsigned n_step = 10;
979//Reduce the number of steps if validating
980if (CommandLineArgs::command_line_flag_has_been_set("--validation"))
981 {
982 n_step=3;
983 }
984
985//Now Crank up interaction
986for(unsigned i=0;i<n_step;i++)
987 {
989 problem.newton_solve(1);
990
991 //Reset the lagrangian nodal coordinates in the fluid mesh
992 //(Obviously we shouldn't do this in the solid mesh)
993 problem.Fluid_mesh_pt->set_lagrangian_nodal_coordinates();
994 //Output solution
995 problem.doc_solution(doc_info);
996 doc_info.number()++;
997
998 //Calculate the strain energy of the solid and dissipation in the
999 //fluid as global output measures of the solution for validation purposes
1000 problem.output_strain_and_dissipation(trace);
1001 }
1002
1003trace.close();
1004} // end main
1005
1006
1007
void doc_solution(DocInfo &doc_info)
Doc the solution.
Vector< MeshAsGeomObject * > Solid_fsi_boundary_pt
Vector< SolidMesh * > Lagrange_multiplier_mesh_pt
Vector of pointers to mesh of Lagrange multiplier elements.
RefineableSolidTriangleMesh< SOLID_ELEMENT > * Solid_mesh_pt
Bulk solid mesh.
void actions_before_adapt()
Actions before adapt.
double get_solid_strain_energy()
Calculate the strain energy of the solid.
void create_lagrange_multiplier_elements()
Create the multipliers that add lagrange multipliers to the fluid elements that apply the solid displ...
void create_fsi_traction_elements()
Create the traction element.
void delete_fsi_traction_elements()
Delete the traction elements.
RefineableSolidTriangleMesh< FLUID_ELEMENT > * Fluid_mesh_pt
Bulk fluid mesh.
void output_strain_and_dissipation(std::ostream &trace)
Output function to compute the strain energy in the solid and the dissipation in the fluid and write ...
Vector< SolidMesh * > Traction_mesh_pt
Vectors of pointers to mesh of traction elements.
void delete_lagrange_multiplier_elements()
Delete the traction elements.
TriangleMeshPolygon * Fluid_outer_boundary_polyline_pt
Triangle mesh polygon for outer boundary.
void actions_after_adapt()
Actions after adapt.
double get_fluid_dissipation()
Calculate the fluid dissipation.
TriangleMeshPolygon * Solid_outer_boundary_polyline_pt
Triangle mesh polygon for outer boundary.
ConstitutiveLaw * Constitutive_law_pt
Pointer to constitutive law.
ConstitutiveLaw * Mesh_constitutive_law_pt
Pointer to constitutive law for the mesh.
int main(int argc, char **argv)
Demonstrate how to solve an unstructured solid problem.