channel_with_leaflet_mesh.template.h
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 #ifndef OOMPH_CHANNEL_WITH_LEAFLET_MESH_HEADER
27 #define OOMPH_CHANNEL_WITH_LEAFLET_MESH_HEADER
28 
29 // Generic includes
30 #include "../generic/refineable_quad_mesh.h"
31 #include "../generic/macro_element.h"
32 #include "../generic/domain.h"
33 #include "../generic/quad_mesh.h"
34 
35 // Mesh is based on simple_rectangular_quadmesh
38 
39 // Include macro elements
40 #include "../generic/macro_element_node_update_element.h"
41 
42 // and algebraic elements
43 #include "../generic/algebraic_elements.h"
44 
45 // Include the headers file for domain
47 
48 namespace oomph
49 {
50  //===================================================================
51  /// Channel with leaflet mesh
52  //===================================================================
53  template<class ELEMENT>
55  {
56  public:
57  /// Constructor: Pass pointer to GeomObject that represents the
58  /// leaflet,
59  /// the length of the domain to left and right of the leaflet, the
60  /// height of the leaflet and the overall height of the channel,
61  /// the number of element columns to the left and right of the leaflet,
62  /// the number of rows of elements from the bottom of the channel to
63  /// the end of the leaflet, the number of rows of elements above the
64  /// end of the leaflet. Timestepper defaults to Steady default
65  /// Timestepper defined in the Mesh base class
67  GeomObject* leaflet_pt,
68  const double& lleft,
69  const double& lright,
70  const double& hleaflet,
71  const double& htot,
72  const unsigned& nleft,
73  const unsigned& nright,
74  const unsigned& ny1,
75  const unsigned& ny2,
76  TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper);
77 
78  /// Destructor : empty
80 
81  /// Access function to domain
83  {
84  return Domain_pt;
85  }
86 
87  protected:
88  /// Pointer to domain
90 
91  /// Pointer to GeomObject that represents the leaflet
92  GeomObject* Leaflet_pt;
93  };
94 
95 
96  /// //////////////////////////////////////////////////////////////////
97  /// //////////////////////////////////////////////////////////////////
98  /// //////////////////////////////////////////////////////////////////
99 
100 
101  //===================================================================
102  /// Refineable version of ChannelWithLeafletMesh
103  //===================================================================
104  template<class ELEMENT>
106  : public virtual ChannelWithLeafletMesh<ELEMENT>,
107  public RefineableQuadMesh<ELEMENT>
108  {
109  public:
110  /// Constructor: Pass pointer to GeomObject that represents the
111  /// leaflet,
112  /// the length of the domain to left and right of the leaflet, the
113  /// height of the leaflet and the overall height of the channel,
114  /// the number of element columns to the left and right of the leaflet,
115  /// the number of rows of elements from the bottom of the channel to
116  /// the end of the leaflet, the number of rows of elements above the
117  /// end of the leaflet. Timestepper defaults to Steady default
118  /// Timestepper defined in the Mesh base class
120  GeomObject* leaflet_pt,
121  const double& lleft,
122  const double& lright,
123  const double& hleaflet,
124  const double& htot,
125  const unsigned& nleft,
126  const unsigned& nright,
127  const unsigned& ny1,
128  const unsigned& ny2,
129  TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
130  : ChannelWithLeafletMesh<ELEMENT>(leaflet_pt,
131  lleft,
132  lright,
133  hleaflet,
134  htot,
135  nleft,
136  nright,
137  ny1,
138  ny2,
139  time_stepper_pt)
140  {
141  // Build quadtree forest
142  this->setup_quadtree_forest();
143  }
144 
145  /// Destructor (empty)
147  };
148 
149 
150  /// //////////////////////////////////////////////////////////////////
151  /// //////////////////////////////////////////////////////////////////
152  /// //////////////////////////////////////////////////////////////////
153 
154 
155  //=====start_of_mesh=======================================================
156  /// Channel with leaflet mesh with MacroElement-based node update.
157  /// The leaflet is represented by the specified geometric object.
158  /// Some or all of the geometric Data in that geometric object
159  /// may contain unknowns in the global Problem. The dependency
160  /// on these unknowns is taken into account when setting up
161  /// the Jacobian matrix of the elements. For this purpose,
162  /// the element (whose type is specified by the template parameter)
163  /// must inherit from MacroElementNodeUpdateElementBase.
164  //========================================================================
165  template<class ELEMENT>
167  : public virtual MacroElementNodeUpdateMesh,
168  public virtual ChannelWithLeafletMesh<ELEMENT>
169  {
170  public:
171  /// Constructor: Pass pointer to GeomObject that represents the
172  /// leaflet,
173  /// the length of the domain to left and right of the leaflet, the
174  /// height of the leaflet and the overall height of the channel,
175  /// the number of element columns to the left and right of the leaflet,
176  /// the number of rows of elements from the bottom of the channel to
177  /// the end of the leaflet, the number of rows of elements above the
178  /// end of the leaflet. Timestepper defaults to Steady default
179  /// Timestepper defined in the Mesh base class
181  GeomObject* leaflet_pt,
182  const double& lleft,
183  const double& lright,
184  const double& hleaflet,
185  const double& htot,
186  const unsigned& nleft,
187  const unsigned& nright,
188  const unsigned& ny1,
189  const unsigned& ny2,
190  TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
191  : ChannelWithLeafletMesh<ELEMENT>(leaflet_pt,
192  lleft,
193  lright,
194  hleaflet,
195  htot,
196  nleft,
197  nright,
198  ny1,
199  ny2,
200  time_stepper_pt)
201  {
202 #ifdef PARANOID
203  ELEMENT* el_pt = new ELEMENT;
204  if (dynamic_cast<MacroElementNodeUpdateElementBase*>(el_pt) == 0)
205  {
206  std::ostringstream error_message;
207  error_message << "Base class for ELEMENT in "
208  << "MacroElementNodeUpdateChannelWithLeafletMesh needs"
209  << "to be of type MacroElementNodeUpdateElement!\n";
210  error_message << "Whereas it is: typeid(el_pt).name()"
211  << typeid(el_pt).name() << std::endl;
212 
213  std::string function_name =
214  "MacroElementNodeUpdateChannelWithLeafletMesh::\n";
215  function_name += "MacroElementNodeUpdateChannelWithLeafletMesh()";
216 
217  throw OomphLibError(error_message.str(),
218  OOMPH_CURRENT_FUNCTION,
219  OOMPH_EXCEPTION_LOCATION);
220  }
221  delete el_pt;
222 #endif
223 
224  // Setup all the information that's required for MacroElement-based
225  // node update: Tell the elements that their geometry depends on the
226  // wall geometric object
227  unsigned n_element = this->nelement();
228  for (unsigned i = 0; i < n_element; i++)
229  {
230  // Upcast from FiniteElement to the present element
231  ELEMENT* el_pt = dynamic_cast<ELEMENT*>(this->element_pt(i));
232 
233 #ifdef PARANOID
234  // Check if cast is successful
235  MacroElementNodeUpdateElementBase* m_el_pt =
236  dynamic_cast<MacroElementNodeUpdateElementBase*>(el_pt);
237  if (m_el_pt == 0)
238  {
239  std::ostringstream error_message;
240  error_message
241  << "Failed to upcast to MacroElementNodeUpdateElementBase\n";
242  error_message << "Element must be derived from "
243  "MacroElementNodeUpdateElementBase\n";
244  error_message << "but it is of type " << typeid(el_pt).name();
245 
246  std::string function_name =
247  "MacroElementNodeUpdateChannelWithLeafletMesh::\n";
248  function_name += "MacroElementNodeUpdateChannelWithLeafletMesh()";
249 
250  throw OomphLibError(error_message.str(),
251  OOMPH_CURRENT_FUNCTION,
252  OOMPH_EXCEPTION_LOCATION);
253  }
254 #endif
255 
256  // There's just one GeomObject
257  Vector<GeomObject*> geom_object_pt(1);
258  geom_object_pt[0] = this->Leaflet_pt;
259 
260  // Tell the element which geom objects its macro-element-based
261  // node update depends on
262  el_pt->set_node_update_info(geom_object_pt);
263  }
264 
265  // Add the geometric object(s) for the wall to the mesh's storage
266  Vector<GeomObject*> geom_object_pt(1);
267  geom_object_pt[0] = this->Leaflet_pt;
268  MacroElementNodeUpdateMesh::set_geom_object_vector_pt(geom_object_pt);
269 
270  // Fill in the domain pointer to the mesh's storage in the base class
271  MacroElementNodeUpdateMesh::macro_domain_pt() = this->domain_pt();
272 
273  } // end of constructor
274 
275 
276  /// Destructor: empty
278 
279 
280  }; // end of mesh
281 
282 
283  /// /////////////////////////////////////////////////////////////////////////
284  /// /////////////////////////////////////////////////////////////////////////
285  /// /////////////////////////////////////////////////////////////////////////
286 
287 
288  //=====start_of_mesh=======================================================
289  /// Refineable mesh with MacroElement-based node update.
290  //========================================================================
291  template<class ELEMENT>
293  : public virtual MacroElementNodeUpdateChannelWithLeafletMesh<ELEMENT>,
294  public virtual RefineableQuadMesh<ELEMENT>
295  {
296  public:
297  /// Constructor: Pass pointer to GeomObject that represents the
298  /// leaflet,
299  /// the length of the domain to left and right of the leaflet, the
300  /// height of the leaflet and the overall height of the channel,
301  /// the number of element columns to the left and right of the leaflet,
302  /// the number of rows of elements from the bottom of the channel to
303  /// the end of the leaflet, the number of rows of elements above the
304  /// end of the leaflet. Timestepper defaults to Steady default
305  /// Timestepper defined in the Mesh base class
307  GeomObject* leaflet_pt,
308  const double& lleft,
309  const double& lright,
310  const double& hleaflet,
311  const double& htot,
312  const unsigned& nleft,
313  const unsigned& nright,
314  const unsigned& ny1,
315  const unsigned& ny2,
316  TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
317  : ChannelWithLeafletMesh<ELEMENT>(leaflet_pt,
318  lleft,
319  lright,
320  hleaflet,
321  htot,
322  nleft,
323  nright,
324  ny1,
325  ny2,
326  time_stepper_pt),
328  lleft,
329  lright,
330  hleaflet,
331  htot,
332  nleft,
333  nright,
334  ny1,
335  ny2,
336  time_stepper_pt)
337  {
338  // Build quadtree forest
339  this->setup_quadtree_forest();
340  }
341 
342 
343  /// Destructor: empty
345 
346  }; // end of mesh
347 
348 
349  /// ////////////////////////////////////////////////////////////////////
350  /// ///////////////////////////////////////////////////////////////////
351  /// ////////////////////////////////////////////////////////////////////
352 
353 
354  //=================================================================
355  /// Algebraic version of ChannelWithLeafletMesh. Leaflet is
356  /// assumed to be in its undeformed (straight vertical) position
357  /// when the algebraic node update is set up.
358  //=================================================================
359  template<class ELEMENT>
361  : public AlgebraicMesh,
362  public virtual ChannelWithLeafletMesh<ELEMENT>
363  {
364  public:
365  /// Constructor: Pass pointer to GeomObject that represents the
366  /// leaflet,
367  /// the length of the domain to left and right of the leaflet, the
368  /// height of the leaflet and the overall height of the channel,
369  /// the number of element columns to the left and right of the leaflet,
370  /// the number of rows of elements from the bottom of the channel to
371  /// the end of the leaflet, the number of rows of elements above the
372  /// end of the leaflet. Timestepper defaults to Steady default
373  /// Timestepper defined in the Mesh base class
375  GeomObject* leaflet_pt,
376  const double& lleft,
377  const double& lright,
378  const double& hleaflet,
379  const double& htot,
380  const unsigned& nleft,
381  const unsigned& nright,
382  const unsigned& ny1,
383  const unsigned& ny2,
384  TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
385  : ChannelWithLeafletMesh<ELEMENT>(leaflet_pt,
386  lleft,
387  lright,
388  hleaflet,
389  htot,
390  nleft,
391  nright,
392  ny1,
393  ny2,
394  time_stepper_pt)
395  {
396  // Store origin of leaflet for fast reference
397  Vector<double> zeta(1);
398  zeta[0] = 0.0;
399  Vector<double> r(2);
400  this->Leaflet_pt->position(zeta, r);
401  X_0 = r[0];
402 
403  // Store length of the leaflet for fast access (it's also available
404  // through the domain, of course)
405  Hleaflet = hleaflet;
406 
407  // Add the geometric object to the list associated with this AlgebraicMesh
408  AlgebraicMesh::add_geom_object_list_pt(leaflet_pt);
409 
410  // Setup algebraic node update operations
412  }
413 
414 
415  /// Destructor: empty
417 
418 
419  /// Update the geometric references that are used
420  /// to update node after mesh adaptation.
421  /// Empty -- no update of node update required without adaptivity
422  void update_node_update(AlgebraicNode*& node_pt) {}
423 
424  /// Update nodal position at time level t (t=0: present;
425  /// t>0: previous)
426  void algebraic_node_update(const unsigned& t, AlgebraicNode*& node_pt);
427 
428  protected:
429  /// Function to setup the algebraic node update
431 
432  /// Update function for nodes in lower left region (I)
433  void node_update_I(const unsigned& t, AlgebraicNode*& node_pt);
434 
435  /// Update function for nodes in lower right region (II)
436  void node_update_II(const unsigned& t, AlgebraicNode*& node_pt);
437 
438  /// Update function for nodes in upper left region (III)
439  void node_update_III(const unsigned& t, AlgebraicNode*& node_pt);
440 
441  /// Update function for nodes in upper right region (IV)
442  void node_update_IV(const unsigned& t, AlgebraicNode*& node_pt);
443 
444  /// Helper function
445  void slanted_bound_up(const unsigned& t,
446  const Vector<double>& zeta,
447  Vector<double>& r);
448 
449  /// Origin of the wall (stored explicitly for reference in
450  /// algebraic node update -- it's also stored independently in
451  /// domain....)
452  double X_0;
453 
454  /// Length of the leaflet (stored explicitly for reference in
455  /// algebraic node update -- it's also stored independently in
456  /// domain....)
457  double Hleaflet;
458  };
459 
460  /// ////////////////////////////////////////////////////////////////////////
461  /// ////////////////////////////////////////////////////////////////////////
462  /// ////////////////////////////////////////////////////////////////////////
463 
464 
465  //===================================================================
466  /// Refineable version of algebraic ChannelWithLeafletMesh
467  //===================================================================
468  template<class ELEMENT>
470  : public RefineableQuadMesh<ELEMENT>,
471  public virtual AlgebraicChannelWithLeafletMesh<ELEMENT>
472  {
473  public:
474  /// Constructor: Pass pointer to GeomObject that represents the
475  /// leaflet,
476  /// the length of the domain to left and right of the leaflet, the
477  /// height of the leaflet and the overall height of the channel,
478  /// the number of element columns to the left and right of the leaflet,
479  /// the number of rows of elements from the bottom of the channel to
480  /// the end of the leaflet, the number of rows of elements above the
481  /// end of the leaflet. Timestepper defaults to Steady default
482  /// Timestepper defined in the Mesh base class
484  GeomObject* leaflet_pt,
485  const double& lleft,
486  const double& lright,
487  const double& hleaflet,
488  const double& htot,
489  const unsigned& nleft,
490  const unsigned& nright,
491  const unsigned& ny1,
492  const unsigned& ny2,
493  TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
494  : ChannelWithLeafletMesh<ELEMENT>(leaflet_pt,
495  lleft,
496  lright,
497  hleaflet,
498  htot,
499  nleft,
500  nright,
501  ny1,
502  ny2,
503  time_stepper_pt),
504  AlgebraicChannelWithLeafletMesh<ELEMENT>(leaflet_pt,
505  lleft,
506  lright,
507  hleaflet,
508  htot,
509  nleft,
510  nright,
511  ny1,
512  ny2,
513  time_stepper_pt)
514  {
515  // Build quadtree forest
516  this->setup_quadtree_forest();
517  }
518 
519  /// Update the node update data for specified node following
520  /// any mesh adapation
521  void update_node_update(AlgebraicNode*& node_pt);
522  };
523 
524 
525  /// //////////////////////////////////////////////////////////////////
526  /// //////////////////////////////////////////////////////////////////
527  /// //////////////////////////////////////////////////////////////////
528 
529 
530  //==========================================================================
531  /// Channel with leaflet mesh upgraded to (pseudo-)solid mesh
532  //==========================================================================
533  template<class ELEMENT>
535  : public virtual ChannelWithLeafletMesh<ELEMENT>,
536  public virtual SolidMesh
537  {
538  public:
539  /// Constructor: Pass pointer to GeomObject that represents the
540  /// leaflet, the length of the domain to left and right of the leaflet, the
541  /// height of the leaflet and the overall height of the channel,
542  /// the number of element columns to the left and right of the leaflet,
543  /// the number of rows of elements from the bottom of the channel to
544  /// the end of the leaflet, the number of rows of elements above the
545  /// end of the leaflet. Timestepper defaults to Steady default
546  /// Timestepper defined in the Mesh base class
548  GeomObject* leaflet_pt,
549  const double& lleft,
550  const double& lright,
551  const double& hleaflet,
552  const double& htot,
553  const unsigned& nleft,
554  const unsigned& nright,
555  const unsigned& ny1,
556  const unsigned& ny2,
557  TimeStepper* time_stepper_pt = &Mesh::Default_TimeStepper)
558  : ChannelWithLeafletMesh<ELEMENT>(leaflet_pt,
559  lleft,
560  lright,
561  hleaflet,
562  htot,
563  nleft,
564  nright,
565  ny1,
566  ny2,
567  time_stepper_pt)
568  {
569  // Update position of all nodes (the ones haven't been given
570  // positions yet!)
571  bool update_all_solid_nodes = true;
572  node_update(update_all_solid_nodes);
573 
574  // Make the current configuration the undeformed one by
575  // setting the nodal Lagrangian coordinates to their current
576  // Eulerian ones
577  set_lagrangian_nodal_coordinates();
578  }
579 
580  /// Destructor : empty
582  };
583 
584 
585 } // namespace oomph
586 
587 #endif
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
void setup_algebraic_node_update()
Function to setup the algebraic node update.
double X_0
Origin of the wall (stored explicitly for reference in algebraic node update – it's also stored indep...
AlgebraicChannelWithLeafletMesh(GeomObject *leaflet_pt, const double &lleft, const double &lright, const double &hleaflet, const double &htot, const unsigned &nleft, const unsigned &nright, const unsigned &ny1, const unsigned &ny2, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass pointer to GeomObject that represents the leaflet, the length of the domain to left...
double Hleaflet
Length of the leaflet (stored explicitly for reference in algebraic node update – it's also stored in...
void algebraic_node_update(const unsigned &t, AlgebraicNode *&node_pt)
Update nodal position at time level t (t=0: present; t>0: previous)
void node_update_I(const unsigned &t, AlgebraicNode *&node_pt)
Update function for nodes in lower left region (I)
void node_update_III(const unsigned &t, AlgebraicNode *&node_pt)
Update function for nodes in upper left region (III)
void node_update_II(const unsigned &t, AlgebraicNode *&node_pt)
Update function for nodes in lower right region (II)
void slanted_bound_up(const unsigned &t, const Vector< double > &zeta, Vector< double > &r)
Helper function.
void node_update_IV(const unsigned &t, AlgebraicNode *&node_pt)
Update function for nodes in upper right region (IV)
void update_node_update(AlgebraicNode *&node_pt)
Update the geometric references that are used to update node after mesh adaptation....
Rectangular domain with a leaflet blocking the lower half.
ChannelWithLeafletDomain * Domain_pt
Pointer to domain.
ChannelWithLeafletDomain * domain_pt()
Access function to domain.
GeomObject * Leaflet_pt
Pointer to GeomObject that represents the leaflet.
ChannelWithLeafletMesh(GeomObject *leaflet_pt, const double &lleft, const double &lright, const double &hleaflet, const double &htot, const unsigned &nleft, const unsigned &nright, const unsigned &ny1, const unsigned &ny2, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass pointer to GeomObject that represents the leaflet, the length of the domain to left...
virtual ~ChannelWithLeafletMesh()
Destructor : empty.
////////////////////////////////////////////////////////////////// //////////////////////////////////...
MacroElementNodeUpdateChannelWithLeafletMesh(GeomObject *leaflet_pt, const double &lleft, const double &lright, const double &hleaflet, const double &htot, const unsigned &nleft, const unsigned &nright, const unsigned &ny1, const unsigned &ny2, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass pointer to GeomObject that represents the leaflet, the length of the domain to left...
///////////////////////////////////////////////////////////////////////// ///////////////////////////...
MacroElementNodeUpdateRefineableChannelWithLeafletMesh(GeomObject *leaflet_pt, const double &lleft, const double &lright, const double &hleaflet, const double &htot, const unsigned &nleft, const unsigned &nright, const unsigned &ny1, const unsigned &ny2, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass pointer to GeomObject that represents the leaflet, the length of the domain to left...
////////////////////////////////////////////////////////////////// //////////////////////////////////...
PseudoElasticChannelWithLeafletMesh(GeomObject *leaflet_pt, const double &lleft, const double &lright, const double &hleaflet, const double &htot, const unsigned &nleft, const unsigned &nright, const unsigned &ny1, const unsigned &ny2, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass pointer to GeomObject that represents the leaflet, the length of the domain to left...
//////////////////////////////////////////////////////////////////////// ////////////////////////////...
RefineableAlgebraicChannelWithLeafletMesh(GeomObject *leaflet_pt, const double &lleft, const double &lright, const double &hleaflet, const double &htot, const unsigned &nleft, const unsigned &nright, const unsigned &ny1, const unsigned &ny2, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass pointer to GeomObject that represents the leaflet, the length of the domain to left...
void update_node_update(AlgebraicNode *&node_pt)
Update the node update data for specified node following any mesh adapation.
////////////////////////////////////////////////////////////////// //////////////////////////////////...
RefineableChannelWithLeafletMesh(GeomObject *leaflet_pt, const double &lleft, const double &lright, const double &hleaflet, const double &htot, const unsigned &nleft, const unsigned &nright, const unsigned &ny1, const unsigned &ny2, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass pointer to GeomObject that represents the leaflet, the length of the domain to left...
Simple rectangular 2D Quad mesh class. Nx : number of elements in the x direction.
////////////////////////////////////////////////////////////////////// //////////////////////////////...