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-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 // 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 
68 using namespace std;
69 using 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 //====================================================================
107 template<class FLUID_ELEMENT, class SOLID_ELEMENT>
108 class UnstructuredFSIProblem : public Problem
109 {
110 
111 public:
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
126  this->delete_lagrange_multiplier_elements();
127  this->delete_fsi_traction_elements();
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
250  this->create_fsi_traction_elements();
251  this->create_lagrange_multiplier_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>
265  (this,b,Fluid_mesh_pt,Traction_mesh_pt[b]);
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 
277  trace << Global_Physical_Variables::Q <<
278  " " << dissipation << " " << strain_energy << std::endl;
279  }
280 
281 
282 private:
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
334  delete Solid_fsi_boundary_pt[b]; Solid_fsi_boundary_pt[b] = 0;
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 
467 public:
468  /// Bulk fluid mesh
469  RefineableSolidTriangleMesh<FLUID_ELEMENT>* Fluid_mesh_pt;
470 
471 private:
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 //========================================================================
495 template<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 //========================================================================
886 template<class FLUID_ELEMENT, class SOLID_ELEMENT>
888 doc_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  sprintf(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  sprintf(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 //========================================================================
922 int 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
963 problem.doc_solution(doc_info);
964 doc_info.number()++;
965 
966 // Solve the problem
967 problem.newton_solve();
968 
969 //Output solution
970 problem.doc_solution(doc_info);
971 doc_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
975 problem.output_strain_and_dissipation(trace);
976 
977 //Number of steps to be taken
978 unsigned n_step = 10;
979 //Reduce the number of steps if validating
980 if (CommandLineArgs::command_line_flag_has_been_set("--validation"))
981  {
982  n_step=3;
983  }
984 
985 //Now Crank up interaction
986 for(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 
1003 trace.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.
~UnstructuredFSIProblem()
Destructor (empty)
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.