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