action functions
|
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 if you wish to be informed of the library's "official" release. |
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
1.4.7