Example codes
and tutorials
The (Not-so) Quick Guide
List of tutorials/demo codes
Single-Physics Problems
Poisson
Adaptivity illustrated for Poisson
Advection-Diffusion
Unsteady heat equation
Linear wave equation
The Young-Laplace equation
Navier-Stokes
Free-surface Navier-Stokes
Axisymmetric Navier-Stokes
Solid mechanics
Beam structures
Shell structures
Multi-physics Problems
Fluid-structure interaction
Boussinesq convection
Steady thermoelasticity
Methods-based example codes and tutorials
Mesh generation
Linear solvers and preconditioners
Visualisation of the results
Parallel processing
How to write a new element
How to write a new refineable element
Default nonlinear solvers -- the sequence of action functions
...
Documentation
FE theory and top-down discussion of the data structure
The (Not-so) Quick Guide
Comprehensive bottom-up discussion of the data structure
List of available structured and unstructured meshes
Linear solvers and preconditioners
Visualisation of the results
Parallel processing
Coding conventions and C++ style
Creating documentation
Optimisation - robustness vs. "raw speed"
Linear vs. nonlinear problems
Storing shape functions
Changing the default "full" integration scheme
Disabling the ALE formulation of unsteady equations
C vs. C++ output
Different sparse assembly techniques and the STL memory pool
Publications
Publications
Talks
Journal publications
Theses
Picture show
Download
Copyright
Download/installation instructions
Download page
FAQ & Contact
FAQ
Change log
Bugs and other known problems
Completeness of the library & our "To-Do List"
Contact the developers
Get involved

 


Beta release!

Please note that the library has not been "officially" released. While we continue to work on the documentation, these web pages are likely to contain broken links and documents in draft form. Please send an email to

oomph-lib AT maths DOT man DOT ac DOT uk

if you wish to be informed of the library's "official" release.

refineable_elements.h

Go to the documentation of this file.
00001 //LIC// ====================================================================
00002 //LIC// This file forms part of oomph-lib, the object-oriented, 
00003 //LIC// multi-physics finite-element library, available 
00004 //LIC// at http://www.oomph-lib.org.
00005 //LIC// 
00006 //LIC//           Version 0.90. August 3, 2009.
00007 //LIC// 
00008 //LIC// Copyright (C) 2006-2009 Matthias Heil and Andrew Hazel
00009 //LIC// 
00010 //LIC// This library is free software; you can redistribute it and/or
00011 //LIC// modify it under the terms of the GNU Lesser General Public
00012 //LIC// License as published by the Free Software Foundation; either
00013 //LIC// version 2.1 of the License, or (at your option) any later version.
00014 //LIC// 
00015 //LIC// This library is distributed in the hope that it will be useful,
00016 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
00017 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00018 //LIC// Lesser General Public License for more details.
00019 //LIC// 
00020 //LIC// You should have received a copy of the GNU Lesser General Public
00021 //LIC// License along with this library; if not, write to the Free Software
00022 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
00023 //LIC// 02110-1301  USA.
00024 //LIC// 
00025 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
00026 //LIC// 
00027 //LIC//====================================================================
00028 //Header file for classes that define refineable element objects
00029 
00030 //Include guard to prevent multiple inclusions of the header
00031 #ifndef OOMPH_REFINEABLE_ELEMENTS_HEADER
00032 #define OOMPH_REFINEABLE_ELEMENTS_HEADER
00033 
00034 // Config header generated by autoconfig
00035 #ifdef HAVE_CONFIG_H
00036   #include <oomph-lib-config.h>
00037 #endif
00038 
00039 #include "elements.h"
00040 #include "tree.h"
00041 
00042 namespace oomph
00043 {
00044 
00045 class Mesh;
00046 
00047 //=======================================================================
00048 /// RefineableElements are FiniteElements that may be subdivided into 
00049 /// children to provide a better local approximation to the solution. 
00050 /// After non-uniform refinement adjacent elements need not necessarily have
00051 /// nodes in common. A node that does not have a counterpart in its
00052 /// neighbouring element is known as a hanging node and its position and
00053 /// any data that it stores must be constrained to ensure inter-element
00054 /// continuity. 
00055 ///
00056 /// Generic data and function interfaces associated with refinement 
00057 /// are defined in this class.
00058 /// 
00059 /// Additional data includes:
00060 /// - a pointer to a general Tree object that is used to track the
00061 ///   refinement history,
00062 /// - a refinement level (not necessarily the same as the level in the tree!),
00063 /// - a flag indicating whether the element should be refined,
00064 /// - a flag indicating whether the element should be de-refined,
00065 /// - a global element number for plotting/validation  purposes,
00066 /// - storage for local equation numbers associated with hanging nodes.
00067 ///
00068 /// Additional functions perform the following generic tasks:
00069 /// - provide access to  additional data,
00070 /// - setup local equation numbering for data associated with hanging nodes,
00071 /// - generic finite-difference calculation of contributions to the 
00072 ///   elemental jacobian from nodal data to include hanging nodes,
00073 /// - split of the element into its sons,
00074 /// - select and deselect the element for refinement,
00075 /// - select and deselect the sons of the element for de-refinement (merging),
00076 ///
00077 /// In addition, there are a number of interfaces that specify element-specific
00078 /// tasks. These should be overloaded in RefineableElements of particular
00079 /// geometric types and perform the following tasks:
00080 /// - return a pointer to the root and father elements in the tree structure,
00081 /// - define the number of sons into which the element is divided,
00082 /// - build the element: construct nodes, assign their positions, 
00083 ///   values and any boundary conditions,
00084 /// - recreate the element from its sons if they are merged,
00085 /// - deactivate the element, perform any operations that are 
00086 ///   required when the element is still in the tree, but no longer active 
00087 /// - set the number and provide access to the values interpolated
00088 ///   by the nodes,
00089 /// - setup the hanging nodes
00090 ///  
00091 /// In mixed element different sets of nodes are used to interpolate different
00092 /// unknowns. Interfaces are provided for functions that can be used to find
00093 /// the position of the nodes that interpolate the different unknowns. These
00094 /// functions are used to setup hanging node information automatically in
00095 /// particular elements, e.g. Taylor Hood Navier--Stokes. 
00096 /// The default implementation assumes that the elements are isoparameteric.
00097 ///
00098 //======================================================================
00099 class RefineableElement : public virtual FiniteElement
00100 {
00101   protected:
00102 
00103  /// A pointer to a general tree object
00104  Tree* Tree_pt;
00105 
00106  /// Refinement level
00107  unsigned Refine_level;
00108 
00109  /// Flag for refinement
00110  bool To_be_refined;
00111  
00112  /// Flag for unrefinement
00113  bool Sons_to_be_unrefined;
00114 
00115  /// Global element number -- for plotting/validation purposes
00116  long Number;
00117   
00118  /// \short Max. allowed discrepancy in element integrity check
00119  static double Max_integrity_tolerance;
00120 
00121  /// \short Static helper function that is used to check that the value_id
00122  /// is in range
00123  static void check_value_id(const int &n_continuously_interpolated_values,
00124                             const int &value_id);
00125 
00126 
00127   /// \short Assemble the jacobian matrix for the mapping from local
00128  /// to Eulerian coordinates, given the derivatives of the shape function
00129  /// w.r.t the local coordinates.
00130  /// Overload the standard version to use the hanging information for
00131  /// the Eulerian coordinates.
00132  void assemble_local_to_eulerian_jacobian(
00133   const DShape &dpsids, DenseMatrix<double> &jacobian) const;
00134  
00135  /// \short Assemble the the "jacobian" matrix of second derivatives of the
00136  /// mapping from local to Eulerian coordinates, given
00137  /// the second derivatives of the shape functions w.r.t. local coordinates.
00138  /// Overload the standard version to use the hanging information for
00139  /// the Eulerian coordinates.
00140  void assemble_local_to_eulerian_jacobian2(
00141   const DShape &d2psids, DenseMatrix<double> &jacobian2) const;
00142  
00143  /// \short Assemble the covariant Eulerian base vectors, assuming that
00144  /// the derivatives of the shape functions with respect to the local 
00145  /// coordinates have already been constructed.
00146  /// Overload the standard version to account for hanging nodes.
00147  void assemble_eulerian_base_vectors(
00148   const DShape &dpsids, DenseMatrix<double> &interpolated_G) const;
00149  
00150  /// \short Calculate the mapping from local to Eulerian coordinates given 
00151  /// the derivatives of the shape functions w.r.t the local coordinates.
00152  /// assuming that the coordinates are aligned in the direction of the local 
00153  /// coordinates, i.e. there are no cross terms and the jacobian is diagonal.
00154  /// This funciton returns the determinant of the jacobian, the jacobian 
00155  /// and the inverse jacobian. Overload the standard version to take
00156  /// hanging info into account.
00157  double local_to_eulerian_mapping_diagonal(
00158   const DShape &dpsids,DenseMatrix<double> &jacobian,
00159   DenseMatrix<double> &inverse_jacobian) const;
00160 
00161   private:
00162 
00163  /// \short Storage for local equation numbers of hanging node variables
00164  /// (values stored at master nodes). It is
00165  /// essential that these are indexed by a Node pointer because the Node 
00166  /// may be internal or external to the element.
00167  /// local equation number = Local_hang_eqn(master_node_pt,ival)
00168   std::map<Node*,int> *Local_hang_eqn;
00169 
00170  /// \short Lookup scheme for unique number associated with any of the nodes
00171  /// that actively control the shape of the element (i.e. they are either
00172  /// non-hanging nodes of this element or master nodes of hanging nodes.
00173  std::map<Node*,unsigned> Shape_controlling_node_lookup;
00174 
00175   protected:
00176  
00177  /// \short Access function that returns the local equation number for the
00178  /// hanging node variables (values stored at master nodes). The local
00179  /// equation number corresponds to the i-th unknown stored at the node
00180  /// addressed by node_pt
00181  inline int local_hang_eqn(Node* const &node_pt, const unsigned &i)
00182   {
00183 #ifdef RANGE_CHECKING
00184    if(i > ncont_interpolated_values())
00185     {
00186      std::ostringstream error_message;
00187      error_message << "Range Error: Value " << i
00188                    << " is not in the range (0,"
00189                    << ncont_interpolated_values()-1 << ")";
00190      throw OomphLibError(error_message.str(),
00191                          "RefineableElement::local_hang_eqn()",
00192                          OOMPH_EXCEPTION_LOCATION);
00193     }
00194 #endif
00195 
00196    return Local_hang_eqn[i][node_pt];
00197   }
00198 
00199 
00200  /// \short Assign the local equation numbers for hanging node variables
00201  void assign_hanging_local_eqn_numbers();
00202 
00203  /// \short Calculate the contributions to the jacobian from the nodal
00204  /// degrees of freedom using finite differences.
00205  /// This version is overloaded to take hanging node information into
00206  /// account
00207  virtual void fill_in_jacobian_from_nodal_by_fd(Vector<double> &residuals,
00208                                             DenseMatrix<double> &jacobian);
00209 
00210   public:
00211 
00212  /// \short Constructor, calls the FiniteElement constructor and initialises
00213  /// the member data
00214  RefineableElement() : FiniteElement(), Tree_pt(0), Refine_level(0),
00215   To_be_refined(false), Sons_to_be_unrefined(false), Number(-1),
00216   Local_hang_eqn(0) {}
00217 
00218 
00219  /// Destructor, delete the allocated storage
00220  /// for the hanging equations
00221  virtual ~RefineableElement() {delete[] Local_hang_eqn;}
00222 
00223  /// Broken copy constructor
00224  RefineableElement(const RefineableElement&) 
00225   { 
00226    BrokenCopy::broken_copy("RefineableElement");
00227   } 
00228  
00229  /// Broken assignment operator
00230  void operator=(const RefineableElement&) 
00231   {
00232    BrokenCopy::broken_assign("RefineableElement");
00233   }
00234 
00235   /// Access function: Pointer to quadtree representation of this element
00236  Tree* tree_pt() {return Tree_pt;}
00237 
00238  /// Set pointer to quadtree representation of this element
00239  void set_tree_pt(Tree* my_tree_pt) {Tree_pt=my_tree_pt;}
00240 
00241  /// \short Set the number of sons that can be constructed by the element
00242  /// The default is none
00243  virtual unsigned required_nsons() const {return 0;}
00244 
00245  /// \short Split the element into the  number of sons to be
00246  /// constructed and return a 
00247  /// vector of pointers to the sons. Elements are allocated, but they are
00248  /// not given any properties. The refinement level of the sons is one
00249  /// higher than that of the father elemern.
00250  template<class ELEMENT>
00251   void split(Vector<ELEMENT*>& son_pt) const
00252   {
00253    // Increase refinement level
00254    int son_refine_level=Refine_level+1;
00255    
00256    //How many sons are to be constructed
00257    unsigned n_sons = required_nsons();
00258    //Resize the son pointer
00259    son_pt.resize(n_sons);
00260 
00261    //Loop over the sons and construct
00262    for(unsigned i=0;i<n_sons;i++)
00263     {
00264      son_pt[i]=new ELEMENT;
00265      //Set the refinement level of the newly constructed son.
00266      son_pt[i]->set_refinement_level(son_refine_level);
00267     }
00268   }
00269 
00270  /// \short Interface to function that builds the element: i.e. 
00271  /// construct the nodes, assign their positions, 
00272  //  apply boundary conditions, etc. The 
00273  /// required procedures depend on the geometrical type of the element
00274  /// and must be implemented in specific refineable elements. Any new
00275  /// nodes created during the build process are returned in the 
00276  /// vector new_node_pt.
00277  virtual void build(Mesh* &mesh_pt, Vector<Node*> &new_node_pt,
00278                     bool &was_already_built, std::ofstream &new_nodes_file)=0;
00279 
00280  /// Set the refinement level
00281  void set_refinement_level(const int& refine_level) 
00282   {Refine_level=refine_level;}
00283  
00284  /// Return the Refinement level
00285  unsigned refinement_level() const {return Refine_level;}
00286  
00287  /// Select the element for refinement
00288  void select_for_refinement() {To_be_refined=true;}
00289 
00290  /// Deselect the element for refinement
00291  void deselect_for_refinement() {To_be_refined=false;}
00292  
00293  /// Unrefinement will be performed by merging the four sons of this element
00294  void select_sons_for_unrefinement() {Sons_to_be_unrefined=true;}
00295 
00296  /// No unrefinement will be performed by merging the four sons of this element
00297  void deselect_sons_for_unrefinement() {Sons_to_be_unrefined=false;}
00298 
00299  /// Has the element been selected for refinement?
00300  bool to_be_refined() {return To_be_refined;}
00301 
00302  /// Has the element been selected for unrefinement?
00303  bool sons_to_be_unrefined() {return Sons_to_be_unrefined;}
00304 
00305  /// \short Rebuild the element, e.g. set internal values in line with
00306  /// those of the sons that have now merged
00307  virtual void rebuild_from_sons(Mesh* &mesh_pt)=0;
00308 
00309  /// \short Unbuild the element, i.e. mark the nodes that were created
00310  /// during its creation for possible deletion
00311  virtual void unbuild()
00312   {
00313    //Get pointer to father element
00314    RefineableElement* father_pt = father_element_pt();
00315    //If there is no father, nothing to do
00316    if(father_pt==0) {return;}
00317 
00318    //Loop over all the nodes
00319    unsigned n_node = this->nnode();
00320    for(unsigned n=0;n<n_node;n++)
00321     {
00322      //If any node in this element is in the father, it can't be deleted
00323      if(father_pt->get_node_number(this->node_pt(n)) >= 0)
00324       {
00325        node_pt(n)->set_non_obsolete();
00326       }
00327     }
00328   }
00329 
00330  /// \short Final operations that must be performed when the element is no
00331  /// longer active in the mesh, but still resident in the QuadTree.
00332  virtual void deactivate_element();
00333  
00334  /// Return true if all the nodes have been built, false if not
00335  virtual bool nodes_built() {return node_pt(0)!=0;}
00336 
00337  /// Element number (for debugging/plotting)
00338  long number() const {return Number;}
00339  
00340  /// Set element number (for debugging/plotting)
00341  void set_number(const long& mynumber) {Number=mynumber;}
00342 
00343  /// \short Number of continuously interpolated values. Note: We assume
00344  /// that they are located at the beginning of the value_pt Vector!
00345  /// (Used for interpolation to son elements, for integrity check
00346  /// and post-processing -- we can only expect
00347  /// the continously interpolated values to be continous across
00348  /// element boundaries).
00349  virtual unsigned ncont_interpolated_values() const=0;
00350 
00351  /// \short Get all continously interpolated function values in this 
00352  /// element as a Vector. Note: Vector sets is own size to ensure that
00353  /// that this function can be used in black-box fashion
00354  virtual void get_interpolated_values(const Vector<double>&s, 
00355                                       Vector<double>& values)=0;
00356  
00357  /// \short Get all continously interpolated function values at previous 
00358  /// timestep in this element as a Vector. (t=0: present; t>0: prev. timestep)
00359  /// Note: Vector sets is own size to ensure that
00360  /// that this function can be used in black-box fashion
00361  virtual void get_interpolated_values(const unsigned& t,
00362                                       const Vector<double>&s, 
00363                                       Vector<double>& values)=0;
00364 
00365  /// \short In mixed elements, different sets of nodes are used to interpolate
00366  /// different unknowns. This function returns the n-th node that interpolates
00367  /// the value_id-th unknown. Default implementation is that all
00368  /// variables use the positional nodes, i.e. isoparametric elements. Note
00369  /// that any overloaded versions of this function MUST provide a set
00370  /// of nodes for the position, which always has the value_id -1.
00371  virtual Node* interpolating_node_pt(const unsigned &n,
00372                                      const int &value_id)
00373   
00374   {return node_pt(n);}
00375 
00376  /// \short Return the local one dimensional fraction of the n1d-th node
00377  /// in the direction of the local coordinate s[i] that is used to interpolate
00378  /// the value_id-th continuously interpolated unknown. Default assumes 
00379  /// isoparametric interpolation for all unknowns
00380  virtual double local_one_d_fraction_of_interpolating_node(
00381   const unsigned &n1d, const unsigned &i, const int &value_id)
00382   {return local_one_d_fraction_of_node(n1d,i);}
00383 
00384  /// \short Return a pointer to the node that interpolates the value-id-th
00385  /// unknown at local coordinate s. If there is not a node at that position,
00386  /// then return 0.
00387  virtual Node* 
00388   get_interpolating_node_at_local_coordinate(const Vector<double> &s,
00389                                              const int &value_id)
00390                                              
00391   {return get_node_at_local_coordinate(s);}
00392 
00393 
00394  /// \short Return the number of nodes that are used to interpolate the
00395  /// value_id-th unknown. Default is to assume isoparametric elements.
00396  virtual unsigned ninterpolating_node(const int &value_id) {return nnode();}
00397 
00398  /// \short Return the number of nodes in a one_d direction that are 
00399  /// used to interpolate the value_id-th unknown. Default is to assume
00400  /// an isoparametric mapping.
00401  virtual unsigned ninterpolating_node_1d(const int &value_id) 
00402   {return nnode_1d();}
00403 
00404  /// \short Return the basis functions that are used to interpolate
00405  /// the value_id-th unknown. By default assume isoparameteric interpolation
00406  virtual void interpolating_basis(const Vector<double> &s,
00407                                   Shape &psi,
00408                                   const int &value_id) const
00409   {shape(s,psi);}
00410 
00411  /// \short Check the integrity of the element: Continuity of positions
00412  /// values, etc. Essentially, check that the approximation of the functions
00413  /// is consistent when viewed from both sides of the element boundaries
00414  /// Must be overloaded for each different geometric element
00415  virtual void check_integrity(double &max_error)=0;
00416 
00417 
00418   /// \short Max. allowed discrepancy in element integrity check
00419  static double max_integrity_tolerance()
00420   { return Max_integrity_tolerance;}
00421 
00422   /// \short The purpose of this function is to identify all possible
00423  /// Data that can affect the fields interpolated by the FiniteElement.
00424  /// This must be overloaded to include data from any hanging nodes 
00425  /// correctly
00426  void identify_field_data_for_interactions(
00427   std::set<std::pair<Data*,unsigned> > &paired_field_data);
00428 
00429 
00430  /// \short Overload the function that assigns local equation numbers
00431  /// for the Data stored at the nodes so that hanging data is taken 
00432  /// into account
00433  inline void assign_nodal_local_eqn_numbers()
00434   {
00435    FiniteElement::assign_nodal_local_eqn_numbers();
00436    assign_hanging_local_eqn_numbers();
00437   }
00438 
00439  /// Setup the local equation numbering schemes:
00440  /*virtual inline void assign_all_generic_local_eqn_numbers()
00441   {
00442    //Call the standard FiniteElement local numbering scheme
00443    FiniteElement::assign_all_generic_local_eqn_numbers();
00444    //Set up the generic hanging-node-based look-up schemes
00445    assign_hanging_local_eqn_numbers();
00446   }*/
00447 
00448  /// \short Pointer to the root element in refinement hierarchy (must be 
00449  /// implemented in specific elements that do refinement via
00450  /// tree-like refinement structure. Here we provide a default
00451  /// implementation that is appropriate for cases where tree-like
00452  /// refinement doesn't exist or if the element doesn't have 
00453  /// root in that tree (i.e. if it's a root itself): We return
00454  /// "this". 
00455  virtual RefineableElement* root_element_pt()
00456   {
00457    //If there is no tree -- the element is its own root
00458    if(Tree_pt==0) {return this;}
00459    //Otherwise it's the tree's root object
00460    else {return Tree_pt->root_pt()->object_pt();}
00461   }
00462 
00463  /// Return a pointer to the father element.
00464  virtual RefineableElement* father_element_pt() const 
00465   {
00466    //If we have no tree, we have no father
00467    if(Tree_pt==0) {return 0;}
00468    else
00469     {
00470      //Otherwise get the father of the tree
00471      Tree* father_pt = Tree_pt->father_pt();
00472      //If the tree has no father then return null, no father
00473      if(father_pt==0) {return 0;}
00474      else {return father_pt->object_pt();}
00475     }
00476   }
00477 
00478  /// Return a pointer to the "father" element at the specified refinement level
00479  void get_father_at_refinement_level(unsigned& refinement_level, 
00480                                      RefineableElement*& father_at_reflevel_pt)
00481   {
00482    // Get the father in the tree (it shouldn't try to get a null Tree...)
00483    Tree* father_pt = Tree_pt->father_pt();
00484    // Get the refineable element associated with this father
00485    RefineableElement* father_el_pt=dynamic_cast<RefineableElement*>
00486     (father_pt->object_pt());
00487    // Get the refinement level
00488    unsigned level=father_el_pt->refinement_level();
00489    // If the level matches the required one then return, if not call again
00490    if (level==refinement_level) 
00491     {
00492      father_at_reflevel_pt=father_el_pt;
00493     }
00494    else
00495     {
00496      // Recursive call
00497      father_el_pt->get_father_at_refinement_level(refinement_level,
00498                                                   father_at_reflevel_pt);
00499     }
00500   }
00501 
00502  /// \short Further build: e.g. deal with interpolation of internal values
00503  virtual void further_build() {}
00504  
00505  /// \short Mark up any hanging nodes that arise as a result of non-uniform
00506  /// refinement. Any hanging nodes will be documented in files addressed by
00507  /// the streams in the vector output_stream, if the streams are open.
00508  virtual void setup_hanging_nodes(Vector<std::ofstream*> &output_stream) { }
00509 
00510  /// \short Perform additional hanging node procedures for variables
00511  /// that are not interpolated by all nodes (e.g. lower order interpolations
00512  /// for the pressure in Taylor Hood). 
00513  virtual void further_setup_hanging_nodes() { }
00514 
00515  /// \short Compute derivatives of elemental residual vector with respect
00516  /// to nodal coordinates. Default implementation by FD can be overwritten
00517  /// for specific elements. 
00518  /// dresidual_dnodal_coordinates(l,i,j) = d res(l) / dX_{ij}
00519  /// This version is overloaded from the version in FiniteElement
00520  /// and takes hanging nodes into account -- j in the above loop 
00521  /// loops over all the nodes that actively control the
00522  /// shape of the element (i.e. they are non-hanging or master nodes of 
00523  /// hanging nodes in this element).
00524  void get_dresidual_dnodal_coordinates(RankThreeTensor<double>&
00525                                        dresidual_dnodal_coordinates);
00526  
00527 
00528  /// \short Number of shape-controlling nodes = the number
00529  /// of non-hanging nodes plus the number of master nodes associated
00530  /// with hanging nodes.
00531  unsigned nshape_controlling_nodes()
00532   {
00533    return Shape_controlling_node_lookup.size();
00534   }
00535 
00536  /// \short Return lookup scheme for unique number associated
00537  /// with any of the nodes that actively control the shape of the
00538  /// element (i.e. they are either non-hanging nodes of this element
00539  /// or master nodes of hanging nodes.
00540  std::map<Node*,unsigned> shape_controlling_node_lookup()
00541   {
00542    return Shape_controlling_node_lookup;
00543   }
00544 
00545 
00546 };
00547  
00548 
00549 //////////////////////////////////////////////////////////////////////
00550 //////////////////////////////////////////////////////////////////////
00551 //////////////////////////////////////////////////////////////////////
00552 
00553 
00554 
00555 
00556 
00557 //=======================================================================
00558 /// A base class for elements that can have hanging nodes
00559 /// but are not refineable as such. This class is usually used as a
00560 /// base class for FaceElements that are attached to refineable
00561 /// bulk elements (and stripped out before adapting the bulk
00562 /// mesh, so they don't participate in the refimenent process
00563 /// itself). We therefore simply break the pure virtual functions
00564 /// that don't make any sense for such elements
00565 //======================================================================
00566  class NonRefineableElementWithHangingNodes : public virtual RefineableElement
00567   {
00568    
00569     public:
00570    
00571    
00572    /// Broken build function -- shouldn't really be needed
00573    void build(Mesh* &mesh_pt, Vector<Node*> &new_node_pt,
00574               bool &was_already_built, std::ofstream &new_nodes_file)
00575     {
00576      std::ostringstream error_message;
00577      error_message << "This function is broken as it's only needed/used \n"
00578                    << "during \"proper\" refinement\n";
00579      throw OomphLibError(error_message.str(),
00580                          "NonRefineableElementWithHangingNodes::build()",
00581                          OOMPH_EXCEPTION_LOCATION);
00582     }
00583    
00584    
00585    
00586    /// \short Broken function -- this shouldn't really be needed.
00587    void get_interpolated_values(const Vector<double>&s, 
00588                                 Vector<double>& values)
00589     {
00590      std::ostringstream error_message;
00591      error_message << "This function is broken as it's only needed/used \n"
00592                    << "during \"proper\" refinement\n";
00593      throw OomphLibError(
00594       error_message.str(),
00595       "NonRefineableElementWithHangingNodes::get_interpolated_values()",
00596       OOMPH_EXCEPTION_LOCATION);
00597     }
00598    
00599    /// \short Broken function -- this shouldn't really be needed.
00600    virtual void get_interpolated_values(const unsigned& t,
00601                                         const Vector<double>&s, 
00602                                         Vector<double>& values)
00603     {
00604      std::ostringstream error_message;
00605      error_message << "This function is broken as it's only needed/used \n"
00606                    << "during \"proper\" refinement\n";
00607      throw OomphLibError(
00608       error_message.str(),
00609       "NonRefineableElementWithHangingNodes::get_interpolated_values()",
00610       OOMPH_EXCEPTION_LOCATION);
00611     }
00612    
00613    
00614    /// Broken function -- this shouldn't really be needed.
00615    void check_integrity(double &max_error)
00616     {    
00617      std::ostringstream error_message;
00618      error_message << "This function is broken as it's only needed/used \n"
00619                    << "during \"proper\" refinement\n";
00620      throw OomphLibError(
00621       error_message.str(),
00622       "NonRefineableElementWithHangingNodes::check_integrity()",
00623       OOMPH_EXCEPTION_LOCATION);
00624     }
00625    
00626    /// Broken function -- this shouldn't really be needed.
00627    void rebuild_from_sons(Mesh* &mesh_pt)
00628     {
00629      std::ostringstream error_message;
00630      error_message << "This function is broken as it's only needed/used \n"
00631                    << "during \"proper\" refinement\n";
00632      throw OomphLibError(
00633       error_message.str(),
00634       "NonRefineableElementWithHangingNodes::rebuild_from_sons()",
00635       OOMPH_EXCEPTION_LOCATION);
00636     }
00637    
00638    
00639    
00640   };
00641 
00642 
00643 //////////////////////////////////////////////////////////////////////
00644 //////////////////////////////////////////////////////////////////////
00645 //////////////////////////////////////////////////////////////////////
00646 
00647 
00648 
00649 
00650 //=======================================================================
00651 /// RefineableSolidElements are SolidFiniteElements that may 
00652 /// be subdivided into children to provide a better local approximation 
00653 /// to the solution. The distinction is required to keep a clean 
00654 /// separation between problems that alter nodal positions and others.
00655 /// A number of procedures are generic and are included in this class.
00656 //======================================================================
00657 class RefineableSolidElement : public virtual RefineableElement,
00658                                public virtual SolidFiniteElement
00659 {
00660 
00661   private:
00662 
00663  /// \short Storage for local equation numbers of 
00664  /// hanging node variables associated with nodal positions.
00665  /// local position equation number = 
00666  /// Local_position_hang_eqn(master_node_pt,ival)
00667  std::map<Node*,DenseMatrix<int> > Local_position_hang_eqn; 
00668 
00669 
00670  /// \short Assign local equation numbers to the hanging values associated
00671  /// with positions or additional solid values.
00672  void assign_solid_hanging_local_eqn_numbers();
00673 
00674 
00675  protected:
00676 
00677  /// \short Flag deciding if the Lagrangian coordinates of newly-created interior 
00678  /// SolidNodes are to be determined by the father element's undeformed 
00679  /// MacroElement representation (if it has one). Default: False as it means that, 
00680  /// following a refinement an element is no longer in equilbrium (as the Lagrangian
00681  /// coordinate is determined differently from the Eulerian one). However, basing
00682  /// the Lagrangian coordinates on the undeformed MacroElement can be
00683  /// more stable numerically and for steady problems it's not a big deal
00684  /// either way as the difference between the two formulations only matters
00685  /// at finite resolution so we have no right to say that one is "more correct"
00686  /// than the other...
00687  bool Use_undeformed_macro_element_for_new_lagrangian_coords;
00688  
00689  /// \short Access the local equation number of of hanging node variables
00690  /// associated with nodal positions. The function returns a dense
00691  /// matrix that contains all the local equation numbers corresponding to
00692  /// the positional degrees of freedom.
00693  DenseMatrix<int> &local_position_hang_eqn(Node* const &node_pt)
00694   {return Local_position_hang_eqn[node_pt];}
00695 
00696 
00697  /// \short Assemble the jacobian matrix for the mapping from local
00698  /// to lagrangian coordinates, given the derivatives of the shape function
00699  /// Overload the standard version to use the hanging information for
00700  /// the lagrangian coordinates.
00701 void assemble_local_to_lagrangian_jacobian(
00702   const DShape &dpsids, DenseMatrix<double> &jacobian) const;
00703 
00704  /// \short Assemble the the "jacobian" matrix of second derivatives, given
00705  /// the second derivatives of the shape functions w.r.t. local coordinates
00706  /// Overload the standard version to use the hanging information for
00707  /// the lagrangian coordinates.
00708  void assemble_local_to_lagrangian_jacobian2(
00709   const DShape &d2psids, DenseMatrix<double> &jacobian2) const;
00710  
00711  /// \short Calculate the mapping from local to Lagrangian coordinates given 
00712  /// the derivatives of the shape functions w.r.t the local coorindates.
00713  /// assuming that the coordinates are aligned in the direction of the local 
00714  /// coordinates, i.e. there are no cross terms and the jacobian is diagonal.
00715  /// This function returns the determinant of the jacobian, the jacobian 
00716  /// and the inverse jacobian.
00717  double local_to_lagrangian_mapping_diagonal(
00718   const DShape &dpsids,DenseMatrix<double> &jacobian,
00719   DenseMatrix<double> &inverse_jacobian) const;
00720 
00721   public:
00722  
00723  /// Constructor
00724  RefineableSolidElement() : RefineableElement(), SolidFiniteElement(),
00725   Use_undeformed_macro_element_for_new_lagrangian_coords(false){}
00726 
00727  /// Virtual Destructor, delete any allocated storage
00728  virtual ~RefineableSolidElement() { }
00729  
00730  /// \short Overload the local equation numbers for Data stored as part
00731  /// of solid nodes to include hanging node data
00732  void assign_solid_local_eqn_numbers()
00733   {
00734    SolidFiniteElement::assign_solid_local_eqn_numbers();
00735    assign_solid_hanging_local_eqn_numbers();
00736   }
00737 
00738  ///\short The number of geometric data affecting a 
00739  /// RefineableSolidFiniteElemnet is not
00740  ///the same as the number of nodes (one variable position data per node)
00741  inline unsigned ngeom_data() const 
00742   {
00743    throw OomphLibError(
00744     "Geometric Data not yet set up for RefineableSolidNodes\n",
00745     "RefineableSolidElement::ngeom_data()",
00746     OOMPH_EXCEPTION_LOCATION);
00747    return nnode();
00748   }
00749 
00750  /// \short Return pointer to the j-th Data item that the object's 
00751  /// shape depends on. (Redirects to the nodes' positional Data).
00752  inline Data* geom_data_pt(const unsigned& j)
00753   {
00754    throw OomphLibError(
00755     "Geometric Data not yet set up for RefineableSolidNodes\n",
00756     "RefineableSolidElement::geom_data_pt()",
00757     OOMPH_EXCEPTION_LOCATION);
00758    return static_cast<SolidNode*>(node_pt(j))->variable_position_pt();
00759   }
00760  
00761 
00762  /// \short Compute element residual Vector and element Jacobian matrix
00763  /// corresponding to the solid positions. Overloaded version to take
00764  /// the hanging nodes into account
00765  void fill_in_jacobian_from_solid_position_by_fd(Vector<double> & residuals,
00766                                              DenseMatrix<double> &jacobian);
00767 
00768 
00769  /// \short Access fct to flag deciding if the Lagrangian coordinates of newly-created interior 
00770  /// SolidNodes are to be determined by the father element's undeformed 
00771  /// MacroElement representation (if it has one). Default: False as it means that, 
00772  /// following a refinement an element is no longer in equilbrium (as the Lagrangian
00773  /// coordinate is determined differently from the Eulerian one). However, basing
00774  /// the Lagrangian coordinates on the undeformed MacroElement can be
00775  /// more stable numerically and for steady problems it's not a big deal
00776  /// either way as the difference between the two formulations only matters
00777  /// at finite resolution so we have no right to say that one is "more correct"
00778  /// than the other...
00779  bool& use_undeformed_macro_element_for_new_lagrangian_coords()
00780   {return Use_undeformed_macro_element_for_new_lagrangian_coords;}
00781 
00782 
00783  /// \short Further build: Pass the father's 
00784  /// Use_undeformed_macro_element_for_new_lagrangian_coords
00785  /// flag down, then call the underlying RefineableElement's
00786  /// version.
00787  virtual void further_build()
00788   {
00789    Use_undeformed_macro_element_for_new_lagrangian_coords=
00790     dynamic_cast<RefineableSolidElement*>(father_element_pt())
00791     ->use_undeformed_macro_element_for_new_lagrangian_coords();
00792    
00793    RefineableElement::further_build();
00794   }
00795  
00796 };
00797 
00798 }
00799 
00800 #endif

Generated on Mon Aug 10 11:23:50 2009 by  doxygen 1.4.7