elements.cc
Go to the documentation of this file.
1 //LIC// ====================================================================
2 //LIC// This file forms part of oomph-lib, the object-oriented,
3 //LIC// multi-physics finite-element library, available
4 //LIC// at http://www.oomph-lib.org.
5 //LIC//
6 //LIC// Version 1.0; svn revision $LastChangedRevision$
7 //LIC//
8 //LIC// $LastChangedDate$
9 //LIC//
10 //LIC// Copyright (C) 2006-2016 Matthias Heil and Andrew Hazel
11 //LIC//
12 //LIC// This library is free software; you can redistribute it and/or
13 //LIC// modify it under the terms of the GNU Lesser General Public
14 //LIC// License as published by the Free Software Foundation; either
15 //LIC// version 2.1 of the License, or (at your option) any later version.
16 //LIC//
17 //LIC// This library is distributed in the hope that it will be useful,
18 //LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
19 //LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
20 //LIC// Lesser General Public License for more details.
21 //LIC//
22 //LIC// You should have received a copy of the GNU Lesser General Public
23 //LIC// License along with this library; if not, write to the Free Software
24 //LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
25 //LIC// 02110-1301 USA.
26 //LIC//
27 //LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
28 //LIC//
29 //LIC//====================================================================
30 //Non-inline member functions for generic elements
31 
32 #include<float.h>
33 
34 //oomph-lib includes
35 #include "elements.h"
36 #include "timesteppers.h"
37 #include "integral.h"
38 #include "shape.h"
39 #include "oomph_definitions.h"
41 
42 namespace oomph
43 {
44 
45 /// Static boolean to suppress warnings about repeated internal
46 /// data. Defaults to false
48 
49 
50 /// Static boolean to suppress warnings about repeated external
51 /// data. Defaults to true
53 
54 
55 ///////////////////////////////////////////////////////////////////////////
56 ///////////////////////////////////////////////////////////////////////////
57 // Functions for generalised elements
58 ///////////////////////////////////////////////////////////////////////////
59 ///////////////////////////////////////////////////////////////////////////
60 
61 //=======================================================================
62 /// Add a (pointer to an) internal data object to the element and
63 /// return the index required to obtain it from the access
64 /// function \c internal_data_pt()
65 //=======================================================================
66  unsigned GeneralisedElement::add_internal_data(Data* const &data_pt,
67  const bool &fd)
68  {
69  //Local cache of numbers of internal and external data
70  const unsigned n_internal_data = Ninternal_data;
71  const unsigned n_external_data = Nexternal_data;
72 
73  //Find out whether the data is already stored in the array
74 
75  //Loop over the number of internals
76  //The internal data are stored at the beginning of the array
77  for(unsigned i=0;i<n_internal_data;i++)
78  {
79  //If the passed pointer is stored in the array
80  if(internal_data_pt(i) == data_pt)
81  {
82 #ifdef PARANOID
84  {
85  oomph_info << std::endl << std::endl;
86  oomph_info
87  << "-----------------------------------------------------------------"
88  << std::endl
89  << "Info: Data is already included in this element's internal storage."
90  << std::endl
91  << "It's stored as entry " << i << " and I'm not adding it again."
92  << std::endl<< std::endl
93  << "Note: You can suppress this message by recompiling without"
94  << "\n PARANOID or setting the boolean \n"
95  << "\n GeneralisedElement::Suppress_warning_about_repeated_internal_data"
96  << "\n\n to true."
97  << std::endl
98  << "-----------------------------------------------------------------"
99  << std::endl << std::endl;
100  }
101 #endif
102  //Return the index to the data object
103  return i;
104  }
105  }
106 
107  //Allocate new storage for the pointers to data
108  Data** new_data_pt = new Data*[n_internal_data + n_external_data + 1];
109 
110  //Copy the old internal values across to the beginning of the array
111  for(unsigned i=0;i<n_internal_data;i++) {new_data_pt[i] = Data_pt[i];}
112 
113  //Now add the new value to the end of the internal data
114  new_data_pt[n_internal_data] = data_pt;
115 
116  //Copy the external values across
117  for(unsigned i=0;i<n_external_data;i++)
118  {new_data_pt[n_internal_data + 1 + i] = Data_pt[n_internal_data + i];}
119 
120  //Delete the storage associated with the previous values
121  delete[] Data_pt;
122 
123  //Set the pointer to the new storage
124  Data_pt = new_data_pt;
125 
126  //Resize the array of boolean flags
127  Data_fd.resize(n_internal_data + n_external_data + 1);
128  //Shuffle the flags for the external data to the end of the array
129  for(unsigned i=n_external_data;i>0;i--)
130  {
131  Data_fd[n_internal_data + i] = Data_fd[n_internal_data + i-1];
132  }
133  //Now add the new flag to the end of the internal data
134  Data_fd[n_internal_data] = fd;
135 
136  //Increase the number of internals
137  ++Ninternal_data;
138 
139  //Return the final index to the new internal data
140  return n_internal_data;
141  }
142 
143 //=======================================================================
144 /// Add the contents of the queue global_eqn_numbers to
145 /// the local storage for the equation numbers, which represents the
146 /// local-to-global translation scheme. It is essential that the entries
147 /// are added in order, i.e. from the front.
148 //=======================================================================
150  std::deque<unsigned long> const &global_eqn_numbers,
151  std::deque<double*> const &global_dof_pt)
152  {
153  //Find the number of dofs
154  const unsigned n_dof = Ndof;
155  //Find the number of additional dofs
156  const unsigned n_additional_dof = global_eqn_numbers.size();
157  //If there are none, return immediately
158  if(n_additional_dof==0) {return;}
159 
160  //Find the new total number of equation numbers
161  const unsigned new_n_dof = n_dof + n_additional_dof;
162  //Create storage for all equations, initialised to NULL
163  unsigned long *new_eqn_number = new unsigned long[new_n_dof];
164 
165  //Copy over the existing values to the start new storage
166  for(unsigned i=0;i<n_dof;i++) {new_eqn_number[i] = Eqn_number[i];}
167 
168  //Set an index to the next position in the new storage
169  unsigned index = n_dof;
170  //Loop over the queue and add it's entries to our new storage
171  for(std::deque<unsigned long>::const_iterator it=global_eqn_numbers.begin();
172  it!=global_eqn_numbers.end();++it)
173  {
174  //Add the value to the storage
175  new_eqn_number[index] = *it;
176  //Increase the array index
177  ++index;
178  }
179 
180 
181  //If a non-empty dof deque has been passed then do stuff
182  const unsigned n_additional_dof_pt = global_dof_pt.size();
183  if(n_additional_dof_pt > 0)
184  {
185 //If it's size is not the same as the equation numbers complain
186 #ifdef PARANOID
187  if(n_additional_dof_pt != n_additional_dof)
188  {
189  std::ostringstream error_stream;
190  error_stream
191  <<
192  "global_dof_pt is non-empty, yet it does not have the same size\n"
193  <<
194  "as global_eqn_numbers.\n"
195  <<
196  "There are " << n_additional_dof << " equation numbers,\n"
197  <<
198  "but " << n_additional_dof_pt << std::endl;
199 
200  throw OomphLibError(error_stream.str(),
201  OOMPH_CURRENT_FUNCTION,
202  OOMPH_EXCEPTION_LOCATION);
203  }
204 #endif
205 
206  //Create storge for all dofs initialised to NULL
207  double **new_dof_pt = new double*[new_n_dof];
208  //Copy over the exisiting values to the start of new storage
209  for(unsigned i=0;i<n_dof;i++) {new_dof_pt[i] = Dof_pt[i];}
210 
211  //Set an index to the next position in the new storage
212  unsigned index = n_dof;
213  //Loop over the queue and add it's entries to our new storage
214  for(std::deque<double*>::const_iterator it=global_dof_pt.begin();
215  it!=global_dof_pt.end();++it)
216  {
217  //Add the value to the storage
218  new_dof_pt[index] = *it;
219  //Increase the array index
220  ++index;
221  }
222 
223  //Now delete the old storage
224  delete[] Dof_pt;
225  //Set the pointer to address the new storage
226  Dof_pt = new_dof_pt;
227  }
228 
229  //Now delete the old for the equation numbers storage
230  delete[] Eqn_number;
231  //Set the pointer to address the new storage
232  Eqn_number = new_eqn_number;
233  //Finally update the number of degrees of freedom
234  Ndof = new_n_dof;
235  }
236 
237 //========================================================================
238 /// Empty dense matrix used as a dummy argument to combined
239 /// residual and jacobian functions in the case when only the residuals
240 /// are being assembled
241 //========================================================================
243 
244 //========================================================================
245 /// Static storage used when pointers to the dofs are being assembled by
246 /// add_global_eqn_numbers()
247 //========================================================================
248 std::deque<double*> GeneralisedElement::Dof_pt_deque;
249 
250 
251 //=========================================================================
252 /// Default value used as the increment for finite difference calculations
253 /// of the jacobian matrices
254 //=========================================================================
256 
257 //==========================================================================
258 /// Destructor for generalised elements: Wipe internal data. Pointers
259 /// to external data get NULLed but are not deleted because they
260 /// are (generally) shared by lots of elements.
261 //==========================================================================
262  GeneralisedElement::~GeneralisedElement()
263  {
264  //Delete each of the objects stored as internal data
265  for(unsigned i=Ninternal_data;i>0;i--)
266  {
267  //The objects are stored at the beginning of the Data_pt array
268  delete Data_pt[i-1];
269  Data_pt[i-1] = 0;
270  }
271 
272  //Now delete the storage for internal and external data
273  delete[] Data_pt;
274 
275  //Now if we have allocated storage for the local equation for
276  //the internal and external data, delete it.
277  if(Data_local_eqn)
278  {
279  delete[] Data_local_eqn[0];
280  delete[] Data_local_eqn;
281  }
282 
283  //Delete the storage for the global equation numbers
284  delete[] Eqn_number;
285  }
286 
287 
288 //=======================================================================
289 /// Add a (pointer to an) external data object to the element and
290 /// return the index required to obtain it from the access
291 /// function \c external_data_pt()
292 //=======================================================================
293  unsigned GeneralisedElement::add_external_data(Data* const &data_pt,
294  const bool &fd)
295  {
296  //Find the numbers of internal and external data
297  const unsigned n_internal_data = Ninternal_data;
298  const unsigned n_external_data = Nexternal_data;
299  //Find out whether the data is already stored in the array
300 
301  //Loop over the number of externals
302  //The external data are stored at the end of the array Data_pt
303  for(unsigned i=0;i<n_external_data;i++)
304  {
305  //If the passed pointer is stored in the array
306  if(external_data_pt(i) == data_pt)
307  {
308 #ifdef PARANOID
310  {
311  oomph_info << std::endl << std::endl;
312  oomph_info
313  << "-----------------------------------------------------------------"
314  << std::endl
315  << "Info: Data is already included in this element's external storage."
316  << std::endl
317  << "It's stored as entry " << i << " and I'm not adding it again"
318  << std::endl << std::endl
319  << "Note: You can suppress this message by recompiling without"
320  << "\n PARANOID or setting the boolean \n"
321  << "\n GeneralisedElement::Suppress_warning_about_repeated_external_data"
322  << "\n\n to true."
323  << std::endl
324  << "-----------------------------------------------------------------"
325  << std::endl << std::endl;
326  }
327 #endif
328  //Return the index to the data object
329  return i;
330  }
331  }
332 
333  //Allocate new storage for the pointers to data
334  Data** new_data_pt = new Data*[n_internal_data + n_external_data + 1];
335 
336  //Copy the old internal and external values across to the new array
337  for(unsigned i=0;i<(n_internal_data + n_external_data);i++)
338  {new_data_pt[i] = Data_pt[i];}
339 
340  //Add the new data pointer to the end of the array
341  new_data_pt[n_internal_data + n_external_data] = data_pt;
342 
343  //Delete the storage associated with the previous values
344  delete[] Data_pt;
345 
346  //Set the pointer to the new storage
347  Data_pt = new_data_pt;
348 
349  //Resize the array of boolean flags
350  Data_fd.resize(n_internal_data + n_external_data + 1);
351  //Now add the new flag to the end of the external data
352  Data_fd[n_internal_data + n_external_data] = fd;
353 
354  //Increase the number of externals
355  ++Nexternal_data;
356 
357  //Return the final index to the new external data
358  return n_external_data;
359  }
360 
361 //========================================================================
362 /// Flush all the external data, i.e. clear the pointers to external
363 /// data from the internal storage.
364 //========================================================================
366  {
367  //Get the numbers of internal and external data
368  const unsigned n_external_data = Nexternal_data;
369  //If there is external data
370  if(n_external_data>0)
371  {
372  //Storage for new data, initialised to NULL
373  Data** new_data_pt=0;
374 
375  //Find the number of internal data
376  const unsigned n_internal_data = Ninternal_data;
377  //If there is internal data resize Data_pt and copy over
378  //the pointers
379  if(n_internal_data > 0)
380  {
381  //The new data pointer should only be the size of the internal data
382  new_data_pt = new Data*[n_internal_data];
383  //Copy over the internal data only
384  for(unsigned i=0;i<n_internal_data;i++) {new_data_pt[i] = Data_pt[i];}
385  }
386 
387  //Delete the old storage
388  delete[] Data_pt;
389  //Set the new storage, this will be NULL if there is no internal data
390  Data_pt = new_data_pt;
391  //Set the number of externals to zero
392  Nexternal_data = 0;
393 
394  //Resize the array of boolean flags to the number of internals
395  Data_fd.resize(n_internal_data);
396  }
397  }
398 
399 
400 
401 //=========================================================================
402 /// Remove the object addressed by data_pt from the external data array
403 /// Note that this could mess up the numbering of other external data
404 //========================================================================
406  {
407  //Get the current numbers of external data
408  const unsigned n_external_data = Nexternal_data;
409  //Index of the data to be removed
410  //We initialise this to be n_external_data, and it will be smaller than
411  //n_external_data if the data pointer is found in the array
412  unsigned index = n_external_data;
413  //Loop over the external data and find the argument
414  for(unsigned i=0;i<n_external_data;i++)
415  {
416  //If we have found the data pointer, set the index and break
417  if(external_data_pt(i) == data_pt)
418  {
419  index=i;
420  break;
421  }
422  }
423 
424  //If we have found an index less than Nexternal_data, then we have found
425  //the data in the array
426  if(index < n_external_data)
427  {
428  //Initialise a new array to NULL
429  Data** new_data_pt = 0;
430 
431  //Find the number of internals
432  const unsigned n_internal_data = Ninternal_data;
433 
434  //Find the new number of total data items (one fewer)
435  const unsigned n_total_data = n_internal_data + n_external_data - 1;
436 
437  //Create a new array containing the data items, if we have any
438  if(n_total_data > 0) {new_data_pt = new Data*[n_total_data];}
439 
440  //Copy over all the internal data
441  for(unsigned i=0;i<n_internal_data;i++) {new_data_pt[i] = Data_pt[i];}
442 
443  //Now copy over the un-flushed data
444  unsigned counter=0;
445  for(unsigned i=0;i<n_external_data;i++)
446  {
447  //If we are not at the deleted index
448  if(i!=index)
449  {
450  //Copy the undeleted entry into the next entry in our new
451  //array of pointers to Data
452  new_data_pt[n_internal_data + counter] = Data_pt[i];
453  //Increase the counter
454  ++counter;
455  }
456  }
457 
458  //Delete the storage associated with the previous values
459  delete[] Data_pt;
460 
461  //Set pointers to the new storage, will be NULL if no data left
462  Data_pt = new_data_pt;
463 
464  //Remove the entry from the array of boolean flags
465  Data_fd.erase(Data_fd.begin()+n_internal_data+index);
466 
467  //Decrease the number of externals
468  --Nexternal_data;
469 
470  //Issue a warning if there will be external data remaining
471  if(Nexternal_data > 1)
472  {
473  std::ostringstream warning_stream;
474  warning_stream << "Data removed from element's external data "
475  << std::endl
476  << "You may have to update the indices for remaining data "
477  << std::endl
478  << "This can be achieved by using add_external_data() "
479  << std::endl;
480  OomphLibWarning(warning_stream.str(),
481  "GeneralisedElement::flush_external_data()",
482  OOMPH_EXCEPTION_LOCATION);
483  }
484  }
485  }
486 
487 
488 
489 //==========================================================================
490 /// This function loops over the internal data of the element and assigns
491 /// GLOBAL equation numbers to the data objects.
492 ///
493 /// Pass:
494 /// - the current total number of dofs, global_number, which gets
495 /// incremented.
496 /// - Dof_pt, the Vector of pointers to the global dofs
497 /// (to which any internal dofs are added).
498 /// .
499 //==========================================================================
501  assign_internal_eqn_numbers(unsigned long &global_number,
503  {
504  //Loop over the internal data and assign the equation numbers
505  //The internal data are stored at the beginning of the Data_pt array
506  for(unsigned i=0;i<Ninternal_data;i++)
507  {internal_data_pt(i)->assign_eqn_numbers(global_number,Dof_pt);}
508  }
509 
510 
511 
512 //==========================================================================
513 /// \short Function to describe the dofs of the Element. The ostream
514 /// specifies the output stream to which the description
515 /// is written; the string stores the currently
516 /// assembled output that is ultimately written to the
517 /// output stream by Data::describe_dofs(...); it is typically
518 /// built up incrementally as we descend through the
519 /// call hierarchy of this function when called from
520 /// Problem::describe_dofs(...)
521 //==========================================================================
522  void GeneralisedElement::describe_dofs(std::ostream& out,
523  const std::string& current_string) const
524  {
525  for(unsigned i=0;i<Ninternal_data;i++)
526  {
527  std::stringstream conversion;
528  conversion <<" of Internal Data "<<i<<current_string;
529  std::string in(conversion.str());
530  internal_data_pt(i)->describe_dofs(out,in);
531  }
532  }
533 
534 //==========================================================================
535 /// \short Function to describe the local dofs of the element. The ostream
536 /// specifies the output stream to which the description
537 /// is written; the string stores the currently
538 /// assembled output that is ultimately written to the
539 /// output stream by Data::describe_dofs(...); it is typically
540 /// built up incrementally as we descend through the
541 /// call hierarchy of this function when called from
542 /// Problem::describe_dofs(...)
543 //==========================================================================
545 describe_local_dofs(std::ostream& out,const std::string& current_string) const
546  {
547  // Find the number of internal and external data
548  const unsigned n_internal_data = Ninternal_data;
549  const unsigned n_external_data = Nexternal_data;
550 
551  // Now loop over the internal data and describe local equation numbers
552  for(unsigned i=0;i<n_internal_data;i++)
553  {
554  // Pointer to the internal data
555  Data* const data_pt = internal_data_pt(i);
556 
557  std::stringstream conversion;
558  conversion <<" of Internal Data "<<i<<current_string;
559  std::string in(conversion.str());
560  data_pt->describe_dofs(out,in);
561  }
562 
563 
564  // Now loop over the external data and assign local equation numbers
565  for(unsigned i=0;i<n_external_data;i++)
566  {
567  // Pointer to the external data
568  Data* const data_pt = external_data_pt(i);
569 
570  std::stringstream conversion;
571  conversion<<" of External Data "<<i<<current_string;
572  std::string in(conversion.str());
573  data_pt->describe_dofs(out,in);
574  }
575 
576  }
577 
578  //==========================================================================
579  /// This function loops over the internal data of the element and add
580  /// pointers to their unconstrained values to a map indexed by the global
581  /// equation number.
582  //==========================================================================
584  std::map<unsigned,double*> &map_of_value_pt)
585  {
586  //Loop over the internal data and add their data to the map
587  //The internal data are stored at the beginning of the Data_pt array
588  for(unsigned i=0;i<Ninternal_data;i++)
589  {internal_data_pt(i)->add_value_pt_to_map(map_of_value_pt);}
590  }
591 
592 
593 
594 
595 #ifdef OOMPH_HAS_MPI
596  //=========================================================================
597  /// Add all internal data and time history values to the vector in
598  /// the internal storage order
599  //=========================================================================
601  Vector<double> &vector_of_values)
602  {
603  for(unsigned i=0;i<Ninternal_data;i++)
604  {internal_data_pt(i)->add_values_to_vector(vector_of_values);}
605  }
606 
607  //========================================================================
608  /// Read all internal data and time history values from the vector
609  /// starting from index. On return the index will be
610  /// set to the value at the end of the data that has been read in
611  //========================================================================
613  const Vector<double> &vector_of_values,
614  unsigned &index)
615  {
616  for(unsigned i=0;i<Ninternal_data;i++)
617  {internal_data_pt(i)->read_values_from_vector(vector_of_values,index);}
618  }
619 
620  //=========================================================================
621  /// Add all equation numbers associated with internal data
622  /// to the vector in the internal storage order
623  //=========================================================================
625  Vector<long> &vector_of_eqn_numbers)
626  {
627  for(unsigned i=0;i<Ninternal_data;i++)
628  {internal_data_pt(i)->add_eqn_numbers_to_vector(vector_of_eqn_numbers);}
629  }
630 
631  //=========================================================================
632  /// Read all equation numbers associated with internal data
633  /// from the vector
634  /// starting from index. On return the index will be
635  /// set to the value at the end of the data that has been read in
636  //=========================================================================
638  const Vector<long> & vector_of_eqn_numbers, unsigned &index)
639  {
640  for(unsigned i=0;i<Ninternal_data;i++)
641  {internal_data_pt(i)->read_eqn_numbers_from_vector(vector_of_eqn_numbers,
642  index);}
643  }
644 
645 #endif
646 
647 
648  //====================================================================
649  /// Setup the arrays of local equation numbers for the element.
650  /// In general, this function should not need to be overloaded. Instead
651  /// the function assign_all_generic_local_eqn_numbers() will be overloaded
652  /// by different types of Element.
653  /// That said, the function is virtual so that it
654  /// may be overloaded by the user if they *really* know what they are doing.
655  //==========================================================================
657  const bool &store_local_dof_pt)
658  {
660  assign_all_generic_local_eqn_numbers(store_local_dof_pt);
662 
663  //Check that no global equation numbers are repeated
664 #ifdef PARANOID
665 
666  std::ostringstream error_stream;
667 
668  //Loop over the array of equation numbers and add to set to assess
669  //uniqueness
670  std::map<unsigned,bool> is_repeated;
671  std::set<unsigned long> set_of_global_eqn_numbers;
672  unsigned old_ndof=0;
673  for(unsigned n=0;n<Ndof;++n)
674  {
675  set_of_global_eqn_numbers.insert(Eqn_number[n]);
676  if (set_of_global_eqn_numbers.size()==old_ndof)
677  {
678  error_stream << "Repeated global eqn: " << Eqn_number[n] << std::endl;
679  is_repeated[Eqn_number[n]]=true;
680  }
681  old_ndof=set_of_global_eqn_numbers.size();
682  }
683 
684  //If the sizes do not match we have a repeat, throw an error
685  if(set_of_global_eqn_numbers.size() != Ndof)
686  {
687 #ifdef OOMPH_HAS_MPI
688  error_stream << "Element is ";
689  if (!is_halo()) error_stream << "not ";
690  error_stream << "a halo element\n\n";
691 #endif
692  error_stream << "\nLocal/lobal equation numbers: " << std::endl;
693  for(unsigned n=0;n<Ndof;++n)
694  {
695  error_stream << " " << n << " " << Eqn_number[n] << std::endl;
696  }
697 
698  // It's helpful for debugging purposes to output more about this element
699  error_stream << std::endl << " element address is " << this << std::endl;
700 
701  // Check if the repeated dofs are among the internal Data values
702  unsigned nint=this->ninternal_data();
703  error_stream << "Number of internal data " << nint << std::endl;
704  for (unsigned i=0;i<nint;i++)
705  {
706  Data* data_pt=this->internal_data_pt(i);
707  unsigned nval=data_pt->nvalue();
708  for (unsigned j=0;j<nval;j++)
709  {
710  int eqn_no=data_pt->eqn_number(j);
711  error_stream << "Internal dof: " << eqn_no << std::endl;
712  if (is_repeated[unsigned(eqn_no)])
713  {
714  error_stream << "Repeated internal dof: " << eqn_no << std::endl;
715  }
716  }
717  }
718 
719  // Check if the repeated dofs are among the external Data values
720  unsigned next=this->nexternal_data();
721  error_stream << "Number of external data " << next << std::endl;
722  for (unsigned i=0;i<next;i++)
723  {
724  Data* data_pt=this->external_data_pt(i);
725  unsigned nval=data_pt->nvalue();
726  for (unsigned j=0;j<nval;j++)
727  {
728  int eqn_no=data_pt->eqn_number(j);
729  error_stream << "External dof: " << eqn_no << std::endl;
730  if (is_repeated[unsigned(eqn_no)])
731  {
732  error_stream << "Repeated external dof: " << eqn_no;
733  Node* nod_pt=dynamic_cast<Node*>(data_pt);
734  if (nod_pt!=0)
735  {
736  error_stream << " (is a node at: ";
737  unsigned ndim=nod_pt->ndim();
738  for (unsigned ii=0;ii<ndim;ii++)
739  {
740  error_stream << nod_pt->x(ii) << " ";
741  }
742  }
743  error_stream << ")\n";
744  }
745  }
746  }
747 
748 
749  // If it's an element with external element check the associated
750  // Data
752  dynamic_cast<ElementWithExternalElement*>(this);
753  if (e_el_pt!=0)
754  {
755  // Check if the repeated dofs are among the external Data values
756  {
757  unsigned next=e_el_pt->nexternal_interaction_field_data();
758  error_stream << "Number of external element data " << next << std::endl;
760  for (unsigned i=0;i<next;i++)
761  {
762  unsigned nval=data_pt[i]->nvalue();
763  for (unsigned j=0;j<nval;j++)
764  {
765  int eqn_no=data_pt[i]->eqn_number(j);
766  error_stream << "External element dof: " << eqn_no << std::endl;
767  if (is_repeated[unsigned(eqn_no)])
768  {
769  error_stream << "Repeated external element dof: "
770  << eqn_no;
771  Node* nod_pt=dynamic_cast<Node*>(data_pt[i]);
772  if (nod_pt!=0)
773  {
774  error_stream << " (is a node at: ";
775  unsigned ndim=nod_pt->ndim();
776  for (unsigned ii=0;ii<ndim;ii++)
777  {
778  error_stream << nod_pt->x(ii) << " ";
779  }
780  }
781  error_stream << ")\n";
782  }
783  }
784  }
785  }
786 
787 
788  // Check if the repeated dofs are among the external geom Data values
789  {
790  unsigned next=e_el_pt->nexternal_interaction_geometric_data();
791  error_stream << "Number of external element geom data "
792  << next << std::endl;
793  Vector<Data*> data_pt(e_el_pt->
794  external_interaction_geometric_data_pt());
795  for (unsigned i=0;i<next;i++)
796  {
797  unsigned nval=data_pt[i]->nvalue();
798  for (unsigned j=0;j<nval;j++)
799  {
800  int eqn_no=data_pt[i]->eqn_number(j);
801  error_stream << "External element geometric dof: "
802  << eqn_no << std::endl;
803  if (is_repeated[unsigned(eqn_no)])
804  {
805  error_stream << "Repeated external element geometric dof: "
806  << eqn_no << " "
807  << data_pt[i]->value(j);
808  Node* nod_pt=dynamic_cast<Node*>(data_pt[i]);
809  if (nod_pt!=0)
810  {
811  error_stream << " (is a node at: ";
812  unsigned ndim=nod_pt->ndim();
813  for (unsigned ii=0;ii<ndim;ii++)
814  {
815  error_stream << nod_pt->x(i) << " ";
816  }
817  error_stream << ")";
818  }
819  error_stream << "\n";
820  }
821  }
822  }
823  }
824 
825  }
826 
827  // If it's a FiniteElement then output its nodes
828  FiniteElement* f_el_pt=dynamic_cast<FiniteElement*>(this);
829  if (f_el_pt!=0)
830  {
831  unsigned n_node=f_el_pt->nnode();
832  for (unsigned n=0;n<n_node;n++)
833  {
834  Node* nod_pt=f_el_pt->node_pt(n);
835  unsigned nval=nod_pt->nvalue();
836  for (unsigned j=0;j<nval;j++)
837  {
838  int eqn_no=nod_pt->eqn_number(j);
839  error_stream << "Node " << n
840  << ": Nodal dof: "
841  << eqn_no;
842  if (eqn_no>=0)
843  {
844  if (is_repeated[unsigned(eqn_no)])
845  {
846  error_stream << "Node " << n
847  << ": Repeated nodal dof: "
848  << eqn_no;
849  if (j>=f_el_pt->required_nvalue(n))
850  {
851  error_stream << " (resized)";
852  }
853  error_stream << std::endl;
854  }
855  }
856  }
857  SolidNode* solid_nod_pt=dynamic_cast<SolidNode*>(nod_pt);
858  if (solid_nod_pt!=0)
859  {
860  Data* data_pt=solid_nod_pt->variable_position_pt();
861  unsigned nval=data_pt->nvalue();
862  for (unsigned j=0;j<nval;j++)
863  {
864  int eqn_no=data_pt->eqn_number(j);
865  error_stream << "Node " << n << ": Positional dof: "
866  << eqn_no << std::endl;
867  if (is_repeated[unsigned(eqn_no)])
868  {
869  error_stream << "Repeated positional dof: "
870  << eqn_no << " " << data_pt->value(j) << std::endl;
871  }
872  }
873  }
874  }
875 
876  // Output nodal coordinates of element
877  n_node=f_el_pt->nnode();
878  for (unsigned n=0;n<n_node;n++)
879  {
880  Node* nod_pt=f_el_pt->node_pt(n);
881  unsigned n_dim=nod_pt->ndim();
882  error_stream << "Node " << n << " at ( ";
883  for (unsigned i=0;i<n_dim;i++)
884  {
885  error_stream << nod_pt->x(i) << " ";
886  }
887  error_stream << ")" << std::endl;
888  }
889 
890  }
891 
892 
893  throw OomphLibError(error_stream.str(),
894  OOMPH_CURRENT_FUNCTION,
895  OOMPH_EXCEPTION_LOCATION);
896  }
897 #endif
898  }
899 
900 
901 //==========================================================================
902 /// This function loops over the internal and external data of the element,
903 /// adds the GLOBAL equation numbers to the local-to-global look-up scheme and
904 /// fills in the look-up schemes for the local equation
905 /// numbers.
906 /// If the boolean argument is true then pointers to the dofs will be
907 /// stored in Dof_pt
908 //==========================================================================
910  const bool &store_local_dof_pt)
911  {
912  //Find the number of internal and external data
913  const unsigned n_internal_data = Ninternal_data;
914  const unsigned n_external_data = Nexternal_data;
915  //Find the total number of data
916  const unsigned n_total_data = n_internal_data + n_external_data;
917 
918  //If there is data
919  if(n_total_data > 0)
920  {
921  //Find the number of local equations assigned so far
922  unsigned local_eqn_number = ndof();
923 
924  //We need to find the total number of values stored in all the
925  //internal and external data
926  //Initialise to the number stored in the first data object
927  unsigned n_total_values = Data_pt[0]->nvalue();
928  //Loop over the other data and add the number of values stored
929  for(unsigned i=1;i<n_total_data;++i)
930  {n_total_values += Data_pt[i]->nvalue();}
931 
932  //If allocated delete the old storage
933  if(Data_local_eqn)
934  {
935  delete[] Data_local_eqn[0];
936  delete[] Data_local_eqn;
937  }
938 
939  //If there are no values then we are done, null out the storage and
940  //return
941  if(n_total_values==0) {Data_local_eqn=0; return;}
942 
943  //Allocate the storage for the local equation numbers
944  //The idea is that we store internal equation numbers followed by
945  //external equation numbers
946 
947  //Firstly allocate pointers to the rows for each data item
948  Data_local_eqn = new int*[n_total_data];
949  //Now allocate storage for all the equation numbers
950  Data_local_eqn[0] = new int[n_total_values];
951  //Set all values to be unclassified
952  for(unsigned i=0;i<n_total_values;++i)
954 
955  //Loop over the remaining rows and set their pointers
956  for(unsigned i=1;i<n_total_data;++i)
957  {
958  //Initially set the pointer to the i-th row to the pointer
959  //to the i-1th row
961  //Now increase the row pointer by the number of values
962  //stored at the i-1th data object
963  Data_local_eqn[i] += Data_pt[i-1]->nvalue();
964  }
965 
966  //A local queue to store the global equation numbers
967  std::deque<unsigned long> global_eqn_number_queue;
968 
969  //Now loop over the internal data and assign local equation numbers
970  for(unsigned i=0;i<n_internal_data;i++)
971  {
972  //Pointer to the internal data
973  Data* const data_pt = internal_data_pt(i);
974  //Find the number of values stored at the internal data
975  unsigned n_value = data_pt->nvalue();
976 
977  //Loop over the number of values
978  for(unsigned j=0;j<n_value;j++)
979  {
980  //Get the GLOBAL equation number
981  long eqn_number = data_pt->eqn_number(j);
982  //If the GLOBAL equation number is positive (a free variable)
983  if(eqn_number >= 0)
984  {
985  //Add the GLOBAL equation number to the queue
986  global_eqn_number_queue.push_back(eqn_number);
987  //Add pointer to the dof to the queue if required
988  if(store_local_dof_pt)
989  {
990  GeneralisedElement::Dof_pt_deque.push_back(data_pt->value_pt(j));
991  }
992  //Add the local equation number to the storage scheme
994  //Increase the local number
995  local_eqn_number++;
996  }
997  else
998  {
999  //Set the local scheme to be pinned
1001  }
1002  }
1003  } //End of loop over internal data
1004 
1005 
1006  //Now loop over the external data and assign local equation numbers
1007  for(unsigned i=0;i<n_external_data;i++)
1008  {
1009  //Pointer to the external data
1010  Data* const data_pt = external_data_pt(i);
1011  //Find the number of values stored at the external data
1012  unsigned n_value = data_pt->nvalue();
1013 
1014  //Loop over the number of values
1015  for(unsigned j=0;j<n_value;j++)
1016  {
1017  //Get the GLOBAL equation number
1018  long eqn_number = data_pt->eqn_number(j);
1019  //If the GLOBAL equation number is positive (a free variable)
1020  if(eqn_number >= 0)
1021  {
1022  //Add the GLOBAL equation number to the queue
1023  global_eqn_number_queue.push_back(eqn_number);
1024  //Add pointer to the dof to the queue if required
1025  if(store_local_dof_pt)
1026  {
1027  GeneralisedElement::Dof_pt_deque.push_back(data_pt->value_pt(j));
1028  }
1029  //Add the local equation number to the local scheme
1030  Data_local_eqn[n_internal_data + i][j] = local_eqn_number;
1031  //Increase the local number
1032  local_eqn_number++;
1033  }
1034  else
1035  {
1036  //Set the local scheme to be pinned
1037  Data_local_eqn[n_internal_data + i][j] = Data::Is_pinned;
1038  }
1039  }
1040  }
1041 
1042  //Now add our global equations numbers to the internal element storage
1043  add_global_eqn_numbers(global_eqn_number_queue,
1044  GeneralisedElement::Dof_pt_deque);
1045  //Clear the memory used in the deque
1046  if(store_local_dof_pt)
1047  {std::deque<double*>().swap(GeneralisedElement::Dof_pt_deque);}
1048  }
1049  }
1050 
1051 
1052 //============================================================================
1053 /// This function calculates the entries of Jacobian matrix, used in
1054 /// the Newton method, associated with the internal degrees of freedom.
1055 /// It does this using finite differences,
1056 /// rather than an analytical formulation, so can be done in total generality.
1057 /// If the boolean argument is true, the finite
1058 /// differencing will be performed for all internal data, irrespective of
1059 /// the information in Data_fd. The default value (false)
1060 /// uses the information in Data_fd to selectively difference only
1061 /// certain data.
1062 //==========================================================================
1065  DenseMatrix<double> &jacobian,
1066  const bool &fd_all_data)
1067  {
1068  //Locally cache the number of internal data
1069  const unsigned n_internal_data = Ninternal_data;
1070 
1071  //If there aren't any internal data, then return straight away
1072  if(n_internal_data == 0) {return;}
1073 
1074  //Call the update function to ensure that the element is in
1075  //a consistent state before finite differencing starts
1077 
1078  //Find the number of dofs in the element
1079  const unsigned n_dof = ndof();
1080 
1081  //Create newres vector
1082  Vector<double> newres(n_dof);
1083 
1084  //Integer storage for local unknown
1085  int local_unknown=0;
1086 
1087  //Use the default finite difference step
1088  const double fd_step = Default_fd_jacobian_step;
1089 
1090  //Loop over the internal data
1091  for(unsigned i=0;i<n_internal_data;i++)
1092  {
1093  //If we are doing all finite differences or
1094  //the variable is included in the finite difference loop, do it
1095  if(fd_all_data || internal_data_fd(i))
1096  {
1097  //Get the number of value at the internal data
1098  const unsigned n_value = internal_data_pt(i)->nvalue();
1099 
1100  //Loop over the number of values
1101  for(unsigned j=0;j<n_value;j++)
1102  {
1103  //Get the local equation number
1104  local_unknown = internal_local_eqn(i,j);
1105  //If it's not pinned
1106  if(local_unknown >= 0)
1107  {
1108  //Get a pointer to the value of the internal data
1109  double* const value_pt = internal_data_pt(i)->value_pt(j);
1110 
1111  //Save the old value of the Internal data
1112  const double old_var = *value_pt;
1113 
1114  //Increment the value of the Internal data
1115  *value_pt += fd_step;
1116 
1117  //Now update any slaved variables
1119 
1120  //Calculate the new residuals
1121  get_residuals(newres);
1122 
1123  //Do finite differences
1124  for(unsigned m=0;m<n_dof;m++)
1125  {
1126  double sum = (newres[m] - residuals[m])/fd_step;
1127  //Stick the entry into the Jacobian matrix
1128  jacobian(m,local_unknown) = sum;
1129  }
1130 
1131  //Reset the Internal data
1132  *value_pt = old_var;
1133 
1134  //Reset any slaved variables
1136  }
1137  }
1138  } //End of finite-differencing for datum (if block)
1139  }
1140 
1141  //End of finite difference loop
1142  //Final reset of any slaved data
1144  }
1145 
1146 //============================================================================
1147 /// This function calculates the entries of Jacobian matrix, used in
1148 /// the Newton method, associated with the external degrees of freedom.
1149 /// It does this using finite differences,
1150 /// rather than an analytical formulation, so can be done in total generality.
1151 /// If the boolean argument is true, the finite
1152 /// differencing will be performed for all internal data, irrespective of
1153 /// the information in Data_fd. The default value (false)
1154 /// uses the information in Data_fd to selectively difference only
1155 /// certain data.
1156 //==========================================================================
1159  DenseMatrix<double> &jacobian,
1160  const bool &fd_all_data)
1161  {
1162  //Locally cache the number of external data
1163  const unsigned n_external_data = Nexternal_data;
1164  //If there aren't any external data, then return straight away
1165  if(n_external_data == 0) {return;}
1166 
1167  //Call the update function to ensure that the element is in
1168  //a consistent state before finite differencing starts
1170 
1171  //Find the number of dofs in the element
1172  const unsigned n_dof = ndof();
1173 
1174  //Create newres vector
1175  Vector<double> newres(n_dof);
1176 
1177  //Integer storage for local unknown
1178  int local_unknown=0;
1179 
1180  //Use the default finite difference step
1181  const double fd_step = Default_fd_jacobian_step;
1182 
1183  //Loop over the external data
1184  for(unsigned i=0;i<n_external_data;i++)
1185  {
1186  //If we are doing all finite differences or
1187  //the variable is included in the finite difference loop, do it
1188  if(fd_all_data || external_data_fd(i))
1189  {
1190  //Get the number of value at the external data
1191  const unsigned n_value = external_data_pt(i)->nvalue();
1192 
1193  //Loop over the number of values
1194  for(unsigned j=0;j<n_value;j++)
1195  {
1196  //Get the local equation number
1197  local_unknown = external_local_eqn(i,j);
1198  //If it's not pinned
1199  if(local_unknown >= 0)
1200  {
1201  //Get a pointer to the External data value
1202  double* const value_pt = external_data_pt(i)->value_pt(j);
1203 
1204  //Save the old value of the External data
1205  const double old_var = *value_pt;
1206 
1207  //Increment the value of the External data
1208  *value_pt += fd_step;
1209 
1210  //Now update any slaved variables
1212 
1213  //Calculate the new residuals
1214  get_residuals(newres);
1215 
1216  //Do finite differences
1217  for(unsigned m=0;m<n_dof;m++)
1218  {
1219  double sum = (newres[m] - residuals[m])/fd_step;
1220  //Stick the entry into the Jacobian matrix
1221  jacobian(m,local_unknown) = sum;
1222  }
1223 
1224  //Reset the External data
1225  *value_pt = old_var;
1226 
1227  //Reset any slaved variables
1229  }
1230  }
1231  } //End of finite differencing for datum (if block)
1232  }
1233 
1234  //End of finite difference loop
1235  //Final reset of any slaved data
1237  }
1238 
1239  //=====================================================================
1240  /// \short Add the elemental contribution to the mass matrix
1241  /// and the residuals vector. Note that
1242  /// this function will NOT initialise the residuals vector or the mass
1243  /// matrix. It must be called after the residuals vector and
1244  /// jacobian matrix have been initialised to zero. The default
1245  /// is deliberately broken.
1246  //=====================================================================
1249  DenseMatrix<double> &mass_matrix)
1250  {
1251  std::string error_message =
1252  "Empty fill_in_contribution_to_mass_matrix() has been called.\n";
1253  error_message +=
1254  "This function is called from the default implementation of\n";
1255  error_message += "get_mass_matrix();\n";
1256  error_message +=
1257  "and must calculate the residuals vector and mass matrix ";
1258  error_message += "without initialising any of their entries.\n\n";
1259 
1260  error_message +=
1261  "If you do not wish to use these defaults, you must overload\n";
1262  error_message +=
1263  "get_mass_matrix(), which must initialise the entries\n";
1264  error_message +=
1265  "of the residuals vector and mass matrix to zero.\n";
1266 
1267  throw
1268  OomphLibError(error_message,
1269  "GeneralisedElement::fill_in_contribution_to_mass_matrix()",
1270  OOMPH_EXCEPTION_LOCATION);
1271  }
1272 
1273  //=====================================================================
1274  /// \short Add the elemental contribution to the jacobian matrix,
1275  /// mass matrix and the residuals vector. Note that
1276  /// this function should NOT initialise any entries.
1277  /// It must be called after the residuals vector and
1278  /// matrices have been initialised to zero. The default is deliberately
1279  /// broken.
1280  //=====================================================================
1282  Vector<double> &residuals,
1283  DenseMatrix<double> &jacobian, DenseMatrix<double> &mass_matrix)
1284  {
1285  std::string error_message =
1286  "Empty fill_in_contribution_to_jacobian_and_mass_matrix() has been ";
1287  error_message += "called.\n";
1288  error_message +=
1289  "This function is called from the default implementation of\n";
1290  error_message += "get_jacobian_and_mass_matrix();\n";
1291  error_message +=
1292  "and must calculate the residuals vector and mass and jacobian matrices ";
1293  error_message += "without initialising any of their entries.\n\n";
1294 
1295  error_message +=
1296  "If you do not wish to use these defaults, you must overload\n";
1297  error_message +=
1298  "get_jacobian_and_mass_matrix(), which must initialise the entries\n";
1299  error_message +=
1300  "of the residuals vector, jacobian and mass matrix to zero.\n";
1301 
1302  throw
1303  OomphLibError(
1304  error_message,
1305  "GeneralisedElement::fill_in_contribution_to_jacobian_and_mass_matrix()",
1306  OOMPH_EXCEPTION_LOCATION);
1307  }
1308 
1309 
1310  //=====================================================================
1311  /// Add the elemental contribution to the derivatives of
1312  /// the residuals with respect to a parameter. This function should
1313  /// NOT initialise any entries and must be called after the entries
1314  /// have been initialised to zero
1315  /// The default implementation is deliberately broken
1316  //========================================================================
1318  double* const &parameter_pt, Vector<double> &dres_dparam)
1319  {
1320  std::string error_message =
1321  "Empty fill_in_contribution_to_dresiduals_dparameter() has been ";
1322  error_message += "called.\n";
1323  error_message +=
1324  "This function is called from the default implementation of\n";
1325  error_message += "get_dresiduals_dparameter();\n";
1326  error_message +=
1327  "and must calculate the derivatives of the residuals vector with respect\n";
1328  error_message += "to the passed parameter ";
1329  error_message += "without initialising any values.\n\n";
1330 
1331  error_message +=
1332  "If you do not wish to use these defaults, you must overload\n";
1333  error_message +=
1334  "get_dresiduals_dparameter(), which must initialise the entries\n";
1335  error_message +=
1336  "of the returned vector to zero.\n";
1337 
1338  error_message +=
1339  "This function is intended for use instead of the default (global) \n";
1340  error_message +=
1341  "finite-difference routine when analytic expressions are to be used\n";
1342  error_message += "in continuation and bifurcation tracking problems.\n\n";
1343  error_message += "This function is only called when the function\n";
1344  error_message +=
1345  "Problem::set_analytic_dparameter() has been called in the driver code\n";
1346 
1347  throw
1348  OomphLibError(
1349  error_message,
1350  "GeneralisedElement::fill_in_contribution_to_dresiduals_dparameter()",
1351  OOMPH_EXCEPTION_LOCATION);
1352  }
1353 
1354  //======================================================================
1355  /// Add the elemental contribution to the derivatives of
1356  /// the elemental Jacobian matrix
1357  /// and residuals with respect to a parameter. This function should
1358  /// NOT initialise any entries and must be called after the entries
1359  /// have been initialised to zero
1360  /// The default implementation is to use finite differences to calculate
1361  /// the derivatives.
1362  //========================================================================
1364  double* const &parameter_pt,
1365  Vector<double> &dres_dparam,
1366  DenseMatrix<double> &djac_dparam)
1367  {
1368  std::string error_message =
1369  "Empty fill_in_contribution_to_djacobian_dparameter() has been ";
1370  error_message += "called.\n";
1371  error_message +=
1372  "This function is called from the default implementation of\n";
1373  error_message += "get_djacobian_dparameter();\n";
1374  error_message +=
1375  "and must calculate the derivatives of residuals vector and jacobian ";
1376  error_message += "matrix\n";
1377  error_message += "with respect to the passed parameter ";
1378  error_message += "without initialising any values.\n\n";
1379 
1380  error_message +=
1381  "If you do not wish to use these defaults, you must overload\n";
1382  error_message +=
1383  "get_djacobian_dparameter(), which must initialise the entries\n";
1384  error_message +=
1385  "of the returned vector and matrix to zero.\n\n";
1386 
1387  error_message +=
1388  "This function is intended for use instead of the default (global) \n";
1389  error_message +=
1390  "finite-difference routine when analytic expressions are to be used\n";
1391  error_message += "in continuation and bifurcation tracking problems.\n\n";
1392  error_message += "This function is only called when the function\n";
1393  error_message +=
1394  "Problem::set_analytic_dparameter() has been called in the driver code\n";
1395 
1396 
1397  throw
1398  OomphLibError(
1399  error_message,
1400  "GeneralisedElement::fill_in_contribution_to_djacobian_dparameter()",
1401  OOMPH_EXCEPTION_LOCATION);
1402  }
1403 
1404 
1405  //=====================================================================
1406  /// \short Add the elemental contribution to the derivative of the
1407  /// jacobian matrix,
1408  /// mass matrix and the residuals vector with respect to a parameter.
1409  /// Note that
1410  /// this function should NOT initialise any entries.
1411  /// It must be called after the residuals vector and
1412  /// matrices have been initialised to zero. The default is deliberately
1413  /// broken.
1414  //=====================================================================
1417  double* const &parameter_pt,
1418  Vector<double> &dres_dparam,
1419  DenseMatrix<double> &djac_dparam,
1420  DenseMatrix<double> &dmass_matrix_dparam)
1421  {
1422  std::string error_message =
1423  "Empty fill_in_contribution_to_djacobian_and_dmass_matrix_dparameter() has";
1424  error_message += " been called.\n";
1425  error_message +=
1426  "This function is called from the default implementation of\n";
1427  error_message += "get_djacobian_and_dmass_matrix_dparameter();\n";
1428  error_message +=
1429  "and must calculate the residuals vector and mass and jacobian matrices ";
1430  error_message += "without initialising any of their entries.\n\n";
1431 
1432  error_message +=
1433  "If you do not wish to use these defaults, you must overload\n";
1434  error_message +=
1435  "get_djacobian_and_dmass_matrix_dparameter(), which must initialise the\n";
1436  error_message +=
1437  "entries of the returned vector and matrices to zero.\n";
1438 
1439 
1440  error_message +=
1441  "This function is intended for use instead of the default (global) \n";
1442  error_message +=
1443  "finite-difference routine when analytic expressions are to be used\n";
1444  error_message += "in continuation and bifurcation tracking problems.\n\n";
1445  error_message += "This function is only called when the function\n";
1446  error_message +=
1447  "Problem::set_analytic_dparameter() has been called in the driver code\n";
1448 
1449 
1450 
1451  throw
1452  OomphLibError(
1453  error_message,
1454  "GeneralisedElement::fill_in_contribution_to_djacobian_and_dmass_matrix_dparameter()",
1455  OOMPH_EXCEPTION_LOCATION);
1456  }
1457 
1458 
1459 
1460  //========================================================================
1461  /// Fill in contribution to the product of the Hessian
1462  /// (derivative of Jacobian with
1463  /// respect to all variables) an eigenvector, Y, and
1464  /// other specified vectors, C
1465  /// (d(J_{ij})/d u_{k}) Y_{j} C_{k}
1466  /// At the moment the dof pointer is passed in to enable
1467  /// easy calculation of finite difference default
1468 //==========================================================================
1470  Vector<double> const &Y,
1471  DenseMatrix<double> const &C,
1472  DenseMatrix<double> &product)
1473  {
1474  std::string error_message =
1475  "Empty fill_in_contribution_to_hessian_vector_products() has been ";
1476  error_message += "called.\n";
1477  error_message +=
1478  "This function is called from the default implementation of\n";
1479  error_message += "get_hessian_vector_products(); ";
1480  error_message += " and must calculate the products \n";
1481  error_message += "of the hessian matrix with the (global) ";
1482  error_message += "vectors Y and C\n";
1483  error_message += "without initialising any values.\n\n";
1484 
1485  error_message +=
1486  "If you do not wish to use these defaults, you must overload\n";
1487  error_message +=
1488  "get_hessian_vector_products(), which must initialise the entries\n";
1489  error_message +=
1490  "of the returned vector to zero.\n\n";
1491 
1492  error_message +=
1493  "This function is intended for use instead of the default (global) \n";
1494  error_message +=
1495  "finite-difference routine when analytic expressions are to be used.\n";
1496  error_message += "This function is only called when the function\n";
1497  error_message +=
1498  "Problem::set_analytic_hessian_products() has been called in the driver code\n";
1499 
1500  throw
1501  OomphLibError(
1502  error_message,
1503  "GeneralisedElement::fill_in_contribution_to_hessian_vector_product()",
1504  OOMPH_EXCEPTION_LOCATION);
1505  }
1506 
1507  //========================================================================
1508  /// Fill in the contribution to the inner products between given
1509  /// pairs of history values
1510  //==========================================================================
1512  Vector<std::pair<unsigned,unsigned> > const &history_index,
1513  Vector<double> &inner_product)
1514  {
1515  std::string error_message =
1516  "Empty fill_in_contribution_to_inner_products() has been called.\n";
1517  error_message +=
1518  "This function is called from the default implementation of\n";
1519  error_message += "get_inner_products();\n ";
1520  error_message += "It must calculate the inner products over the element\n";
1521  error_message += "of given pairs of history values\n";
1522  error_message += "without initialision of the output vector.\n\n";
1523 
1524  error_message +=
1525  "If you do not wish to use these defaults, you must overload\n";
1526  error_message +=
1527  "get_inner_products(), which must initialise the entries\n";
1528  error_message +=
1529  "of the returned vector to zero.\n\n";
1530 
1531  throw
1532  OomphLibError(
1533  error_message,
1534  OOMPH_CURRENT_FUNCTION,
1535  OOMPH_EXCEPTION_LOCATION);
1536  }
1537 
1538  //========================================================================
1539  /// \short Fill in the contributions to the vectors that when taken
1540  /// as dot product with other history values give the inner product
1541  /// over the element
1542  //==========================================================================
1544  Vector<unsigned> const &history_index,
1545  Vector<Vector<double> > &inner_product_vector)
1546  {
1547  std::string error_message =
1548  "Empty fill_in_contribution_to_inner_product_vectors() has been called.\n";
1549  error_message +=
1550  "This function is called from the default implementation of\n";
1551  error_message += "get_inner_product_vectors(); ";
1552  error_message += " and must calculate the vectors \n";
1553  error_message += "corresponding to the input history values\n ";
1554  error_message += "that when multiplied by other vectors of history values\n";
1555  error_message += "return the appropriate dot products.\n\n";
1556  error_message += "The output vectors must not be initialised.\n\n";
1557 
1558  error_message +=
1559  "If you do not wish to use these defaults, you must overload\n";
1560  error_message +=
1561  "get_inner_products(), which must initialise the entries\n";
1562  error_message +=
1563  "of the returned vector to zero.\n\n";
1564 
1565  throw
1566  OomphLibError(
1567  error_message,
1568  OOMPH_CURRENT_FUNCTION,
1569  OOMPH_EXCEPTION_LOCATION);
1570  }
1571 
1572 
1573 
1574 
1575 
1576 //==========================================================================
1577 /// Self-test: Have all internal values been classified as
1578 /// pinned/unpinned? Return 0 if OK.
1579 //==========================================================================
1581  {
1582  // Initialise
1583  bool passed=true;
1584 
1585  //Loop over internal Data
1586  for(unsigned i=0;i<Ninternal_data;i++)
1587  {
1588  if(internal_data_pt(i)->self_test()!=0)
1589  {
1590  passed=false;
1591  oomph_info
1592  << "\n ERROR: Failed GeneralisedElement::self_test()!" << std::endl;
1593  oomph_info
1594  << "for internal data object number: " << i << std::endl;
1595  }
1596  }
1597 
1598  //Loop over external Data
1599  for(unsigned i=0;i<Nexternal_data;i++)
1600  {
1601  if(external_data_pt(i)->self_test()!=0)
1602  {
1603  passed=false;
1604  oomph_info
1605  << "\n ERROR: Failed GeneralisedElement::self_test()!" << std::endl;
1606  oomph_info
1607  << "for external data object number: " << i << std::endl;
1608  }
1609  }
1610 
1611  // Return verdict
1612  if (passed) {return 0;}
1613  else {return 1;}
1614  }
1615 
1616 
1617 //======================================================================
1618 /// Helper namespace for tolerances, number of iterations, etc
1619 /// used in the locate_zeta function in FiniteElement
1620 //======================================================================
1621 namespace Locate_zeta_helpers
1622 {
1623  /// Convergence tolerance for the newton solver
1624  double Newton_tolerance = 1.0e-7;
1625 
1626  /// Maximum number of newton iterations
1627  unsigned Max_newton_iterations = 10;
1628 
1629  /// \short Multiplier for (zeta-based) outer radius of element used for
1630  /// deciding that point is outside element. Set to negative value
1631  /// to suppress test.
1633 
1634  /// Number of points along one dimension of each element used
1635  /// to create coordinates within the element in order to see
1636  /// which has the smallest initial residual (and is therefore used
1637  /// as the initial guess in the Newton method when locating coordinate)
1638  unsigned N_local_points = 5;
1639 }
1640 
1641 
1642 ///////////////////////////////////////////////////////////////////////////
1643 ///////////////////////////////////////////////////////////////////////////
1644 // Functions for finite elements
1645 ///////////////////////////////////////////////////////////////////////////
1646 ///////////////////////////////////////////////////////////////////////////
1647 
1648 //======================================================================
1649  /// Return the i-th coordinate at local node n. Do not use
1650  /// the hanging node representation.
1651  /// NOTE: Moved to cc file because of a possible compiler bug
1652  /// in gcc (yes, really!). The move to the cc file avoids inlining
1653  /// which appears to cause problems (only) when compiled with gcc
1654  /// and -O3; offensive "illegal read" is in optimised-out section
1655  /// of code and data that is allegedly illegal is readily readable
1656  /// (by other means) just before this function is called so I can't
1657  /// really see how we could possibly be responsible for this...
1658 //======================================================================
1659  double FiniteElement::raw_nodal_position(const unsigned &n, const unsigned &i) const
1660  {
1661  /* oomph_info << "n: "<< n << std::endl; */
1662  /* oomph_info << "i: "<< i << std::endl; */
1663  /* oomph_info << "node_pt(n): "<< node_pt(n) << std::endl; */
1664  /* oomph_info << "node_pt(n)->x(i): "<< node_pt(n)->x(i) << std::endl; */
1665  //double tmp=node_pt(n)->x(i);
1666  //oomph_info << "tmp: "<< tmp << std::endl;
1667  return node_pt(n)->x(i);
1668  }
1669 
1670 
1671 
1672 //======================================================================
1673 /// \short Function to describe the local dofs of the element. The ostream
1674 /// specifies the output stream to which the description
1675 /// is written; the string stores the currently
1676 /// assembled output that is ultimately written to the
1677 /// output stream by Data::describe_dofs(...); it is typically
1678 /// built up incrementally as we descend through the
1679 /// call hierarchy of this function when called from
1680 /// Problem::describe_dofs(...)
1681 //======================================================================
1682 void FiniteElement::describe_local_dofs(std::ostream& out,
1683  const std::string& current_string) const
1684 {
1685  // Call the standard finite element classification.
1686  GeneralisedElement::describe_local_dofs(out,current_string);
1687  describe_nodal_local_dofs(out,current_string);
1688 }
1689 
1690 //======================================================================
1691 // \short Function to describe the local dofs of the element. The ostream
1692 /// specifies the output stream to which the description
1693 /// is written; the string stores the currently
1694 /// assembled output that is ultimately written to the
1695 /// output stream by Data::describe_dofs(...); it is typically
1696 /// built up incrementally as we descend through the
1697 /// call hierarchy of this function when called from
1698 /// Problem::describe_dofs(...)
1699 //======================================================================
1700 void FiniteElement::
1701 describe_nodal_local_dofs(std::ostream& out,
1702  const std::string& current_string) const
1703 {
1704  //Find the number of nodes
1705  const unsigned n_node = nnode();
1706  for(unsigned n=0;n<n_node;n++)
1707  {
1708  //Pointer to node
1709  Node* const nod_pt = node_pt(n);
1710 
1711  std::stringstream conversion;
1712  conversion<<" of Node "<<n<<current_string;
1713  std::string in(conversion.str());
1714  nod_pt->describe_dofs(out,in);
1715  }// End if for n_node
1716 }// End describe_nodal_local_dofs
1717 
1718 //========================================================================
1719 /// \short Internal function used to check for singular or negative values
1720 /// of the determinant of the Jacobian of the mapping between local and
1721 /// global or lagrangian coordinates. Negative jacobians are allowed if the
1722 /// Accept_negative_jacobian flag is set to true.
1723 //========================================================================
1724  void FiniteElement::check_jacobian(const double &jacobian) const
1725  {
1726  //First check for a zero jacobian
1727  if(std::fabs(jacobian) < Tolerance_for_singular_jacobian)
1728  {
1730  {
1731  throw OomphLibQuietException();
1732  }
1733  else
1734  {
1735  std::ostringstream error_stream;
1736  error_stream
1737  << "Determinant of Jacobian matrix is zero --- "
1738  << "singular mapping!\nThe determinant of the "
1739  << "jacobian is " << std::fabs(jacobian)
1740  << " which is smaller than the treshold "
1741  << Tolerance_for_singular_jacobian <<"\n"
1742  << "You can change this treshold, by specifying "
1743  << "FiniteElement::Tolerance_for_singular_jacobian \n"
1744  << "Here are the nodal coordinates of the inverted element\n"
1745  << "in the form \n\n x,y[,z], hang_status\n\n"
1746  << "where hang_status = 1 or 2 for non-hanging or hanging\n"
1747  << "nodes respectively (useful for automatic sizing of\n"
1748  << "tecplot markers to identify the hanging nodes). \n\n" ;
1749  unsigned n_dim_nod=node_pt(0)->ndim();
1750  unsigned n_nod=nnode();
1751  unsigned hang_count=0;
1752  for (unsigned j=0;j<n_nod;j++)
1753  {
1754  for (unsigned i=0;i<n_dim_nod;i++)
1755  {
1756  error_stream << node_pt(j)->x(i) << " ";
1757  }
1758  if (node_pt(j)->is_hanging())
1759  {
1760  error_stream << " 2";
1761  hang_count++;
1762  }
1763  else
1764  {
1765  error_stream << " 1";
1766  }
1767  error_stream << std::endl;
1768  }
1769  error_stream << std::endl << std::endl;
1770  if ((Macro_elem_pt!=0)&&(0!=hang_count))
1771  {
1772  error_stream
1773  << "NOTE: Offending element is associated with a MacroElement\n"
1774  << " AND the element has hanging nodes! \n"
1775  << " If an element is thin and highly curved, the \n"
1776  << " constraints imposed by\n \n"
1777  << " (1) inter-element continuity (imposed by the hanging\n"
1778  << " node constraints) and \n\n"
1779  << " (2) the need to respect curvilinear domain boundaries\n"
1780  << " during mesh refinement (imposed by the element's\n"
1781  << " macro element mapping)\n\n"
1782  << " may be irreconcilable! \n \n"
1783  << " You may have to re-design your base mesh to avoid \n"
1784  << " the creation of thin, highly curved elements during\n"
1785  << " the refinement process.\n"
1786  << std::endl;
1787  }
1788  throw OomphLibError(error_stream.str(),
1789  OOMPH_CURRENT_FUNCTION,
1790  OOMPH_EXCEPTION_LOCATION);
1791  }
1792  }
1793 
1794 
1795  //Now check for negative jacobians, if we're not allowing them (default)
1796  if((Accept_negative_jacobian==false) && (jacobian < 0.0))
1797  {
1798 
1800  {
1801  throw OomphLibQuietException();
1802  }
1803  else
1804  {
1805  std::ostringstream error_stream;
1806  error_stream
1807  << "Negative Jacobian in transform from "
1808  << "local to global coordinates"
1809  << std::endl
1810  << " You have an inverted coordinate system!"
1811  << std::endl << std::endl;
1812  error_stream
1813  << "Here are the nodal coordinates of the inverted element\n"
1814  << "in the form \n\n x,y[,z], hang_status\n\n"
1815  << "where hang_status = 1 or 2 for non-hanging or hanging\n"
1816  << "nodes respectively (useful for automatic sizing of\n"
1817  << "tecplot markers to identify the hanging nodes). \n\n" ;
1818  unsigned n_dim_nod=node_pt(0)->ndim();
1819  unsigned n_nod=nnode();
1820  unsigned hang_count=0;
1821  for (unsigned j=0;j<n_nod;j++)
1822  {
1823  for (unsigned i=0;i<n_dim_nod;i++)
1824  {
1825  error_stream << node_pt(j)->x(i) << " ";
1826  }
1827  if (node_pt(j)->is_hanging())
1828  {
1829  error_stream << " 2";
1830  hang_count++;
1831  }
1832  else
1833  {
1834  error_stream << " 1";
1835  }
1836  error_stream << std::endl;
1837  }
1838  error_stream << std::endl << std::endl;
1839  if ((Macro_elem_pt!=0)&&(0!=hang_count))
1840  {
1841  error_stream
1842  << "NOTE: The inverted element is associated with a MacroElement\n"
1843  << " AND the element has hanging nodes! \n"
1844  << " If an element is thin and highly curved, the \n"
1845  << " constraints imposed by\n \n"
1846  << " (1) inter-element continuity (imposed by the hanging\n"
1847  << " node constraints) and \n\n"
1848  << " (2) the need to respect curvilinear domain boundaries\n"
1849  << " during mesh refinement (imposed by the element's\n"
1850  << " macro element mapping)\n\n"
1851  << " may be irreconcilable! \n \n"
1852  << " You may have to re-design your base mesh to avoid \n"
1853  << " the creation of thin, highly curved elements during\n"
1854  << " the refinement process.\n"
1855  << std::endl;
1856  }
1857 
1858  error_stream
1859  << std::endl << std::endl
1860  << "If you believe that inverted elements do not cause any\n"
1861  << "problems in your specific application you can \n "
1862  << "suppress this test by: " << std::endl
1863  << " i) setting the (static) flag "
1864  << "FiniteElement::Accept_negative_jacobian to be true" << std::endl;
1865  error_stream << " ii) switching OFF the PARANOID flag"
1866  << std::endl << std::endl;
1867 
1868  throw OomphLibError(error_stream.str(),
1869  OOMPH_CURRENT_FUNCTION,
1870  OOMPH_EXCEPTION_LOCATION);
1871  }
1872  }
1873  }
1874 
1875 //=========================================================================
1876 /// Internal function that is used to assemble the jacobian of the mapping
1877 /// from local coordinates (s) to the eulerian coordinates (x), given the
1878 /// derivatives of the shape functions. The entire jacobian matrix is
1879 /// constructed and this function will only work if there are the same number
1880 /// of local coordinates as global coordinates (i.e. for "bulk" elements).
1881 //=========================================================================
1882 void FiniteElement::
1884  DenseMatrix<double> &jacobian) const
1885 {
1886  //Locally cache the elemental dimension
1887  const unsigned el_dim = dim();
1888  //The number of shape functions must be equal to the number
1889  //of nodes (by definition)
1890  const unsigned n_shape = nnode();
1891  //The number of shape function types must be equal to the number
1892  //of nodal position types (by definition)
1893  const unsigned n_shape_type = nnodal_position_type();
1894 
1895 #ifdef PARANOID
1896  //Check for dimensional compatibility
1897  if(Elemental_dimension != Nodal_dimension)
1898  {
1899  std::ostringstream error_message;
1900  error_message << "Dimension mismatch" << std::endl;
1901  error_message << "The elemental dimension: " << Elemental_dimension
1902  << " must equal the nodal dimension: "
1903  << Nodal_dimension
1904  << " for the jacobian of the mapping to be well-defined"
1905  << std::endl;
1906  throw OomphLibError(error_message.str(),
1907  OOMPH_CURRENT_FUNCTION,
1908  OOMPH_EXCEPTION_LOCATION);
1909  }
1910 #endif
1911 
1912 
1913  //Loop over the rows of the jacobian
1914  for(unsigned i=0;i<el_dim;i++)
1915  {
1916  //Loop over the columns of the jacobian
1917  for(unsigned j=0;j<el_dim;j++)
1918  {
1919  //Zero the entry
1920  jacobian(i,j) = 0.0;
1921  //Loop over the shape functions
1922  for(unsigned l=0;l<n_shape;l++)
1923  {
1924  for(unsigned k=0;k<n_shape_type;k++)
1925  {
1926  //Jacobian is dx_j/ds_i, which is represented by the sum
1927  //over the dpsi/ds_i of the nodal points X j
1928  //Call the Non-hanging version of positions
1929  //This is overloaded in refineable elements
1930  jacobian(i,j) += raw_nodal_position_gen(l,k,j)*dpsids(l,k,i);
1931  }
1932  }
1933  }
1934  }
1935 }
1936 
1937 //=========================================================================
1938 /// Internal function that is used to assemble the jacobian of second
1939 /// derivatives of the mapping from local coordinates (s) to the
1940 /// eulerian coordinates (x), given the second derivatives of the
1941 /// shape functions.
1942 //=========================================================================
1943  void FiniteElement::
1945  DenseMatrix<double> &jacobian2) const
1946  {
1947  //Find the dimension of the element
1948  const unsigned el_dim = dim();
1949  //Find the number of shape functions and shape functions types
1950  //Must be equal to the number of nodes and their position types
1951  //by the definition of the shape function.
1952  const unsigned n_shape = nnode();
1953  const unsigned n_shape_type = nnodal_position_type();
1954  //Find the number of second derivatives
1955  const unsigned n_row = N2deriv[el_dim];
1956 
1957  //Assemble the "jacobian" (d^2 x_j/ds_i^2) for second derivatives of
1958  //shape functions
1959  //Loop over the rows (number of second derivatives)
1960  for(unsigned i=0;i<n_row;i++)
1961  {
1962  //Loop over the columns (element dimension
1963  for(unsigned j=0;j<el_dim;j++)
1964  {
1965  //Zero the entry
1966  jacobian2(i,j) = 0.0;
1967  //Loop over the shape functions
1968  for(unsigned l=0;l<n_shape;l++)
1969  {
1970  //Loop over the shape function types
1971  for(unsigned k=0;k<n_shape_type;k++)
1972  {
1973  //Add the terms to the jacobian entry
1974  //Call the Non-hanging version of positions
1975  //This is overloaded in refineable elements
1976  jacobian2(i,j) += raw_nodal_position_gen(l,k,j)*d2psids(l,k,i);
1977  }
1978  }
1979  }
1980  }
1981  }
1982 
1983  //=====================================================================
1984  /// Assemble the covariant Eulerian base vectors and return them in the
1985  /// matrix interpolated_G. The derivatives of the shape functions with
1986  /// respect to the local coordinate should already have been calculated
1987  /// before calling this function
1988  //=====================================================================
1990  const DShape &dpsids, DenseMatrix<double> &interpolated_G) const
1991  {
1992  //Find the number of nodes and position types
1993  const unsigned n_node = nnode();
1994  const unsigned n_position_type = nnodal_position_type();
1995  //Find the dimension of the node and element
1996  const unsigned n_dim_node = nodal_dimension();
1997  const unsigned n_dim_element = dim();
1998 
1999  //Loop over the dimensions and compute the entries of the
2000  //base vector matrix
2001  for(unsigned i=0;i<n_dim_element;i++)
2002  {
2003  for(unsigned j=0;j<n_dim_node;j++)
2004  {
2005  //Initialise the j-th component of the i-th base vector to zero
2006  interpolated_G(i,j) = 0.0;
2007  for(unsigned l=0;l<n_node;l++)
2008  {
2009  for(unsigned k=0;k<n_position_type;k++)
2010  {
2011  interpolated_G(i,j) += raw_nodal_position_gen(l,k,j)*dpsids(l,k,i);
2012  }
2013  }
2014  }
2015  }
2016  }
2017 
2018 
2019 //============================================================================
2020 /// Zero-d specialisation of function to calculate inverse of jacobian mapping
2021 //============================================================================
2022  template<>
2023  double FiniteElement::
2024  invert_jacobian<0>(const DenseMatrix<double> &jacobian,
2025  DenseMatrix<double> &inverse_jacobian) const
2026  {
2027  //Issue a warning
2028  oomph_info << "\nWarning: You are trying to invert the jacobian for "
2029  << "a 'point' element" << std::endl
2030  << "This makes no sense and is almost certainly an error"
2031  << std::endl << std::endl;
2032 
2033  //Dummy return
2034  return(1.0);
2035  }
2036 
2037 
2038 //===========================================================================
2039 /// One-d specialisation of function to calculate inverse of jacobian mapping
2040 //===========================================================================
2041  template<>
2042  double FiniteElement::
2043  invert_jacobian<1>(const DenseMatrix<double> &jacobian,
2044  DenseMatrix<double> &inverse_jacobian) const
2045  {
2046  //Calculate the determinant of the matrix
2047  const double det = jacobian(0,0);
2048 
2049 //Report if Matrix is singular or negative
2050 #ifdef PARANOID
2051  check_jacobian(det);
2052 #endif
2053 
2054  //Calculate the inverse --- trivial in 1D
2055  inverse_jacobian(0,0) = 1.0/jacobian(0,0);
2056 
2057  //Return the determinant
2058  return(det);
2059  }
2060 
2061 //===========================================================================
2062 /// Two-d specialisation of function to calculate inverse of jacobian mapping
2063 //===========================================================================
2064  template<>
2065  double FiniteElement::
2066  invert_jacobian<2>(const DenseMatrix<double> &jacobian,
2067  DenseMatrix<double> &inverse_jacobian) const
2068  {
2069  //Calculate the determinant of the matrix
2070  const double det = jacobian(0,0)*jacobian(1,1) - jacobian(0,1)*jacobian(1,0);
2071 
2072 //Report if Matrix is singular or negative
2073 #ifdef PARANOID
2074  check_jacobian(det);
2075 #endif
2076 
2077  //Calculate the inverset of the 2x2 matrix
2078  inverse_jacobian(0,0) = jacobian(1,1)/det;
2079  inverse_jacobian(0,1) = -jacobian(0,1)/det;
2080  inverse_jacobian(1,0) = -jacobian(1,0)/det;
2081  inverse_jacobian(1,1) = jacobian(0,0)/det;
2082 
2083  //Return the jacobian
2084  return(det);
2085  }
2086 
2087 //=============================================================================
2088 /// Three-d specialisation of function to calculate inverse of jacobian mapping
2089 //=============================================================================
2090  template<>
2091  double FiniteElement::
2092  invert_jacobian<3>(const DenseMatrix<double> &jacobian,
2093  DenseMatrix<double> &inverse_jacobian) const
2094  {
2095  //Calculate the determinant of the matrix
2096  const double det = jacobian(0,0)*jacobian(1,1)*jacobian(2,2)
2097  + jacobian(0,1)*jacobian(1,2)*jacobian(2,0)
2098  + jacobian(0,2)*jacobian(1,0)*jacobian(2,1)
2099  - jacobian(0,0)*jacobian(1,2)*jacobian(2,1)
2100  - jacobian(0,1)*jacobian(1,0)*jacobian(2,2)
2101  - jacobian(0,2)*jacobian(1,1)*jacobian(2,0);
2102 
2103  //Report if Matrix is singular or negative
2104 #ifdef PARANOID
2105  check_jacobian(det);
2106 #endif
2107 
2108  //Calculate the inverse of the 3x3 matrix
2109  inverse_jacobian(0,0) = (jacobian(1,1)*jacobian(2,2)
2110  - jacobian(1,2)*jacobian(2,1))/det;
2111  inverse_jacobian(0,1) = -(jacobian(0,1)*jacobian(2,2)
2112  - jacobian(0,2)*jacobian(2,1))/det;
2113  inverse_jacobian(0,2) = (jacobian(0,1)*jacobian(1,2)
2114  - jacobian(0,2)*jacobian(1,1))/det;
2115  inverse_jacobian(1,0) = -(jacobian(1,0)*jacobian(2,2)
2116  - jacobian(1,2)*jacobian(2,0))/det;
2117  inverse_jacobian(1,1) = (jacobian(0,0)*jacobian(2,2)
2118  - jacobian(0,2)*jacobian(2,0))/det;
2119  inverse_jacobian(1,2) = -(jacobian(0,0)*jacobian(1,2)
2120  - jacobian(0,2)*jacobian(1,0))/det;
2121  inverse_jacobian(2,0) = (jacobian(1,0)*jacobian(2,1)
2122  - jacobian(1,1)*jacobian(2,0))/det;
2123  inverse_jacobian(2,1) = -(jacobian(0,0)*jacobian(2,1)
2124  - jacobian(0,1)*jacobian(2,0))/det;
2125  inverse_jacobian(2,2) = (jacobian(0,0)*jacobian(1,1)
2126  - jacobian(0,1)*jacobian(1,0))/det;
2127 
2128  //Return the determinant
2129  return(det);
2130  }
2131 
2132 //========================================================================
2133 /// Template-free interface for inversion of the jacobian of a mapping.
2134 /// This is slightly inefficient, given that it uses a switch statement.
2135 /// It can always be overloaded in specific geometric elements,
2136 /// for efficiency reasons.
2137 //========================================================================
2138  double FiniteElement::
2140  DenseMatrix<double> &inverse_jacobian) const
2141  {
2142  //Find the spatial dimension of the element
2143  const unsigned el_dim = dim();
2144  //Call the appropriate templated function, depending on the
2145  //element dimension
2146  switch(el_dim)
2147  {
2148  case 0:
2149  return invert_jacobian<0>(jacobian,inverse_jacobian);
2150  break;
2151  case 1:
2152  return invert_jacobian<1>(jacobian,inverse_jacobian);
2153  break;
2154  case 2:
2155  return invert_jacobian<2>(jacobian,inverse_jacobian);
2156  break;
2157  case 3:
2158  return invert_jacobian<3>(jacobian,inverse_jacobian);
2159  break;
2160  //Catch-all default case: issue warning and die
2161  default:
2162  std::ostringstream error_stream;
2163  error_stream
2164  << "Dimension of the element must be 0,1,2 or 3, not " << el_dim
2165  << std::endl;
2166 
2167  throw OomphLibError(error_stream.str(),
2168  OOMPH_CURRENT_FUNCTION,
2169  OOMPH_EXCEPTION_LOCATION);
2170  }
2171  //Dummy return for Intel compiler
2172  return 1.0;
2173  }
2174 
2175 //============================================================================
2176 /// Zero-d specialisation of function to calculate the derivative of the
2177 /// jacobian of a mapping with respect to the nodal coordinates X_ij.
2178 //============================================================================
2179  template<>
2180  void FiniteElement::dJ_eulerian_dnodal_coordinates_templated_helper<0>(
2181  const DenseMatrix<double> &jacobian,const DShape &dpsids,
2182  DenseMatrix<double> &djacobian_dX) const
2183  {
2184  // Issue a warning
2185  oomph_info << "\nWarning: You are trying to calculate derivatives of "
2186  << "a jacobian w.r.t. nodal coordinates for a 'point' "
2187  << "element." << std::endl
2188  << "This makes no sense and is almost certainly an error."
2189  << std::endl << std::endl;
2190  }
2191 
2192 //===========================================================================
2193 /// One-d specialisation of function to calculate the derivative of the
2194 /// jacobian of a mapping with respect to the nodal coordinates X_ij.
2195 //===========================================================================
2196  template<>
2197  void FiniteElement::dJ_eulerian_dnodal_coordinates_templated_helper<1>(
2198  const DenseMatrix<double> &jacobian,const DShape &dpsids,
2199  DenseMatrix<double> &djacobian_dX) const
2200  {
2201  // Determine the number of nodes in the element
2202  const unsigned n_node = nnode();
2203 
2204  // Loop over nodes
2205  for(unsigned j=0;j<n_node;j++)
2206  {
2207  djacobian_dX(0,j) = dpsids(j,0);
2208  }
2209  }
2210 
2211 //===========================================================================
2212 /// Two-d specialisation of function to calculate the derivative of the
2213 /// jacobian of a mapping with respect to the nodal coordinates X_ij.
2214 //===========================================================================
2215  template<>
2216  void FiniteElement::dJ_eulerian_dnodal_coordinates_templated_helper<2>(
2217  const DenseMatrix<double> &jacobian,const DShape &dpsids,
2218  DenseMatrix<double> &djacobian_dX) const
2219  {
2220  // Determine the number of nodes in the element
2221  const unsigned n_node = nnode();
2222 
2223  // Loop over nodes
2224  for(unsigned j=0;j<n_node;j++)
2225  {
2226  // i=0
2227  djacobian_dX(0,j) = dpsids(j,0)*jacobian(1,1) - dpsids(j,1)*jacobian(0,1);
2228 
2229  // i=1
2230  djacobian_dX(1,j) = dpsids(j,1)*jacobian(0,0) - dpsids(j,0)*jacobian(1,0);
2231  }
2232  }
2233 
2234 //=============================================================================
2235 /// Three-d specialisation of function to calculate the derivative of the
2236 /// jacobian of a mapping with respect to the nodal coordinates X_ij.
2237 //=============================================================================
2238  template<>
2239  void FiniteElement::dJ_eulerian_dnodal_coordinates_templated_helper<3>(
2240  const DenseMatrix<double> &jacobian,const DShape &dpsids,
2241  DenseMatrix<double> &djacobian_dX) const
2242  {
2243  // Determine the number of nodes in the element
2244  const unsigned n_node = nnode();
2245 
2246  // Loop over nodes
2247  for(unsigned j=0;j<n_node;j++)
2248  {
2249  // i=0
2250  djacobian_dX(0,j)
2251  = dpsids(j,0)*(jacobian(1,1)*jacobian(2,2)
2252  - jacobian(1,2)*jacobian(2,1))
2253  + dpsids(j,1)*(jacobian(0,2)*jacobian(2,1)
2254  - jacobian(0,1)*jacobian(2,2))
2255  + dpsids(j,2)*(jacobian(0,1)*jacobian(1,2)
2256  - jacobian(0,2)*jacobian(1,1));
2257 
2258  // i=1
2259  djacobian_dX(1,j)
2260  = dpsids(j,0)*(jacobian(1,2)*jacobian(2,0)
2261  - jacobian(1,0)*jacobian(2,2))
2262  + dpsids(j,1)*(jacobian(0,0)*jacobian(2,2)
2263  - jacobian(0,2)*jacobian(2,0))
2264  + dpsids(j,2)*(jacobian(0,2)*jacobian(1,0)
2265  - jacobian(0,0)*jacobian(1,2));
2266 
2267  // i=2
2268  djacobian_dX(2,j)
2269  = dpsids(j,0)*(jacobian(1,0)*jacobian(2,1)
2270  - jacobian(1,1)*jacobian(2,0))
2271  + dpsids(j,1)*(jacobian(0,1)*jacobian(2,0)
2272  - jacobian(0,0)*jacobian(2,1))
2273  + dpsids(j,2)*(jacobian(0,0)*jacobian(1,1)
2274  - jacobian(0,1)*jacobian(1,0));
2275  }
2276  }
2277 
2278 //============================================================================
2279 /// Zero-d specialisation of function to calculate the derivative w.r.t. the
2280 /// nodal coordinates \f$ X_{pq} \f$ of the derivative of the shape functions
2281 /// w.r.t. the global eulerian coordinates \f$ x_i \f$.
2282 //============================================================================
2283  template<>
2284  void FiniteElement::d_dshape_eulerian_dnodal_coordinates_templated_helper<0>(
2285  const double &det_jacobian,
2286  const DenseMatrix<double> &jacobian,
2287  const DenseMatrix<double> &djacobian_dX,
2288  const DenseMatrix<double> &inverse_jacobian,
2289  const DShape &dpsids,
2290  RankFourTensor<double> &d_dpsidx_dX) const
2291  {
2292  // Issue a warning
2293  oomph_info << "\nWarning: You are trying to calculate derivatives of "
2294  << "eulerian derivatives of shape functions w.r.t. nodal "
2295  << "coordinates for a 'point' element." << std::endl
2296  << "This makes no sense and is almost certainly an error."
2297  << std::endl << std::endl;
2298  }
2299 
2300 //===========================================================================
2301 /// One-d specialisation of function to calculate the derivative w.r.t. the
2302 /// nodal coordinates \f$ X_{pq} \f$ of the derivative of the shape functions
2303 /// w.r.t. the global eulerian coordinates \f$ x_i \f$.
2304 //===========================================================================
2305  template<>
2306  void FiniteElement::d_dshape_eulerian_dnodal_coordinates_templated_helper<1>(
2307  const double &det_jacobian,
2308  const DenseMatrix<double> &jacobian,
2309  const DenseMatrix<double> &djacobian_dX,
2310  const DenseMatrix<double> &inverse_jacobian,
2311  const DShape &dpsids,
2312  RankFourTensor<double> &d_dpsidx_dX) const
2313  {
2314  // Find inverse of determinant of jacobian of mapping
2315  const double inv_det_jac = 1.0/det_jacobian;
2316 
2317  // Determine the number of nodes in the element
2318  const unsigned n_node = nnode();
2319 
2320  // Loop over the shape functions
2321  for(unsigned q=0;q<n_node;q++)
2322  {
2323  // Loop over the shape functions
2324  for(unsigned j=0;j<n_node;j++)
2325  {
2326  d_dpsidx_dX(0,q,j,0)
2327  = - djacobian_dX(0,q)*dpsids(j,0)*inv_det_jac*inv_det_jac;
2328  }
2329  }
2330  }
2331 
2332 //===========================================================================
2333 /// Two-d specialisation of function to calculate the derivative w.r.t. the
2334 /// nodal coordinates \f$ X_{pq} \f$ of the derivative of the shape functions
2335 /// w.r.t. the global eulerian coordinates \f$ x_i \f$.
2336 //===========================================================================
2337  template<>
2338  void FiniteElement::d_dshape_eulerian_dnodal_coordinates_templated_helper<2>(
2339  const double &det_jacobian,
2340  const DenseMatrix<double> &jacobian,
2341  const DenseMatrix<double> &djacobian_dX,
2342  const DenseMatrix<double> &inverse_jacobian,
2343  const DShape &dpsids,
2344  RankFourTensor<double> &d_dpsidx_dX) const
2345  {
2346  // Find inverse of determinant of jacobian of mapping
2347  const double inv_det_jac = 1.0/det_jacobian;
2348 
2349  // Determine the number of nodes in the element
2350  const unsigned n_node = nnode();
2351 
2352  // Loop over the spatial dimension (this must be 2)
2353  for(unsigned p=0;p<2;p++)
2354  {
2355  // Loop over the shape functions
2356  for(unsigned q=0;q<n_node;q++)
2357  {
2358  // Loop over the shape functions
2359  for(unsigned j=0;j<n_node;j++)
2360  {
2361  // i=0
2362  d_dpsidx_dX(p,q,j,0) = - djacobian_dX(p,q)*
2363  (inverse_jacobian(0,0)*dpsids(j,0)
2364  + inverse_jacobian(0,1)*dpsids(j,1));
2365 
2366  if(p==1)
2367  {
2368  d_dpsidx_dX(p,q,j,0)
2369  += dpsids(j,0)*dpsids(q,1) - dpsids(j,1)*dpsids(q,0);
2370  }
2371  d_dpsidx_dX(p,q,j,0) *= inv_det_jac;
2372 
2373  // i=1
2374  d_dpsidx_dX(p,q,j,1) = - djacobian_dX(p,q)*
2375  (inverse_jacobian(1,1)*dpsids(j,1)
2376  + inverse_jacobian(1,0)*dpsids(j,0));
2377 
2378  if(p==0)
2379  {
2380  d_dpsidx_dX(p,q,j,1)
2381  += dpsids(j,1)*dpsids(q,0) - dpsids(j,0)*dpsids(q,1);
2382  }
2383  d_dpsidx_dX(p,q,j,1) *= inv_det_jac;
2384  }
2385  }
2386  }
2387  }
2388 
2389 //=============================================================================
2390 /// Three-d specialisation of function to calculate the derivative w.r.t. the
2391 /// nodal coordinates \f$ X_{pq} \f$ of the derivative of the shape functions
2392 /// w.r.t. the global eulerian coordinates \f$ x_i \f$.
2393 //=============================================================================
2394  template<>
2395  void FiniteElement::d_dshape_eulerian_dnodal_coordinates_templated_helper<3>(
2396  const double &det_jacobian,
2397  const DenseMatrix<double> &jacobian,
2398  const DenseMatrix<double> &djacobian_dX,
2399  const DenseMatrix<double> &inverse_jacobian,
2400  const DShape &dpsids,
2401  RankFourTensor<double> &d_dpsidx_dX) const
2402  {
2403  // Find inverse of determinant of jacobian of mapping
2404  const double inv_det_jac = 1.0/det_jacobian;
2405 
2406  // Determine the number of nodes in the element
2407  const unsigned n_node = nnode();
2408 
2409  // Loop over the spatial dimension (this must be 3)
2410  for(unsigned p=0;p<3;p++)
2411  {
2412  // Loop over the shape functions
2413  for(unsigned q=0;q<n_node;q++)
2414  {
2415  // Loop over the shape functions
2416  for(unsigned j=0;j<n_node;j++)
2417  {
2418  // Terms not multiplied by delta function
2419  for(unsigned i=0;i<3;i++)
2420  {
2421  d_dpsidx_dX(p,q,j,i)
2422  = - djacobian_dX(p,q)*(inverse_jacobian(i,0)*dpsids(j,0)
2423  + inverse_jacobian(i,1)*dpsids(j,1)
2424  + inverse_jacobian(i,2)*dpsids(j,2));
2425  }
2426 
2427  // Delta function terms
2428  switch(p)
2429  {
2430  case 0:
2431  d_dpsidx_dX(p,q,j,1)+=((dpsids(q,2)*jacobian(1,2)
2432  - dpsids(q,1)*jacobian(2,2))*dpsids(j,0)
2433  + (dpsids(q,0)*jacobian(2,2)
2434  - dpsids(q,2)*jacobian(0,2))*dpsids(j,1)
2435  + (dpsids(q,1)*jacobian(0,2)
2436  - dpsids(q,0)*jacobian(1,2))*dpsids(j,2));
2437 
2438  d_dpsidx_dX(p,q,j,2)+=((dpsids(q,1)*jacobian(2,1)
2439  - dpsids(q,2)*jacobian(1,1))*dpsids(j,0)
2440  + (dpsids(q,2)*jacobian(0,1)
2441  - dpsids(q,0)*jacobian(2,1))*dpsids(j,1)
2442  + (dpsids(q,0)*jacobian(1,1)
2443  - dpsids(q,1)*jacobian(0,1))*dpsids(j,2));
2444  break;
2445 
2446  case 1:
2447 
2448  d_dpsidx_dX(p,q,j,0)+=((dpsids(q,1)*jacobian(2,2)
2449  - dpsids(q,2)*jacobian(1,2))*dpsids(j,0)
2450  + (dpsids(q,2)*jacobian(0,2)
2451  - dpsids(q,0)*jacobian(2,2))*dpsids(j,1)
2452  + (dpsids(q,0)*jacobian(1,2)
2453  - dpsids(q,1)*jacobian(0,2))*dpsids(j,2));
2454 
2455  d_dpsidx_dX(p,q,j,2)+=((dpsids(q,2)*jacobian(1,0)
2456  - dpsids(q,1)*jacobian(2,0))*dpsids(j,0)
2457  + (dpsids(q,0)*jacobian(2,0)
2458  - dpsids(q,2)*jacobian(0,0))*dpsids(j,1)
2459  + (dpsids(q,1)*jacobian(0,0)
2460  - dpsids(q,0)*jacobian(1,0))*dpsids(j,2));
2461  break;
2462 
2463  case 2:
2464 
2465  d_dpsidx_dX(p,q,j,0)+=((dpsids(q,2)*jacobian(1,1)
2466  - dpsids(q,1)*jacobian(2,1))*dpsids(j,0)
2467  + (dpsids(q,0)*jacobian(2,1)
2468  - dpsids(q,2)*jacobian(0,1))*dpsids(j,1)
2469  + (dpsids(q,1)*jacobian(0,1)
2470  - dpsids(q,0)*jacobian(1,1))*dpsids(j,2));
2471 
2472  d_dpsidx_dX(p,q,j,1)+=((dpsids(q,1)*jacobian(2,0)
2473  - dpsids(q,2)*jacobian(1,0))*dpsids(j,0)
2474  + (dpsids(q,2)*jacobian(0,0)
2475  - dpsids(q,0)*jacobian(2,0))*dpsids(j,1)
2476  + (dpsids(q,0)*jacobian(1,0)
2477  - dpsids(q,1)*jacobian(0,0))*dpsids(j,2));
2478  break;
2479  }
2480 
2481  // Divide through by the determinant of the Jacobian mapping
2482  for(unsigned i=0;i<3;i++)
2483  {
2484  d_dpsidx_dX(p,q,j,i) *= inv_det_jac;
2485  }
2486  }
2487  }
2488  }
2489  }
2490 
2491 //=======================================================================
2492 /// Default value for the number of values at a node
2493 //=======================================================================
2494  const unsigned FiniteElement::Default_Initial_Nvalue = 0;
2495 
2496 //======================================================================
2497 /// \short Default value that is used for the tolerance required when
2498 /// locating nodes via local coordinates
2499  const double FiniteElement::Node_location_tolerance = 1.0e-14;
2500 
2501 //=======================================================================
2502 /// Set the default tolerance for a singular jacobian
2503 //=======================================================================
2505 
2506 //======================================================================
2507 /// Set the default value of the Accept_negative_jacobian flag to be
2508 /// false
2509 //======================================================================
2511 
2512 
2513 //======================================================================
2514 /// Set default for static boolean to suppress output while checking
2515 /// for inverted elements
2516 //======================================================================
2518 
2519 //========================================================================
2520 /// Static array that holds the number of rows in the second derivative
2521 /// matrix as a function of spatial dimension. In one-dimension, there is
2522 /// only one possible second derivative. In two-dimensions, there are three,
2523 /// the two second derivatives and the mixed derivatives. In three
2524 /// dimensions there are six.
2525 //=========================================================================
2526  const unsigned FiniteElement::N2deriv[4]={0,1,3,6};
2527 
2528 //==========================================================================
2529 /// Calculate the mapping from local to eulerian coordinates
2530 /// assuming that the coordinates are aligned in the direction of the local
2531 /// coordinates, i.e. there are no cross terms and the jacobian is diagonal.
2532 /// The local derivatives are passed as dpsids and the jacobian and
2533 /// inverse jacobian are returned.
2534 //==========================================================================
2535  double FiniteElement::
2537  DenseMatrix<double> &jacobian,
2538  DenseMatrix<double> &inverse_jacobian)
2539  const
2540  {
2541  //Find the dimension of the element
2542  const unsigned el_dim = dim();
2543  //Find the number of shape functions and shape functions types
2544  //Equal to the number of nodes and their position types by definition
2545  const unsigned n_shape = nnode();
2546  const unsigned n_shape_type = nnodal_position_type();
2547 
2548 #ifdef PARANOID
2549  //Check for dimension compatibility
2550  if(Elemental_dimension != Nodal_dimension)
2551  {
2552  std::ostringstream error_message;
2553  error_message << "Dimension mismatch" << std::endl;
2554  error_message << "The elemental dimension: " << Elemental_dimension
2555  << " must equal the nodal dimension: "
2556  << Nodal_dimension
2557  << " for the jacobian of the mapping to be well-defined"
2558  << std::endl;
2559  throw OomphLibError(error_message.str(),
2560  OOMPH_CURRENT_FUNCTION,
2561  OOMPH_EXCEPTION_LOCATION);
2562  }
2563 #endif
2564 
2565  //In this case we assume that there are no cross terms, that is
2566  //global coordinate 0 is always in the direction of local coordinate 0
2567 
2568  //Loop over the coordinates
2569  for(unsigned i=0;i<el_dim;i++)
2570  {
2571  //Zero the jacobian and inverse jacobian entries
2572  for(unsigned j=0;j<el_dim;j++)
2573  {jacobian(i,j) = 0.0; inverse_jacobian(i,j) = 0.0;}
2574 
2575  //Loop over the shape functions
2576  for(unsigned l=0;l<n_shape;l++)
2577  {
2578  //Loop over the types of dof
2579  for(unsigned k=0;k<n_shape_type;k++)
2580  {
2581  //Derivatives are always dx_{i}/ds_{i}
2582  jacobian(i,i) += raw_nodal_position_gen(l,k,i)*dpsids(l,k,i);
2583  }
2584  }
2585  }
2586 
2587  //Now calculate the determinant of the matrix
2588  double det = 1.0;
2589  for(unsigned i=0;i<el_dim;i++) {det *= jacobian(i,i);}
2590 
2591 //Report if Matrix is singular, or negative
2592 #ifdef PARANOID
2593  check_jacobian(det);
2594 #endif
2595 
2596  //Calculate the inverse mapping (trivial in this case)
2597  for(unsigned i=0;i<el_dim;i++)
2598  {inverse_jacobian(i,i) = 1.0/jacobian(i,i);}
2599 
2600  //Return the value of the Jacobian
2601  return(det);
2602  }
2603 
2604 //========================================================================
2605 /// Template-free interface calculating the derivative of the jacobian
2606 /// of a mapping with respect to the nodal coordinates X_ij. This is
2607 /// slightly inefficient, given that it uses a switch statement. It can
2608 /// always be overloaded in specific geometric elements, for efficiency
2609 /// reasons.
2610 //========================================================================
2612  const DenseMatrix<double> &jacobian,const DShape &dpsids,
2613  DenseMatrix<double> &djacobian_dX) const
2614 {
2615  // Determine the spatial dimension of the element
2616  const unsigned el_dim = dim();
2617 
2618 #ifdef PARANOID
2619  // Determine the number of nodes in the element
2620  const unsigned n_node = nnode();
2621 
2622  // Check that djacobian_dX has the correct number of rows (= el_dim)
2623  if(djacobian_dX.nrow()!=el_dim)
2624  {
2625  std::ostringstream error_message;
2626  error_message << "djacobian_dX must have the same number of rows as the"
2627  << "\nspatial dimension of the element.";
2628  throw OomphLibError(error_message.str(),
2629  OOMPH_CURRENT_FUNCTION,
2630  OOMPH_EXCEPTION_LOCATION);
2631  }
2632  // Check that djacobian_dX has the correct number of columns (= n_node)
2633  if(djacobian_dX.ncol()!=n_node)
2634  {
2635  std::ostringstream error_message;
2636  error_message << "djacobian_dX must have the same number of columns as the"
2637  << "\nnumber of nodes in the element.";
2638  throw OomphLibError(error_message.str(),
2639  OOMPH_CURRENT_FUNCTION,
2640  OOMPH_EXCEPTION_LOCATION);
2641  }
2642 #endif
2643 
2644  // Call the appropriate templated function, depending on the
2645  // element dimension
2646  switch(el_dim)
2647  {
2648  case 0:
2649  dJ_eulerian_dnodal_coordinates_templated_helper<0>(jacobian,dpsids,
2650  djacobian_dX);
2651  break;
2652  case 1:
2653  dJ_eulerian_dnodal_coordinates_templated_helper<1>(jacobian,dpsids,
2654  djacobian_dX);
2655  break;
2656  case 2:
2657  dJ_eulerian_dnodal_coordinates_templated_helper<2>(jacobian,dpsids,
2658  djacobian_dX);
2659  break;
2660  case 3:
2661  dJ_eulerian_dnodal_coordinates_templated_helper<3>(jacobian,dpsids,
2662  djacobian_dX);
2663  break;
2664  // Catch-all default case: issue warning and die
2665  default:
2666  std::ostringstream error_stream;
2667  error_stream
2668  << "Dimension of the element must be 0,1,2 or 3, not " << el_dim
2669  << std::endl;
2670 
2671  throw OomphLibError(error_stream.str(),
2672  OOMPH_CURRENT_FUNCTION,
2673  OOMPH_EXCEPTION_LOCATION);
2674  }
2675 }
2676 
2677 //========================================================================
2678 /// Template-free interface calculating the derivative w.r.t. the nodal
2679 /// coordinates \f$ X_{pq} \f$ of the derivative of the shape functions
2680 /// \f$ \psi_j \f$ w.r.t. the global eulerian coordinates \f$ x_i \f$.
2681 /// I.e. this function calculates
2682 /// \f[
2683 /// \frac{\partial}{\partial X_{pq}}
2684 /// \left( \frac{\partial \psi_j}{\partial x_i} \right).
2685 /// \f]
2686 /// To do this it requires the determinant of the jacobian mapping, its
2687 /// derivative w.r.t. the nodal coordinates \f$ X_{pq} \f$, the inverse
2688 /// jacobian and the derivatives of the shape functions w.r.t. the local
2689 /// coordinates. The result is returned as a tensor of rank four.
2690 /// Numbering:
2691 /// d_dpsidx_dX(p,q,j,i) = \f$ \frac{\partial}{\partial X_{pq}}
2692 /// \left( \frac{\partial \psi_j}{\partial x_i} \right) \f$
2693 /// This function is slightly inefficient, given that it uses a switch
2694 /// statement. It can always be overloaded in specific geometric elements,
2695 /// for efficiency reasons.
2696 //========================================================================
2698  const double &det_jacobian,
2699  const DenseMatrix<double> &jacobian,
2700  const DenseMatrix<double> &djacobian_dX,
2701  const DenseMatrix<double> &inverse_jacobian,
2702  const DShape &dpsids,
2703  RankFourTensor<double> &d_dpsidx_dX) const
2704 {
2705  // Determine the spatial dimension of the element
2706  const unsigned el_dim = dim();
2707 
2708 #ifdef PARANOID
2709  // Determine the number of nodes in the element
2710  const unsigned n_node = nnode();
2711 
2712  // Check that d_dpsidx_dX is of the correct size
2713  if(d_dpsidx_dX.nindex1()!=el_dim || d_dpsidx_dX.nindex2()!=n_node
2714  || d_dpsidx_dX.nindex3()!=n_node || d_dpsidx_dX.nindex4()!=el_dim)
2715  {
2716  std::ostringstream error_message;
2717  error_message << "d_dpsidx_dX must be of the following dimensions:"
2718  << "\nd_dpsidx_dX(el_dim,n_node,n_node,el_dim)";
2719  throw OomphLibError(error_message.str(),
2720  OOMPH_CURRENT_FUNCTION,
2721  OOMPH_EXCEPTION_LOCATION);
2722  }
2723 #endif
2724 
2725  // Call the appropriate templated function, depending on the
2726  // element dimension
2727  switch(el_dim)
2728  {
2729  case 0:
2730  d_dshape_eulerian_dnodal_coordinates_templated_helper<0>(
2731  det_jacobian,jacobian,djacobian_dX,inverse_jacobian,dpsids,d_dpsidx_dX);
2732  break;
2733  case 1:
2734  d_dshape_eulerian_dnodal_coordinates_templated_helper<1>(
2735  det_jacobian,jacobian,djacobian_dX,inverse_jacobian,dpsids,d_dpsidx_dX);
2736  break;
2737  case 2:
2738  d_dshape_eulerian_dnodal_coordinates_templated_helper<2>(
2739  det_jacobian,jacobian,djacobian_dX,inverse_jacobian,dpsids,d_dpsidx_dX);
2740  break;
2741  case 3:
2742  d_dshape_eulerian_dnodal_coordinates_templated_helper<3>(
2743  det_jacobian,jacobian,djacobian_dX,inverse_jacobian,dpsids,d_dpsidx_dX);
2744  break;
2745  // Catch-all default case: issue warning and die
2746  default:
2747  std::ostringstream error_stream;
2748  error_stream
2749  << "Dimension of the element must be 0,1,2 or 3, not " << el_dim
2750  << std::endl;
2751 
2752  throw OomphLibError(error_stream.str(),
2753  OOMPH_CURRENT_FUNCTION,
2754  OOMPH_EXCEPTION_LOCATION);
2755  }
2756 }
2757 
2758 //=====================================================================
2759 /// Convert derivatives w.r.t local coordinates to derivatives w.r.t the
2760 /// coordinates used to assemble the inverse jacobian mapping passed as
2761 /// inverse_jacobian. The derivatives passed in dbasis will be
2762 /// modified in this function from dbasisds to dbasisdX.
2763 //======================================================================
2765  &inverse_jacobian,
2766  DShape &dbasis) const
2767  {
2768  //Find the number of basis functions and basis function types
2769  const unsigned n_basis = dbasis.nindex1();
2770  const unsigned n_basis_type = dbasis.nindex2();
2771  //Find the dimension of the array (Must be the elemental dimension)
2772  const unsigned n_dim = dim();
2773 
2774  //Allocate temporary (stack) storage of the dimension of the element
2775  double new_derivatives[n_dim];
2776 
2777  //Loop over the number of basis functions
2778  for(unsigned l=0;l<n_basis;l++)
2779  {
2780  //Loop over type of basis functions
2781  for(unsigned k=0;k<n_basis_type;k++)
2782  {
2783  //Loop over the coordinates
2784  for(unsigned j=0;j<n_dim;j++)
2785  {
2786  //Zero the new transformed derivatives
2787  new_derivatives[j] = 0.0;
2788  //Do premultiplication by inverse jacobian
2789  for(unsigned i=0;i<n_dim;i++)
2790  {
2791  new_derivatives[j] += inverse_jacobian(j,i)*dbasis(l,k,i);
2792  }
2793  }
2794  //We now copy the new derivatives into the shape functions
2795  for(unsigned j=0;j<n_dim;j++) {dbasis(l,k,j) = new_derivatives[j];}
2796  }
2797  }
2798  }
2799 
2800 //=====================================================================
2801 /// Convert derivatives w.r.t local coordinates to derivatives w.r.t the
2802 /// coordinates used to assemble the inverse jacobian mapping passed as
2803 /// inverse_jacobian, assuming that the mapping is diagonal. This merely
2804 /// saves a few loops, but is probably worth it.
2805 //======================================================================
2807  &inverse_jacobian,
2808  DShape &dbasis) const
2809  {
2810  //Find the number of basis functions and basis function types
2811  const unsigned n_basis = dbasis.nindex1();
2812  const unsigned n_basis_type = dbasis.nindex2();
2813  //Find the dimension of the array (must be the elemental dimension)
2814  const unsigned n_dim = dim();
2815 
2816  //Loop over the number of basis functions
2817  for(unsigned l=0;l<n_basis;l++)
2818  {
2819  //Loop over type of basis functions
2820  for(unsigned k=0;k<n_basis_type;k++)
2821  {
2822  //Loop over the coordinates
2823  for(unsigned j=0;j<n_dim;j++)
2824  {
2825  dbasis(l,k,j) *= inverse_jacobian(j,j);
2826  }
2827  }
2828  }
2829  }
2830 
2831 //=======================================================================
2832 /// Convert derivatives and second derivatives w.r.t local coordinates to
2833 /// derivatives w.r.t. the coordinates used to assemble the jacobian,
2834 /// inverse_jacobian and jacobian 2 passed. This must be specialised
2835 /// for each dimension, otherwise it gets very ugly
2836 /// Specialisation to one dimension.
2837 //=======================================================================
2838  template<>
2839  void FiniteElement::
2840  transform_second_derivatives_template<1>(const DenseMatrix<double>
2841  &jacobian,
2842  const DenseMatrix<double>
2843  &inverse_jacobian,
2844  const DenseMatrix<double>
2845  &jacobian2,
2846  DShape &dbasis,
2847  DShape &d2basis) const
2848  {
2849  //Find the number of basis functions and basis function types
2850  const unsigned n_basis = dbasis.nindex1();
2851  const unsigned n_basis_type = dbasis.nindex2();
2852 
2853  //The second derivatives are easy, because there can be no mixed terms
2854  //Loop over number of basis functions
2855  for(unsigned l=0;l<n_basis;l++)
2856  {
2857  //Loop over number of basis function types
2858  for(unsigned k=0;k<n_basis_type;k++)
2859  {
2860  d2basis(l,k,0) = d2basis(l,k,0)/(jacobian(0,0)*jacobian(0,0))
2861  //Second term comes from taking d/dx of (dpsi/ds / dx/ds)
2862  - dbasis(l,k,0)*jacobian2(0,0)/
2863  (jacobian(0,0)*jacobian(0,0)*jacobian(0,0));
2864  }
2865  }
2866 
2867  //Assemble the first derivatives (do this last so that we don't
2868  //overwrite the dphids before we use it in the above)
2869  transform_derivatives(inverse_jacobian,dbasis);
2870 
2871  }
2872 
2873 //=======================================================================
2874 /// Convert derivatives and second derivatives w.r.t local coordinates to
2875 /// derivatives w.r.t. the coordinates used to assemble the jacobian,
2876 /// inverse_jacobian and jacobian 2 passed. This must be specialised
2877 /// for each dimension, otherwise it gets very ugly.
2878 /// Specialisation to two spatial dimensions
2879 //=======================================================================
2880  template<>
2881  void FiniteElement::
2882  transform_second_derivatives_template<2>(const DenseMatrix<double>
2883  &jacobian,
2884  const DenseMatrix<double>
2885  &inverse_jacobian,
2886  const DenseMatrix<double>
2887  &jacobian2,
2888  DShape &dbasis,
2889  DShape &d2basis) const
2890  {
2891  //Find the number of basis functions and basis function types
2892  const unsigned n_basis = dbasis.nindex1();
2893  const unsigned n_basis_type = dbasis.nindex2();
2894 
2895  //Calculate the determinant
2896  const double det = jacobian(0,0)*jacobian(1,1) - jacobian(0,1)*jacobian(1,0);
2897 
2898  //Second derivatives ... the approach taken here is to construct
2899  //dphidX/ds which can then be used to calculate the second derivatives
2900  //using the relationship d/dX = inverse_jacobian*d/ds
2901 
2902  double ddetds[2];
2903 
2904  ddetds[0] = jacobian2(0,0)*jacobian(1,1) + jacobian(0,0)*jacobian2(2,1)
2905  - jacobian2(0,1)*jacobian(1,0) - jacobian(0,1)*jacobian2(2,0);
2906  ddetds[1] = jacobian2(2,0)*jacobian(1,1) + jacobian(0,0)*jacobian2(1,1)
2907  - jacobian2(2,1)*jacobian(1,0) - jacobian(0,1)*jacobian2(1,0);
2908 
2909  //Calculate the derivatives of the inverse jacobian wrt the local coordinates
2910  double dinverse_jacobiands[2][2][2];
2911 
2912  dinverse_jacobiands[0][0][0] = jacobian2(2,1)/det -
2913  jacobian(1,1)*ddetds[0]/(det*det);
2914  dinverse_jacobiands[0][1][0] = -jacobian2(0,1)/det +
2915  jacobian(0,1)*ddetds[0]/(det*det);
2916  dinverse_jacobiands[1][0][0] = -jacobian2(2,0)/det +
2917  jacobian(1,0)*ddetds[0]/(det*det);
2918  dinverse_jacobiands[1][1][0] = jacobian2(0,0)/det -
2919  jacobian(0,0)*ddetds[0]/(det*det);
2920 
2921  dinverse_jacobiands[0][0][1] = jacobian2(1,1)/det -
2922  jacobian(1,1)*ddetds[1]/(det*det);
2923  dinverse_jacobiands[0][1][1] = -jacobian2(2,1)/det +
2924  jacobian(0,1)*ddetds[1]/(det*det);
2925  dinverse_jacobiands[1][0][1] = -jacobian2(1,0)/det +
2926  jacobian(1,0)*ddetds[1]/(det*det);
2927  dinverse_jacobiands[1][1][1] = jacobian2(2,0)/det -
2928  jacobian(0,0)*ddetds[1]/(det*det);
2929 
2930  //Set up derivatives of dpsidx wrt local coordinates
2931  DShape dphidXds0(n_basis,n_basis_type,2), dphidXds1(n_basis,n_basis_type,2);
2932 
2933  for(unsigned l=0;l<n_basis;l++)
2934  {
2935  for(unsigned k=0;k<n_basis_type;k++)
2936  {
2937  for(unsigned j=0;j<2;j++)
2938  {
2939  //Note that we can't have an inner loop because of the
2940  //convention I've chosen for the mixed derivatives!
2941  dphidXds0(l,k,j) = dinverse_jacobiands[j][0][0]*dbasis(l,k,0)
2942  + dinverse_jacobiands[j][1][0]*dbasis(l,k,1)
2943  + inverse_jacobian(j,0)*d2basis(l,k,0)
2944  + inverse_jacobian(j,1)*d2basis(l,k,2);
2945 
2946  dphidXds1(l,k,j) = dinverse_jacobiands[j][0][1]*dbasis(l,k,0)
2947  + dinverse_jacobiands[j][1][1]*dbasis(l,k,1)
2948  + inverse_jacobian(j,0)*d2basis(l,k,2)
2949  + inverse_jacobian(j,1)*d2basis(l,k,1);
2950  }
2951  }
2952  }
2953 
2954  //Now calculate the DShape d2phidX
2955  for(unsigned l=0;l<n_basis;l++)
2956  {
2957  for(unsigned k=0;k<n_basis_type;k++)
2958  {
2959  //Zero dpsidx
2960  for(unsigned j=0;j<3;j++) {d2basis(l,k,j) = 0.0;}
2961 
2962  //Do premultiplication by inverse jacobian
2963  for(unsigned i=0;i<2;i++)
2964  {
2965  d2basis(l,k,i) = inverse_jacobian(i,0)*dphidXds0(l,k,i)+
2966  inverse_jacobian(i,1)*dphidXds1(l,k,i);
2967  }
2968  //Calculate mixed derivative term
2969  d2basis(l,k,2) += inverse_jacobian(0,0)*dphidXds0(l,k,1)
2970  + inverse_jacobian(0,1)*dphidXds1(l,k,1);
2971  }
2972  }
2973 
2974  //Assemble the first derivatives second, so that we don't
2975  //overwrite dphids
2976  transform_derivatives(inverse_jacobian,dbasis);
2977  }
2978 
2979 
2980 //=======================================================================
2981 /// Convert derivatives and second derivatives w.r.t local coordinates to
2982 /// derivatives w.r.t. the coordinates used to assemble the jacobian,
2983 /// inverse_jacobian and jacobian 2 passed. This must be specialised
2984 /// for each dimension, otherwise it gets very ugly
2985 /// Specialisation to one dimension.
2986 //=======================================================================
2987  template<>
2988  void FiniteElement::
2989  transform_second_derivatives_diagonal<1>(const DenseMatrix<double>
2990  &jacobian,
2991  const DenseMatrix<double>
2992  &inverse_jacobian,
2993  const DenseMatrix<double>
2994  &jacobian2,
2995  DShape &dbasis,
2996  DShape &d2basis) const
2997  {
2998  FiniteElement::
2999  transform_second_derivatives_template<1>(jacobian,
3000  inverse_jacobian,
3001  jacobian2,dbasis,d2basis);
3002  }
3003 
3004 
3005 //=========================================================================
3006 /// Convert second derivatives w.r.t. local coordinates to
3007 /// second derivatives w.r.t. the coordinates passed in the tensor
3008 /// coordinate. Specialised to two spatial dimension
3009 //=========================================================================
3010  template<>
3011  void FiniteElement::
3012  transform_second_derivatives_diagonal<2>(const DenseMatrix<double>
3013  &jacobian,
3014  const DenseMatrix<double>
3015  &inverse_jacobian,
3016  const DenseMatrix<double>
3017  &jacobian2,
3018  DShape &dbasis,
3019  DShape &d2basis) const
3020  {
3021  //Find the number of basis functions and basis function types
3022  const unsigned n_basis = dbasis.nindex1();
3023  const unsigned n_basis_type = dbasis.nindex2();
3024 
3025  //Again we assume that there are no cross terms and that coordinate
3026  //i depends only upon local coordinate i
3027 
3028  //Now calculate the DShape d2phidx
3029  for(unsigned l=0;l<n_basis;l++)
3030  {
3031  for(unsigned k=0;k<n_basis_type;k++)
3032  {
3033  //Second derivatives
3034  d2basis(l,k,0) = d2basis(l,k,0)/(jacobian(0,0)*jacobian(0,0))
3035  - dbasis(l,k,0)*jacobian2(0,0)/
3036  (jacobian(0,0)*jacobian(0,0)*jacobian(0,0));
3037 
3038  d2basis(l,k,1) = d2basis(l,k,1)/(jacobian(1,1)*jacobian(1,1))
3039  - dbasis(l,k,1)*jacobian2(1,1)/
3040  (jacobian(1,1)*jacobian(1,1)*jacobian(1,1));
3041 
3042  d2basis(l,k,2) = d2basis(l,k,2)/(jacobian(0,0)*jacobian(1,1));
3043  }
3044  }
3045 
3046 
3047  //Assemble the first derivatives
3048  transform_derivatives_diagonal(inverse_jacobian,dbasis);
3049  }
3050 
3051 
3052 //=============================================================================
3053 /// \short Convert derivatives and second derivatives w.r.t. local coordiantes
3054 /// to derivatives and second derivatives w.r.t. the coordinates used to
3055 /// assemble the jacobian, inverse jacobian and jacobian2 passed to the
3056 /// function. This is a template-free general interface, that should be
3057 /// overloaded for efficiency
3058 //============================================================================
3060  &jacobian,
3061  const DenseMatrix<double>
3062  &inverse_jacobian,
3063  const DenseMatrix<double>
3064  &jacobian2,
3065  DShape &dbasis,
3066  DShape &d2basis) const
3067  {
3068  //Find the dimension of the element
3069  const unsigned el_dim = dim();
3070  //Choose the appropriate function based on the dimension of the element
3071  switch(el_dim)
3072  {
3073  case 1:
3074  transform_second_derivatives_template<1>(jacobian,
3075  inverse_jacobian,jacobian2,
3076  dbasis,d2basis);
3077  break;
3078  case 2:
3079  transform_second_derivatives_template<2>(jacobian,
3080  inverse_jacobian,jacobian2,
3081  dbasis,d2basis);
3082  break;
3083 
3084  case 3:
3085  throw OomphLibError("Not implemented yet ... maybe one day",
3086  OOMPH_CURRENT_FUNCTION,
3087  OOMPH_EXCEPTION_LOCATION);
3088 
3089  //transform_second_derivatives_template<3>(dphids,d2phids,jacobian,
3090  // inverse_jacobian,jacobian2,
3091  // dphidX,d2phidX);
3092  break;
3093  //Catch-all default case: issue warning and die
3094  default:
3095  std::ostringstream error_stream;
3096  error_stream
3097  << "Dimension of the element must be 1,2 or 3, not " << el_dim
3098  << std::endl;
3099 
3100  throw OomphLibError(error_stream.str(),
3101  OOMPH_CURRENT_FUNCTION,
3102  OOMPH_EXCEPTION_LOCATION);
3103  }
3104  }
3105 
3106 
3107 //======================================================================
3108 /// \short The destructor cleans up the memory allocated
3109 /// for storage of pointers to nodes. Internal and external data get
3110 /// wiped by the GeneralisedElement destructor; nodes get
3111 /// killed in mesh destructor.
3112 //=======================================================================
3114  {
3115  //Delete the storage allocated for the pointers to the loca nodes
3116  delete[] Node_pt;
3117 
3118  //Delete the storage allocated for the nodal numbering schemes
3119  if(Nodal_local_eqn)
3120  {
3121  delete[] Nodal_local_eqn[0];
3122  delete[] Nodal_local_eqn;
3123  }
3124  }
3125 
3126 
3127  //==============================================================
3128  /// Get the local fraction of the node j in the element;
3129  /// vector sets its own size
3130  //==============================================================
3131  void FiniteElement::local_fraction_of_node(const unsigned &j,
3132  Vector<double> &s_fraction)
3133  {
3134  //Default implementation is rather dumb
3135  //Get the local coordinate and scale by local coordinate range
3136  local_coordinate_of_node(j,s_fraction);
3137  unsigned n_coordinates = s_fraction.size();
3138  for(unsigned i=0;i<n_coordinates;i++)
3139  {
3140  s_fraction[i] = (s_fraction[i] - s_min())/(s_max() - s_min());
3141  }
3142  }
3143 
3144 //=======================================================================
3145 /// Set the spatial integration scheme and also calculate the values of the
3146 /// shape functions and their derivatives w.r.t. the local coordinates,
3147 /// placing the values into storage so that they may be re-used,
3148 /// without recalculation
3149 //=======================================================================
3151  {
3152  //Assign the integration scheme
3153  Integral_pt = integral_pt;
3154  }
3155 
3156 //=========================================================================
3157 /// \short Return the shape function stored at the ipt-th integration
3158 /// point.
3159 //=========================================================================
3160  void FiniteElement::shape_at_knot(const unsigned &ipt, Shape &psi) const
3161  {
3162  //Find the dimension of the element
3163  const unsigned el_dim = dim();
3164  //Storage for the local coordinates of the integration point
3165  Vector<double> s(el_dim);
3166  //Set the local coordinate
3167  for(unsigned i=0;i<el_dim;i++) {s[i] = integral_pt()->knot(ipt,i);}
3168  //Get the shape function
3169  shape(s,psi);
3170  }
3171 
3172 //=========================================================================
3173 /// \short Return the shape function and its derivatives w.r.t. the local
3174 /// coordinates at the ipt-th integration point.
3175 //=========================================================================
3176  void FiniteElement::dshape_local_at_knot(const unsigned &ipt, Shape &psi,
3177  DShape &dpsids) const
3178  {
3179  //Find the dimension of the element
3180  const unsigned el_dim = dim();
3181  //Storage for the local coordinates of the integration point
3182  Vector<double> s(el_dim);
3183  //Set the local coordinate
3184  for(unsigned i=0;i<el_dim;i++) {s[i] = integral_pt()->knot(ipt,i);}
3185  //Get the shape function and derivatives
3186  dshape_local(s,psi,dpsids);
3187  }
3188 
3189 //=========================================================================
3190 /// Calculate the shape function and its first and second derivatives
3191 /// w.r.t. local coordinates at the ipt-th integration point.
3192 /// Numbering:
3193 /// \b 1D:
3194 /// d2psids(i,0) = \f$ d^2 \psi_j / d s^2 \f$
3195 /// \b 2D:
3196 /// d2psids(i,0) = \f$ \partial^2 \psi_j / \partial s_0^2 \f$
3197 /// d2psids(i,1) = \f$ \partial^2 \psi_j / \partial s_1^2 \f$
3198 /// d2psids(i,2) = \f$ \partial^2 \psi_j / \partial s_0 \partial s_1 \f$
3199 /// \b 3D:
3200 /// d2psids(i,0) = \f$ \partial^2 \psi_j / \partial s_0^2 \f$
3201 /// d2psids(i,1) = \f$ \partial^2 \psi_j / \partial s_1^2 \f$
3202 /// d2psids(i,2) = \f$ \partial^2 \psi_j / \partial s_2^2 \f$
3203 /// d2psids(i,3) = \f$ \partial^2 \psi_j / \partial s_0 \partial s_1 \f$
3204 /// d2psids(i,4) = \f$ \partial^2 \psi_j / \partial s_0 \partial s_2 \f$
3205 /// d2psids(i,5) = \f$ \partial^2 \psi_j / \partial s_1 \partial s_2 \f$
3206 //=========================================================================
3207  void FiniteElement::d2shape_local_at_knot(const unsigned &ipt, Shape &psi,
3208  DShape &dpsids, DShape &d2psids)
3209  const
3210  {
3211  //Find the dimension of the element
3212  const unsigned el_dim = dim();
3213  //Storage for the local coordinates of the integration point
3214  Vector<double> s(el_dim);
3215  //Set the local coordinate
3216  for(unsigned i=0;i<el_dim;i++) {s[i] = integral_pt()->knot(ipt,i);}
3217  //Get the shape function and first and second derivatives
3218  d2shape_local(s,psi,dpsids,d2psids);
3219  }
3220 
3221 //=========================================================================
3222 /// \short Compute the geometric shape functions and also
3223 /// first derivatives w.r.t. global coordinates at local coordinate s;
3224 /// Returns Jacobian of mapping from global to local coordinates.
3225 /// Most general form of the function, but may be over-loaded, if desired
3226 //=========================================================================
3228  Shape &psi,
3229  DShape &dpsi) const
3230  {
3231  //Find the element dimension
3232  const unsigned el_dim = dim();
3233 
3234  //Get the values of the shape functions and their local derivatives
3235  //Temporarily stored in dpsi
3236  dshape_local(s,psi,dpsi);
3237 
3238  //Allocate memory for the inverse jacobian
3239  DenseMatrix<double> inverse_jacobian(el_dim);
3240  //Now calculate the inverse jacobian
3241  const double det = local_to_eulerian_mapping(dpsi,inverse_jacobian);
3242 
3243  //Now set the values of the derivatives to be dpsidx
3244  transform_derivatives(inverse_jacobian,dpsi);
3245  //Return the determinant of the jacobian
3246  return det;
3247  }
3248 
3249 //========================================================================
3250 /// \short Compute the geometric shape functions and also first
3251 /// derivatives w.r.t. global coordinates at integration point ipt.
3252 /// Most general form of function, but may be over-loaded if desired
3253 //========================================================================
3254  double FiniteElement::dshape_eulerian_at_knot(const unsigned &ipt,
3255  Shape &psi,
3256  DShape &dpsi) const
3257  {
3258  //Find the element dimension
3259  const unsigned el_dim = dim();
3260 
3261  //Get the values of the shape function and local derivatives
3262  //Temporarily store it in dpsi
3263  dshape_local_at_knot(ipt,psi,dpsi);
3264 
3265  //Allocate memory for the inverse jacobian
3266  DenseMatrix<double> inverse_jacobian(el_dim);
3267  //Now calculate the inverse jacobian
3268  const double det = local_to_eulerian_mapping(dpsi,inverse_jacobian);
3269 
3270  //Now set the values of the derivatives to dpsidx
3271  transform_derivatives(inverse_jacobian,dpsi);
3272  //Return the determinant of the jacobian
3273  return det;
3274  }
3275 
3276 
3277 //========================================================================
3278 /// \short Compute the geometric shape functions (psi) at integration point
3279 /// ipt. Return the determinant of the jacobian of the mapping (detJ).
3280 /// Additionally calculate the derivatives of "detJ" w.r.t. the
3281 /// nodal coordinates.
3282 //========================================================================
3284  const unsigned &ipt,
3285  Shape &psi,
3286  DenseMatrix<double> &djacobian_dX) const
3287  {
3288  // Find the element dimension
3289  const unsigned el_dim = dim();
3290 
3291  // Get the values of the shape function and local derivatives
3292  unsigned nnod=nnode();
3293  DShape dpsi(nnod,el_dim);
3294  dshape_local_at_knot(ipt,psi,dpsi);
3295 
3296  // Allocate memory for the jacobian and the inverse of the jacobian
3297  DenseMatrix<double> jacobian(el_dim), inverse_jacobian(el_dim);
3298 
3299  // Now calculate the inverse jacobian
3300  const double det = local_to_eulerian_mapping(dpsi,jacobian,inverse_jacobian);
3301 
3302  // Calculate the derivative of the jacobian w.r.t. nodal coordinates
3303  // Note: must call this before "transform_derivatives(...)" since this
3304  // function requires dpsids rather than dpsidx
3305  dJ_eulerian_dnodal_coordinates(jacobian,dpsi,djacobian_dX);
3306 
3307  // Return the determinant of the jacobian
3308  return det;
3309  }
3310 
3311 
3312 //========================================================================
3313 /// \short Compute the geometric shape functions (psi) and first
3314 /// derivatives w.r.t. global coordinates (dpsidx) at integration point
3315 /// ipt. Return the determinant of the jacobian of the mapping (detJ).
3316 /// Additionally calculate the derivatives of both "detJ" and "dpsidx"
3317 /// w.r.t. the nodal coordinates.
3318 /// Most general form of function, but may be over-loaded if desired.
3319 //========================================================================
3321  const unsigned &ipt,
3322  Shape &psi,
3323  DShape &dpsi,
3324  DenseMatrix<double> &djacobian_dX,
3325  RankFourTensor<double> &d_dpsidx_dX) const
3326  {
3327  // Find the element dimension
3328  const unsigned el_dim = dim();
3329 
3330  // Get the values of the shape function and local derivatives
3331  // Temporarily store in dpsi
3332  dshape_local_at_knot(ipt,psi,dpsi);
3333 
3334  // Allocate memory for the jacobian and the inverse of the jacobian
3335  DenseMatrix<double> jacobian(el_dim), inverse_jacobian(el_dim);
3336 
3337  // Now calculate the inverse jacobian
3338  const double det = local_to_eulerian_mapping(dpsi,jacobian,inverse_jacobian);
3339 
3340  // Calculate the derivative of the jacobian w.r.t. nodal coordinates
3341  // Note: must call this before "transform_derivatives(...)" since this
3342  // function requires dpsids rather than dpsidx
3343  dJ_eulerian_dnodal_coordinates(jacobian,dpsi,djacobian_dX);
3344 
3345  // Calculate the derivative of dpsidx w.r.t. nodal coordinates
3346  // Note: this function also requires dpsids rather than dpsidx
3347  d_dshape_eulerian_dnodal_coordinates(det,jacobian,djacobian_dX,
3348  inverse_jacobian,dpsi,d_dpsidx_dX);
3349 
3350  // Now set the values of the derivatives to dpsidx
3351  transform_derivatives(inverse_jacobian,dpsi);
3352 
3353  // Return the determinant of the jacobian
3354  return det;
3355  }
3356 
3357 
3358 
3359 //===========================================================================
3360 /// \short Compute the geometric shape functions and also first
3361 /// and second derivatives w.r.t. global coordinates at local coordinate s;
3362 /// Also returns Jacobian of mapping from global to local coordinates.
3363 /// Numbering:
3364 ///\b 1D:
3365 /// d2psidx(i,0) = \f$ d^2 \psi_j / d x^2 \f$
3366 /// \b 2D:
3367 /// d2psidx(i,0) = \f$ \partial^2 \psi_j / \partial x_0^2 \f$
3368 /// d2psidx(i,1) = \f$ \partial^2 \psi_j / \partial x_1^2 \f$
3369 /// d2psidx(i,2) = \f$ \partial^2 \psi_j / \partial x_0 \partial x_1 \f$
3370 /// \b 3D:
3371 /// d2psidx(i,0) = \f$ \partial^2 \psi_j / \partial x_0^2 \f$
3372 /// d2psidx(i,1) = \f$ \partial^2 \psi_j / \partial x_1^2 \f$
3373 /// d2psidx(i,2) = \f$ \partial^2 \psi_j / \partial x_2^2 \f$
3374 /// d2psidx(i,3) = \f$ \partial^2 \psi_j / \partial x_0 \partial x_1 \f$
3375 /// d2psidx(i,4) = \f$ \partial^2 \psi_j / \partial x_0 \partial x_2 \f$
3376 /// d2psidx(i,5) = \f$ \partial^2 \psi_j / \partial x_1 \partial x_2 \f$
3377 //===========================================================================
3379  Shape &psi,
3380  DShape &dpsi,
3381  DShape &d2psi) const
3382  {
3383  //Find the values of the indices of the shape functions
3384  //Locally cached.
3385  //Find the element dimension
3386  const unsigned el_dim = dim();
3387  //Find the number of second derivatives required
3388  const unsigned n_deriv = N2deriv[el_dim];
3389 
3390  //Get the values of the shape function and local derivatives
3391  d2shape_local(s,psi,dpsi,d2psi);
3392 
3393  //Allocate memory for the jacobian and inverse jacobian
3394  DenseMatrix<double> jacobian(el_dim), inverse_jacobian(el_dim);
3395  //Calculate the jacobian and inverse jacobian
3396  const double det =
3397  local_to_eulerian_mapping(dpsi,jacobian,inverse_jacobian);
3398 
3399  //Allocate memory for the jacobian of second derivatives
3400  DenseMatrix<double> jacobian2(n_deriv,el_dim);
3401  //Assemble the jacobian of second derivatives
3402  assemble_local_to_eulerian_jacobian2(d2psi,jacobian2);
3403 
3404  //Now set the value of the derivatives
3405  transform_second_derivatives(jacobian,inverse_jacobian,
3406  jacobian2,dpsi,d2psi);
3407  //Return the determinant of the mapping
3408  return det;
3409  }
3410 
3411 //===========================================================================
3412 /// \short Compute the geometric shape functions and also first
3413 /// and second derivatives w.r.t. global coordinates at ipt-th integration
3414 /// point
3415 /// Returns Jacobian of mapping from global to local coordinates.
3416 /// This is the most general version, may be overloaded, if desired.
3417 /// Numbering:
3418 /// \b 1D:
3419 /// d2psidx(i,0) = \f$ d^2 \psi_j / d x^2 \f$
3420 /// \b 2D:
3421 /// d2psidx(i,0) = \f$ \partial^2 \psi_j / \partial x_0^2 \f$
3422 /// d2psidx(i,1) = \f$ \partial^2 \psi_j / \partial x_1^2 \f$
3423 /// d2psidx(i,2) = \f$ \partial^2 \psi_j / \partial x_0 \partial x_1 \f$
3424 /// \b 3D:
3425 /// d2psidx(i,0) = \f$ \partial^2 \psi_j / \partial x_0^2 \f$
3426 /// d2psidx(i,1) = \f$ \partial^2 \psi_j / \partial x_1^2 \f$
3427 /// d2psidx(i,2) = \f$ \partial^2 \psi_j / \partial x_2^2 \f$
3428 /// d2psidx(i,3) = \f$ \partial^2 \psi_j / \partial x_0 \partial x_1 \f$
3429 /// d2psidx(i,4) = \f$ \partial^2 \psi_j / \partial x_0 \partial x_2 \f$
3430 /// d2psidx(i,5) = \f$ \partial^2 \psi_j / \partial x_1 \partial x_2 \f$
3431 //==========================================================================
3432  double FiniteElement::d2shape_eulerian_at_knot(const unsigned &ipt,
3433  Shape &psi,
3434  DShape &dpsi,
3435  DShape &d2psi) const
3436  {
3437  //Find the values of the indices of the shape functions
3438  //Locally cached
3439  //Find the element dimension
3440  const unsigned el_dim = dim();
3441  //Find the number of second derivatives required
3442  const unsigned n_deriv = N2deriv[el_dim];
3443 
3444  //Get the values of the shape function and local derivatives
3445  d2shape_local_at_knot(ipt,psi,dpsi,d2psi);
3446 
3447  //Allocate memory for the jacobian and inverse jacobian
3448  DenseMatrix<double> jacobian(el_dim), inverse_jacobian(el_dim);
3449  //Calculate the jacobian and inverse jacobian
3450  const double det =
3451  local_to_eulerian_mapping(dpsi,jacobian,inverse_jacobian);
3452 
3453  //Allocate memory for the jacobian of second derivatives
3454  DenseMatrix<double> jacobian2(n_deriv,el_dim);
3455  //Assemble the jacobian of second derivatives
3456  assemble_local_to_eulerian_jacobian2(d2psi,jacobian2);
3457 
3458  //Now set the value of the derivatives
3459  transform_second_derivatives(jacobian,inverse_jacobian,
3460  jacobian2,dpsi,d2psi);
3461  //Return the determinant of the mapping
3462  return det;
3463  }
3464 
3465 
3466 
3467 //==========================================================================
3468 /// This function loops over the nodal data of the element, adds the
3469 /// GLOBAL equation numbers to the local-to-global look-up scheme and
3470 /// fills in the Nodal_local_eqn look-up scheme for the local equation
3471 /// numbers
3472 /// If the boolean argument is true then pointers to the dofs will be
3473 /// stored in Dof_pt
3474 //==========================================================================
3476  const bool &store_local_dof_pt)
3477  {
3478  //Find the number of nodes
3479  const unsigned n_node = nnode();
3480  //If there are nodes
3481  if(n_node > 0)
3482  {
3483  //Find the number of local equations assigned so far
3484  unsigned local_eqn_number = ndof();
3485 
3486  //We need to find the total number of values stored at the node
3487  //Initialise to the number of values stored at the first node
3488  unsigned n_total_values = node_pt(0)->nvalue();
3489  //Loop over the other nodes and add the values stored
3490  for(unsigned n=1;n<n_node;n++)
3491  {n_total_values += node_pt(n)->nvalue();}
3492 
3493  //If allocated delete the old storage
3494  if(Nodal_local_eqn)
3495  {
3496  delete[] Nodal_local_eqn[0];
3497  delete[] Nodal_local_eqn;
3498  }
3499 
3500  //If there are no values, we are done, null out the storage and
3501  //return
3502  if(n_total_values==0) {Nodal_local_eqn=0; return;}
3503 
3504  //Resize the storage for the nodal local equation numbers
3505  //Firstly allocate pointers to rows for each node
3506  Nodal_local_eqn = new int*[n_node];
3507  //Now allocate storage for the equation numbers
3508  Nodal_local_eqn[0] = new int[n_total_values];
3509  //initially all local equations are unclassified
3510  for(unsigned i=0;i<n_total_values;i++)
3511  {Nodal_local_eqn[0][i] = Data::Is_unclassified;}
3512 
3513  //Loop over the remaining rows and set their pointers
3514  for(unsigned n=1;n<n_node;++n)
3515  {
3516  //Initially set the pointer to the i-th row to the pointer
3517  //to the i-1th row
3518  Nodal_local_eqn[n] = Nodal_local_eqn[n-1];
3519  //Now increase the row pointer by the number of values
3520  //stored at the i-1th node
3521  Nodal_local_eqn[n] += Node_pt[n-1]->nvalue();
3522  }
3523 
3524 
3525  //A local queue to store the global equation numbers
3526  std::deque<unsigned long> global_eqn_number_queue;
3527 
3528  //Now loop over the nodes again and assign local equation numbers
3529  for(unsigned n=0;n<n_node;n++)
3530  {
3531  //Pointer to node
3532  Node* const nod_pt = node_pt(n);
3533 
3534  //Find the number of values stored at the node
3535  unsigned n_value = nod_pt->nvalue();
3536 
3537  //Loop over the number of values
3538  for(unsigned j=0;j<n_value;j++)
3539  {
3540  //Get the GLOBAL equation number
3541  long eqn_number = nod_pt->eqn_number(j);
3542  //If the GLOBAL equation number is positive (a free variable)
3543  if(eqn_number >= 0)
3544  {
3545  //Add the GLOBAL equation number to the queue
3546  global_eqn_number_queue.push_back(eqn_number);
3547  //Add pointer to the dof to the queue if required
3548  if(store_local_dof_pt)
3549  {
3550  GeneralisedElement::Dof_pt_deque.push_back(
3551  nod_pt->value_pt(j));
3552  }
3553  //Add the local equation number to the local scheme
3554  Nodal_local_eqn[n][j] = local_eqn_number;
3555  //Increase the local number
3556  local_eqn_number++;
3557  }
3558  else
3559  {
3560  //Set the local scheme to be pinned
3561  Nodal_local_eqn[n][j] = Data::Is_pinned;
3562  }
3563  }
3564  }
3565 
3566  //Now add our global equations numbers to the internal element storage
3567  add_global_eqn_numbers(global_eqn_number_queue,
3568  GeneralisedElement::Dof_pt_deque);
3569  //Clear the memory used in the deque
3570  if(store_local_dof_pt)
3571  {std::deque<double*>().swap(GeneralisedElement::Dof_pt_deque);}
3572  }
3573  }
3574 
3575 
3576 //============================================================================
3577 /// This function calculates the entries of Jacobian matrix, used in
3578 /// the Newton method, associated with the nodal degrees of freedom.
3579 /// It does this using finite differences,
3580 /// rather than an analytical formulation, so can be done in total generality.
3581 //==========================================================================
3582  void FiniteElement::
3584  DenseMatrix<double> &jacobian)
3585  {
3586  //Find the number of nodes
3587  const unsigned n_node = nnode();
3588  //If there aren't any nodes, then return straight awayy
3589  if(n_node == 0) {return;}
3590 
3591  //Call the update function to ensure that the element is in
3592  //a consistent state before finite differencing starts
3593  update_before_nodal_fd();
3594 
3595  //Find the number of dofs in the element
3596  const unsigned n_dof = ndof();
3597  //Create newres vector
3598  Vector<double> newres(n_dof);
3599 
3600  //Integer storage for local unknown
3601  int local_unknown=0;
3602 
3603  //Use the default finite difference step
3604  const double fd_step = Default_fd_jacobian_step;
3605 
3606  //Loop over the nodes
3607  for(unsigned n=0;n<n_node;n++)
3608  {
3609  //Get the number of values stored at the node
3610  const unsigned n_value = node_pt(n)->nvalue();
3611 
3612  //Loop over the number of values
3613  for(unsigned i=0;i<n_value;i++)
3614  {
3615  //Get the local equation number
3616  local_unknown = nodal_local_eqn(n,i);
3617  //If it's not pinned
3618  if(local_unknown >= 0)
3619  {
3620  //Store a pointer to the nodal data value
3621  double* const value_pt = node_pt(n)->value_pt(i);
3622 
3623  //Save the old value of the Nodal data
3624  const double old_var = *value_pt;
3625 
3626  //Increment the value of the Nodal data
3627  *value_pt += fd_step;
3628 
3629  //Now update any slaved variables
3630  update_in_nodal_fd(i);
3631 
3632  //Calculate the new residuals
3633  get_residuals(newres);
3634 
3635  //Do finite differences
3636  for(unsigned m=0;m<n_dof;m++)
3637  {
3638  double sum = (newres[m] - residuals[m])/fd_step;
3639  //Stick the entry into the Jacobian matrix
3640  jacobian(m,local_unknown) = sum;
3641  }
3642 
3643  //Reset the Nodal data
3644  *value_pt = old_var;
3645 
3646  //Reset any slaved variables
3647  reset_in_nodal_fd(i);
3648  }
3649  }
3650  }
3651 
3652  //End of finite difference loop
3653  //Final reset of any slaved data
3654  reset_after_nodal_fd();
3655  }
3656 
3657 
3658 
3659 
3660 //=======================================================================
3661 /// Compute derivatives of elemental residual vector with respect
3662 /// to nodal coordinates. Default implementation by FD can be overwritten
3663 /// for specific elements.
3664 /// dresidual_dnodal_coordinates(l,i,j) = d res(l) / dX_{ij}
3665 ////=======================================================================
3667  RankThreeTensor<double>& dresidual_dnodal_coordinates)
3668 {
3669  // Number of nodes
3670  unsigned n_nod=nnode();
3671 
3672  // If the element has no nodes (why??!!) return straightaway
3673  if (n_nod==0) return;
3674 
3675  // Get dimension from first node
3676  unsigned dim_nod=node_pt(0)->ndim();
3677 
3678  // Number of dofs
3679  unsigned n_dof=ndof();
3680 
3681  // Get reference residual
3682  Vector<double> res(n_dof);
3683  Vector<double> res_pls(n_dof);
3684  get_residuals(res);
3685 
3686  // FD step
3688 
3689  // Do FD loop
3690  for (unsigned j=0;j<n_nod;j++)
3691  {
3692  // Get node
3693  Node* nod_pt=node_pt(j);
3694 
3695  // Loop over coordinate directions
3696  for (unsigned i=0;i<dim_nod;i++)
3697  {
3698  // Make backup
3699  double backup=nod_pt->x(i);
3700 
3701  // Do FD step. No node update required as we're
3702  // attacking the coordinate directly...
3703  nod_pt->x(i)+=eps_fd;
3704 
3705  // Perform auxiliary node update function
3707 
3708  // Get advanced residual
3709  get_residuals(res_pls);
3710 
3711  // Fill in FD entries [Loop order is "wrong" here as l is the
3712  // slow index but this is in a function that's costly anyway
3713  // and gives us the fastest loop outside where these tensor
3714  // is actually used.]
3715  for (unsigned l=0;l<n_dof;l++)
3716  {
3717  dresidual_dnodal_coordinates(l,i,j)=(res_pls[l]-res[l])/eps_fd;
3718  }
3719 
3720  // Reset coordinate. No node update required as we're
3721  // attacking the coordinate directly...
3722  nod_pt->x(i)=backup;
3723 
3724  // Perform auxiliary node update function
3726 
3727  }
3728  }
3729 
3730  }
3731 
3732 
3733 //===============================================================
3734 /// Return the number of the node located at *node_pt
3735 /// if this node is in the element, else return \f$ -1 \f$
3736 //===============================================================
3737  int FiniteElement::get_node_number(Node* const &global_node_pt) const
3738  {
3739  //Initialise the number to -1
3740  int number = -1;
3741  //Find the number of nodes
3742  unsigned n_node = nnode();
3743 #ifdef PARANOID
3744  {
3745  //Error check that node does not appear in element more than once
3746  unsigned count=0;
3747  //Storage for the local node numbers of the element
3748  std::vector<int> local_node_number;
3749  //Loop over the nodes
3750  for(unsigned i=0;i<n_node;i++)
3751  {
3752  //If the node is present increase the counter
3753  //and store the local node number
3754  if(node_pt(i)==global_node_pt)
3755  {
3756  ++count;
3757  local_node_number.push_back(i);
3758  }
3759  }
3760 
3761  //If the node appears more than once, complain
3762  if(count > 1)
3763  {
3764  std::ostringstream error_stream;
3765  error_stream << "Node " << global_node_pt << " appears "
3766  << count << " times in an element." << std::endl
3767  << "In positions: ";
3768  for(std::vector<int>::iterator it=local_node_number.begin();
3769  it!=local_node_number.end();++it)
3770  {
3771  error_stream << *it << " ";
3772  }
3773  error_stream << std::endl
3774  << "That seems very odd." << std::endl;
3775 
3776  throw OomphLibError(error_stream.str(),
3777  OOMPH_CURRENT_FUNCTION,
3778  OOMPH_EXCEPTION_LOCATION);
3779  }
3780  }
3781 #endif
3782 
3783  //Loop over the nodes
3784  for(unsigned i=0;i<n_node;i++)
3785  {
3786  //If the passed node pointer is present in the element
3787  //set number to be its local node number
3788  if(node_pt(i)==global_node_pt)
3789  {
3790  number=i;
3791  break;
3792  }
3793  }
3794 
3795  //Return the node number
3796  return number;
3797  }
3798 
3799 
3800  //==========================================================================
3801  /// \short If there is a node at the local coordinate, s, return the pointer
3802  /// to the node. If not return 0. Note that this is a default, brute
3803  /// force implementation, can almost certainly be made more efficient for
3804  /// specific elements.
3805  //==========================================================================
3807  {
3808  //Locally cache the tolerance
3809  const double tol = Node_location_tolerance;
3810  Vector<double> s_node;
3811  //Locally cache the member data
3812  const unsigned el_dim = Elemental_dimension;
3813  const unsigned n_node = Nnode;
3814  //Loop over the nodes
3815  for(unsigned n=0;n<n_node;n++)
3816  {
3817  bool Match = true;
3818  //Find the local coordinate of the node
3819  local_coordinate_of_node(n,s_node);
3820  for(unsigned i=0;i<el_dim;i++)
3821  {
3822  //Calculate the difference between coordinates
3823  //and if it's bigger than our tolerance
3824  //break out of the (inner)loop
3825  if(std::fabs(s[i] - s_node[i]) > tol)
3826  {
3827  Match = false;
3828  break;
3829  }
3830  }
3831  //If we haven't complained then we have a match
3832  if(Match) {return node_pt(n);}
3833  }
3834  //If we get here, we have no match
3835  return 0;
3836  }
3837 
3838 //======================================================================
3839 /// Compute centre of gravity of all nodes and radius of node that
3840 /// is furthest from it. Used to assess approximately if a point
3841 /// is likely to be contained with an element in locate_zeta-like
3842 /// operations. NOTE: All computed in terms of zeta!
3843 //======================================================================
3845  Vector<double>& cog, double& max_radius) const
3846  {
3847  // Initialise
3848  cog.resize(Elemental_dimension);
3849  max_radius=0.0;
3850 
3851  // Get cog
3852  unsigned nnod=nnode();
3853  for (unsigned j=0;j<nnod;j++)
3854  {
3855  for (unsigned i=0;i<Elemental_dimension;i++)
3856  {
3857  cog[i]+=zeta_nodal(j,0,i);
3858  }
3859  }
3860  for (unsigned i=0;i<Elemental_dimension;i++)
3861  {
3862  cog[i]/=double(nnod);
3863  }
3864 
3865  // Get max distance
3866  for (unsigned j=0;j<nnod;j++)
3867  {
3868  double dist_squared=0.0;
3869  for (unsigned i=0;i<Elemental_dimension;i++)
3870  {
3871  dist_squared+=(cog[i]-zeta_nodal(j,0,i))*(cog[i]-zeta_nodal(j,0,i));
3872  }
3873  if (dist_squared>max_radius) max_radius=dist_squared;
3874  }
3875  max_radius=sqrt(max_radius);
3876  }
3877 
3878 //======================================================================
3879 /// Return FE interpolated coordinate x[i] at local coordinate s
3880 //======================================================================
3882  const unsigned &i) const
3883  {
3884  //Find the number of nodes
3885  const unsigned n_node = nnode();
3886  //Find the number of positional types
3887  const unsigned n_position_type = nnodal_position_type();
3888  //Assign storage for the local shape function
3889  Shape psi(n_node,n_position_type);
3890  //Find the values of shape function
3891  shape(s,psi);
3892 
3893  //Initialise value of x
3894  double interpolated_x = 0.0;
3895  //Loop over the local nodes
3896  for(unsigned l=0;l<n_node;l++)
3897  {
3898  //Loop over the number of dofs
3899  for(unsigned k=0;k<n_position_type;k++)
3900  {
3901  interpolated_x += nodal_position_gen(l,k,i)*psi(l,k);
3902  }
3903  }
3904 
3905  return(interpolated_x);
3906  }
3907 
3908 //=========================================================================
3909 /// Return FE interpolated coordinate x[i] at local coordinate s
3910 /// at previous timestep t (t=0: present; t>0: previous timestep)
3911 //========================================================================
3912  double FiniteElement::interpolated_x(const unsigned &t,
3913  const Vector<double> &s,
3914  const unsigned &i) const
3915  {
3916  //Find the number of nodes
3917  const unsigned n_node = nnode();
3918  //Find the number of positional types
3919  const unsigned n_position_type = nnodal_position_type();
3920 
3921  //Assign storage for the local shape function
3922  Shape psi(n_node,n_position_type);
3923  //Find the values of shape function
3924  shape(s,psi);
3925 
3926  //Initialise value of x
3927  double interpolated_x = 0.0;
3928  //Loop over the local nodes
3929  for(unsigned l=0;l<n_node;l++)
3930  {
3931  //Loop over the number of dofs
3932  for(unsigned k=0;k<n_position_type;k++)
3933  {
3934  interpolated_x += nodal_position_gen(t,l,k,i)*psi(l,k);
3935  }
3936  }
3937 
3938  return(interpolated_x);
3939  }
3940 
3941 //=======================================================================
3942 /// Return FE interpolated position x[] at local coordinate s as Vector
3943 //=======================================================================
3945  const
3946  {
3947  //Find the number of nodes
3948  const unsigned n_node = nnode();
3949  //Find the number of positional types
3950  const unsigned n_position_type = nnodal_position_type();
3951  //Find the dimension stored in the node
3952  const unsigned nodal_dim = nodal_dimension();
3953 
3954  //Assign storage for the local shape function
3955  Shape psi(n_node,n_position_type);
3956  //Find the values of shape function
3957  shape(s,psi);
3958 
3959  //Loop over the dimensions
3960  for(unsigned i=0;i<nodal_dim;i++)
3961  {
3962  //Initilialise value of x[i] to zero
3963  x[i] = 0.0;
3964  //Loop over the local nodes
3965  for(unsigned l=0;l<n_node;l++)
3966  {
3967  //Loop over the number of dofs
3968  for(unsigned k=0;k<n_position_type;k++)
3969  {
3970  x[i] += nodal_position_gen(l,k,i)*psi(l,k);
3971  }
3972  }
3973  }
3974  }
3975 
3976 //==========================================================================
3977 /// Return FE interpolated position x[] at local coordinate s
3978 /// at previous timestep t as Vector (t=0: present; t>0: previous timestep)
3979 //==========================================================================
3980  void FiniteElement::interpolated_x(const unsigned &t,
3981  const Vector<double> &s,
3982  Vector<double>& x) const
3983  {
3984  //Find the number of nodes
3985  const unsigned n_node = nnode();
3986  //Find the number of positional types
3987  const unsigned n_position_type = nnodal_position_type();
3988  //Find the dimensions of the nodes
3989  const unsigned nodal_dim = nodal_dimension();
3990 
3991  //Assign storage for the local shape function
3992  Shape psi(n_node,n_position_type);
3993  //Find the values of shape function
3994  shape(s,psi);
3995 
3996  //Loop over the dimensions
3997  for(unsigned i=0;i<nodal_dim;i++)
3998  {
3999  //Initilialise value of x[i] to zero
4000  x[i] = 0.0;
4001  //Loop over the local nodes
4002  for(unsigned l=0;l<n_node;l++)
4003  {
4004  //Loop over the number of dofs
4005  for(unsigned k=0;k<n_position_type;k++)
4006  {
4007  x[i] += nodal_position_gen(t,l,k,i)*psi(l,k);
4008  }
4009  }
4010  }
4011  }
4012 
4013 //========================================================================
4014 /// \short Calculate the determinant of the
4015 /// Jacobian of the mapping between local and global
4016 /// coordinates at the position. Works directly from the base vectors
4017 /// without assuming that coordinates match spatial dimension. Will
4018 /// be overloaded in FaceElements, in which the elemental dimension does
4019 /// not match the spatial dimension. WARNING: this is always positive
4020 /// and cannot be used to check if the element is inverted, say!
4021 //========================================================================
4023  {
4024  //Find the number of nodes and position types
4025  const unsigned n_node = nnode();
4026  const unsigned n_position_type = nnodal_position_type();
4027  //Find the dimension of the node and element
4028  const unsigned n_dim_node = nodal_dimension();
4029  const unsigned n_dim_element = dim();
4030 
4031  //Set up dummy memory for the shape functions
4032  Shape psi(n_node,n_position_type);
4033  DShape dpsids(n_node,n_position_type,n_dim_element);
4034  //Get the shape functions and local derivatives
4035  dshape_local(s,psi,dpsids);
4036 
4037  //Right calculate the base vectors
4038  DenseMatrix<double> interpolated_G(n_dim_element,n_dim_node);
4039  assemble_eulerian_base_vectors(dpsids,interpolated_G);
4040 
4041  //Calculate the metric tensor of the element
4042  DenseMatrix<double> G(n_dim_element,n_dim_element,0.0);
4043  for(unsigned i=0;i<n_dim_element;i++)
4044  {
4045  for(unsigned j=0;j<n_dim_element;j++)
4046  {
4047  for(unsigned k=0;k<n_dim_node;k++)
4048  {
4049  G(i,j) += interpolated_G(i,k)*interpolated_G(j,k);
4050  }
4051  }
4052  }
4053 
4054  //Calculate the determinant of the metric tensor
4055  double det = 0.0;
4056  switch(n_dim_element)
4057  {
4058  case 0:
4059  throw OomphLibError("Cannot calculate J_eulerian() for point element\n",
4060  OOMPH_CURRENT_FUNCTION,
4061  OOMPH_EXCEPTION_LOCATION);
4062  break;
4063  case 1:
4064  det = G(0,0);
4065  break;
4066  case 2:
4067  det = G(0,0)*G(1,1) - G(0,1)*G(1,0);
4068  break;
4069  case 3:
4070  det = G(0,0)*G(1,1)*G(2,2) + G(0,1)*G(1,2)*G(2,0) + G(0,2)*G(1,0)*G(2,1)
4071  - G(0,0)*G(1,2)*G(2,1) - G(0,1)*G(1,0)*G(2,2) - G(0,2)*G(1,1)*G(2,0);
4072  break;
4073  default:
4074  oomph_info << "More than 3 dimensions in J_eulerian()" << std::endl;
4075  break;
4076  }
4077 
4078  //Return the Jacobian (square-root of the determinant of the metric tensor)
4079  return sqrt(det);
4080  }
4081 
4082 //========================================================================
4083 /// \short Compute the Jacobian of the mapping between the local and global
4084 /// coordinates at the ipt-th integration point
4085 //========================================================================
4086  double FiniteElement::J_eulerian_at_knot(const unsigned &ipt)
4087  const
4088  {
4089  //Find the number of nodes
4090  const unsigned n_node = nnode();
4091  //Find the number of position types
4092  const unsigned n_position_type = nnodal_position_type();
4093  //Find the dimension of the node and element
4094  const unsigned n_dim_node = nodal_dimension();
4095  const unsigned n_dim_element = dim();
4096 
4097  //Set up dummy memory for the shape functions
4098  Shape psi(n_node,n_position_type);
4099  DShape dpsids(n_node,n_position_type,n_dim_element);
4100  //Get the shape functions and local derivatives at the knot
4101  //This call may use the stored versions, which is why this entire
4102  //function doesn't just call J_eulerian(s), after reading out s from
4103  //the knots.
4104  dshape_local_at_knot(ipt,psi,dpsids);
4105 
4106  //Right calculate the base vectors
4107  DenseMatrix<double> interpolated_G(n_dim_element,n_dim_node);
4108  assemble_eulerian_base_vectors(dpsids,interpolated_G);
4109 
4110  //Calculate the metric tensor of the element
4111  DenseMatrix<double> G(n_dim_element,n_dim_element,0.0);
4112  for(unsigned i=0;i<n_dim_element;i++)
4113  {
4114  for(unsigned j=0;j<n_dim_element;j++)
4115  {
4116  for(unsigned k=0;k<n_dim_node;k++)
4117  {
4118  G(i,j) += interpolated_G(i,k)*interpolated_G(j,k);
4119  }
4120  }
4121  }
4122 
4123  //Calculate the determinant of the metric tensor
4124  double det = 0.0;
4125  switch(n_dim_element)
4126  {
4127  case 0:
4128  throw OomphLibError("Cannot calculate J_eulerian() for point element\n",
4129  OOMPH_CURRENT_FUNCTION,
4130  OOMPH_EXCEPTION_LOCATION);
4131  break;
4132  case 1:
4133  det = G(0,0);
4134  break;
4135  case 2:
4136  det = G(0,0)*G(1,1) - G(0,1)*G(1,0);
4137  break;
4138  case 3:
4139  det = G(0,0)*G(1,1)*G(2,2) + G(0,1)*G(1,2)*G(2,0) + G(0,2)*G(1,0)*G(2,1)
4140  - G(0,0)*G(1,2)*G(2,1) - G(0,1)*G(1,0)*G(2,2) - G(0,2)*G(1,1)*G(2,0);
4141  break;
4142  default:
4143  oomph_info << "More than 3 dimensions in J_eulerian()" << std::endl;
4144  break;
4145  }
4146 
4147  //Return the Jacobian (square-root of the determinant of the metric tensor)
4148  return sqrt(det);
4149  }
4150 
4151 //========================================================================
4152 /// \short Check that Jacobian of mapping between local and Eulerian
4153 /// coordinates at all integration points is positive.
4154 //========================================================================
4156  {
4157 
4158  // Bypass check deep down in the guts...
4160  FiniteElement::Accept_negative_jacobian=true;
4161 
4162  // Let's be optimistic...
4163  passed=true;
4164 
4165  //Find the number of nodes
4166  const unsigned n_node = nnode();
4167 
4168  //Find the number of position types
4169  const unsigned n_position_type = nnodal_position_type();
4170 
4171  //Find the dimension of the element
4172  const unsigned n_dim_element = dim();
4173 
4174  //Set up dummy memory for the shape functions
4175  Shape psi(n_node,n_position_type);
4176  DShape dpsi(n_node,n_dim_element);
4177 
4178  unsigned nintpt=integral_pt()->nweight();
4179  for (unsigned ipt=0;ipt<nintpt;ipt++)
4180  {
4181  double jac=dshape_eulerian_at_knot(ipt,psi,dpsi);
4182 
4183  // Are we dead yet?
4184  if (jac<=0.0)
4185  {
4186  passed=false;
4187 
4188  // Reset
4189  FiniteElement::Accept_negative_jacobian=backup;
4190 
4191  return;
4192  }
4193  }
4194 
4195  // Reset
4196  FiniteElement::Accept_negative_jacobian=backup;
4197 
4198  }
4199 
4200 //====================================================================
4201 /// Calculate the size of the element (in Eulerian computational
4202 /// coordinates. Use suitably overloaded compute_physical_size()
4203 /// function to compute the actual size (taking into account
4204 /// factors such as 2pi or radii the integrand). Such function
4205 /// can only be implemented on an equation-by-equation basis.
4206 //====================================================================
4207  double FiniteElement::size() const
4208  {
4209  //Initialise the sum to zero
4210  double sum = 0.0;
4211 
4212  //Loop over the integration points
4213  const unsigned n_intpt = integral_pt()->nweight();
4214  for(unsigned ipt=0;ipt<n_intpt;ipt++)
4215  {
4216  //Get the integral weight
4217  double w = integral_pt()->weight(ipt);
4218  //Get the value of the Jacobian of the mapping to global coordinates
4219  double J = J_eulerian_at_knot(ipt);
4220 
4221  //Add the product to the sum
4222  sum += w*J;
4223  }
4224 
4225  //Return the answer
4226  return(sum);
4227  }
4228 
4229 //==================================================================
4230 /// Integrate Vector-valued time-dep function over element
4231 //==================================================================
4232  void FiniteElement::
4234  const double& time,
4235  Vector<double>& integral)
4236  {
4237  //Initialise all components of integral Vector and setup integrand vector
4238  const unsigned ncomponents=integral.size();
4239  Vector<double> integrand(ncomponents);
4240  for (unsigned i=0;i<ncomponents;i++) {integral[i]=0.0;}
4241 
4242  // Figure out the global (Eulerian) spatial dimension of the
4243  // element
4244  const unsigned n_dim_eulerian = nodal_dimension();
4245 
4246  // Allocate Vector of global Eulerian coordinates
4247  Vector<double> x(n_dim_eulerian);
4248 
4249  // Set the value of n_intpt
4250  const unsigned n_intpt = integral_pt()->nweight();
4251 
4252  // Vector of local coordinates
4253  const unsigned n_dim = dim();
4254  Vector<double> s(n_dim);
4255 
4256  //Loop over the integration points
4257  for(unsigned ipt=0;ipt<n_intpt;ipt++)
4258  {
4259  //Assign the values of s
4260  for(unsigned i=0;i<n_dim;i++) {s[i] = integral_pt()->knot(ipt,i);}
4261 
4262  //Assign the values of the global Eulerian coordinates
4263  for(unsigned i=0;i<n_dim_eulerian;i++) {x[i] = interpolated_x(s,i);}
4264 
4265  //Get the integral weight
4266  double w = integral_pt()->weight(ipt);
4267 
4268  //Get Jacobian of mapping
4269  double J = J_eulerian(s);
4270 
4271  // Evaluate the integrand at the knot points
4272  integrand_fct_pt(time,x,integrand);
4273 
4274  //Add to components of integral Vector
4275  for (unsigned i=0;i<ncomponents;i++) {integral[i]+=integrand[i]*w*J;}
4276  }
4277  }
4278 
4279 //==================================================================
4280 /// Integrate Vector-valued function over element
4281 //==================================================================
4282  void FiniteElement::
4284  Vector<double>& integral)
4285  {
4286  //Initialise all components of integral Vector
4287  const unsigned ncomponents=integral.size();
4288  Vector<double> integrand(ncomponents);
4289  for (unsigned i=0;i<ncomponents;i++) {integral[i]=0.0;}
4290 
4291  // Figure out the global (Eulerian) spatial dimension of the
4292  // element by checking the Eulerian dimension of the nodes
4293  const unsigned n_dim_eulerian = nodal_dimension();
4294 
4295  // Allocate Vector of global Eulerian coordinates
4296  Vector<double> x(n_dim_eulerian);
4297 
4298  // Set the value of n_intpt
4299  const unsigned n_intpt = integral_pt()->nweight();
4300 
4301  // Vector of local coordinates
4302  const unsigned n_dim = dim();
4303  Vector<double> s(n_dim);
4304 
4305  //Loop over the integration points
4306  for(unsigned ipt=0;ipt<n_intpt;ipt++)
4307  {
4308  //Assign the values of s
4309  for(unsigned i=0;i<n_dim;i++) {s[i] = integral_pt()->knot(ipt,i);}
4310 
4311  //Assign the values of the global Eulerian coordinates
4312  for(unsigned i=0;i<n_dim_eulerian;i++) {x[i] = interpolated_x(s,i);}
4313 
4314  //Get the integral weight
4315  double w = integral_pt()->weight(ipt);
4316 
4317  //Get Jacobian of mapping
4318  double J = J_eulerian(s);
4319 
4320  // Evaluate the integrand at the knot points
4321  integrand_fct_pt(x,integrand);
4322 
4323  //Add to components of integral Vector
4324  for (unsigned i=0;i<ncomponents;i++) {integral[i]+=integrand[i]*w*J;}
4325  }
4326  }
4327 
4328 //==========================================================================
4329 /// Self-test: Have all internal values been classified as
4330 /// pinned/unpinned? Has pointer to spatial integration scheme
4331 /// been set? Return 0 if OK.
4332 //==========================================================================
4334  {
4335  // Initialise
4336  bool passed=true;
4337 
4338  if(GeneralisedElement::self_test()!=0) {passed=false;}
4339 
4340  // Check that pointer to spatial integration scheme has been set
4341  if(integral_pt()==0)
4342  {
4343  passed=false;
4344 
4346  "Pointer to spatial integration scheme has not been set.",
4347  "FiniteElement::self_test()",
4348  OOMPH_EXCEPTION_LOCATION);
4349  }
4350 
4351  //If the dimension of the element is zero (point element), there
4352  //is not jacobian
4353  const unsigned dim_el = dim();
4354 
4355  if(dim_el> 0)
4356  {
4357  // Loop over integration points to check sign of Jacobian
4358  //-------------------------------------------------------
4359 
4360  //Set the value of n_intpt
4361  const unsigned n_intpt = integral_pt()->nweight();
4362 
4363  //Set the Vector to hold local coordinates
4364  Vector<double> s(dim_el);
4365 
4366  //Find the number of local nodes
4367  const unsigned n_node = nnode();
4368  const unsigned n_position_type = nnodal_position_type();
4369  //Set up memory for the shape and test functions
4370  Shape psi(n_node,n_position_type);
4371  DShape dpsidx(n_node,dim_el);
4372 
4373  // Jacobian
4374  double jacobian;
4375 
4376 
4377  // Two ways of testing for negative Jacobian for non-FaceElements
4378  unsigned ntest=1;
4379 
4380  // For FaceElements checking the Jacobian via dpsidx doesn't
4381  // make sense
4382  FiniteElement* tmp_pt=const_cast<FiniteElement*>(this);
4383  FaceElement* face_el_pt=dynamic_cast<FaceElement*>(tmp_pt);
4384  if (face_el_pt==0)
4385  {
4386  ntest=2;
4387  }
4388 
4389  // For now overwrite -- the stuff above fails for Bretherton.
4390  // Not sure why.
4391  ntest=1;
4392 
4393  //Loop over the integration points
4394  for(unsigned ipt=0;ipt<n_intpt;ipt++)
4395  {
4396  //Assign values of s
4397  for(unsigned i=0;i<dim_el;i++)
4398  {
4399  s[i] = integral_pt()->knot(ipt,i);
4400  }
4401 
4402 
4403  // Do tests
4404  for (unsigned test=0;test<ntest;test++)
4405  {
4406 
4407  switch (test)
4408  {
4409 
4410  case 0:
4411 
4412  // Get the jacobian from the mapping between local and Eulerian
4413  // coordinates
4414  jacobian = J_eulerian(s);
4415 
4416  break;
4417 
4418  case 1:
4419 
4420  //Call the geometrical shape functions and derivatives
4421  //This also computes the Jacobian by a slightly different
4422  //method
4423  jacobian = dshape_eulerian_at_knot(ipt,psi,dpsidx);
4424 
4425  break;
4426 
4427  default:
4428 
4429  throw OomphLibError("Never get here",
4430  OOMPH_CURRENT_FUNCTION,
4431  OOMPH_EXCEPTION_LOCATION);
4432  }
4433 
4434 
4435  //Check for a singular jacobian
4436  if(std::fabs(jacobian) < 1.0e-16)
4437  {
4438  std::ostringstream warning_stream;
4439  warning_stream << "Determinant of Jacobian matrix is zero at ipt "
4440  << ipt << std::endl;
4441  OomphLibWarning(warning_stream.str(),
4442  "FiniteElement::self_test()",
4443  OOMPH_EXCEPTION_LOCATION);
4444  passed = false;
4445  //Skip the next test
4446  continue;
4447  }
4448 
4449  //Check sign of Jacobian
4450  if ((Accept_negative_jacobian==false) && (jacobian < 0.0))
4451  {
4452  std::ostringstream warning_stream;
4453  warning_stream << "Jacobian negative at integration point ipt="
4454  << ipt << std::endl;
4455  warning_stream
4456  << "If you think that this is what you want you may: " << std::endl
4457  << "set the (static) flag "
4458  << "FiniteElement::Accept_negative_jacobian to be true" << std::endl;
4459 
4460  OomphLibWarning(warning_stream.str(),
4461  "FiniteElement::self_test()",
4462  OOMPH_EXCEPTION_LOCATION);
4463  passed=false;
4464  }
4465 
4466  } // end of loop over two tests
4467 
4468  }
4469  } //End of non-zero dimension check
4470 
4471  // Return verdict
4472  if (passed) {return 0;}
4473  else {return 1;}
4474  }
4475 
4476 
4477 
4478 
4479 //=======================================================================
4480 /// Return the t-th time-derivative of the
4481 /// i-th FE-interpolated Eulerian coordinate at
4482 /// local coordinate s.
4483 //=======================================================================
4485  const unsigned &i,
4486  const unsigned &t_deriv)
4487  {
4488  //Find the number of nodes and positions (locally cached)
4489  const unsigned n_node = nnode();
4490  const unsigned n_position_type = nnodal_position_type();
4491  // Get shape functions: specify # of nodes, # of positional dofs
4492  Shape psi(n_node,n_position_type);
4493  shape(s,psi);
4494 
4495  // Initialise
4496  double drdt=0.0;
4497 
4498  // Assemble time derivative
4499  //Loop over nodes
4500  for(unsigned l=0;l<n_node;l++)
4501  {
4502  //Loop over types of dof
4503  for(unsigned k=0;k<n_position_type;k++)
4504  {
4505  drdt+=dnodal_position_gen_dt(t_deriv,l,k,i)*psi(l,k);
4506  }
4507  }
4508  return drdt;
4509  }
4510 
4511 
4512 
4513 //=======================================================================
4514 /// Compute t-th time-derivative of the
4515 /// FE-interpolated Eulerian coordinate vector at
4516 /// local coordinate s.
4517 //=======================================================================
4519  const unsigned &t_deriv,
4520  Vector<double>& dxdt)
4521  {
4522  //Find the number of nodes and positions (locally cached)
4523  const unsigned n_node = nnode();
4524  const unsigned n_position_type = nnodal_position_type();
4525  const unsigned nodal_dim = nodal_dimension();
4526 
4527  // Get shape functions: specify # of nodes, # of positional dofs
4528  Shape psi(n_node,n_position_type);
4529  shape(s,psi);
4530 
4531  // Loop over directions
4532  for (unsigned i=0;i<nodal_dim;i++)
4533  {
4534  // Initialise
4535  dxdt[i]=0.0;
4536 
4537  // Assemble time derivative
4538  //Loop over nodes
4539  for(unsigned l=0;l<n_node;l++)
4540  {
4541  //Loop over types of dof
4542  for(unsigned k=0;k<n_position_type;k++)
4543  {
4544  dxdt[i]+=dnodal_position_gen_dt(t_deriv,l,k,i)*psi(l,k);
4545  }
4546  }
4547  }
4548  }
4549 
4550 //============================================================================
4551 /// Calculate the interpolated value of zeta, the intrinsic coordinate
4552 /// of the element when viewed as a compound geometric object within a Mesh
4553 /// as a function of the local coordinate of the element, s.
4554 /// The default
4555 /// assumption is the zeta is interpolated using the shape functions of
4556 /// the element with the values given by zeta_nodal().
4557 /// A MacroElement representation of the intrinsic coordinate parametrised
4558 /// by the local coordinate s is used if available.
4559 /// Choosing the MacroElement representation of zeta (Eulerian x by default)
4560 /// allows a correspondence to be established between elements on different
4561 /// Meshes covering the same curvilinear domain in cases where one element
4562 /// is much coarser than the other.
4563 //==========================================================================
4565  Vector<double> &zeta) const
4566 {
4567  //If there is a macro element use it
4568  if(Macro_elem_pt!=0) {this->get_x_from_macro_element(s,zeta);}
4569  //Otherwise interpolate zeta_nodal using the shape functions
4570  else
4571  {
4572  //Find the number of nodes
4573  const unsigned n_node = this->nnode();
4574  //Find the number of positional types
4575  const unsigned n_position_type = this->nnodal_position_type();
4576  //Storage for the shape functions
4577  Shape psi(n_node,n_position_type);
4578  //Get the values of the shape functions at the local coordinate s
4579  this->shape(s,psi);
4580 
4581  //Find the number of coordinates
4582  const unsigned ncoord = this->dim();
4583  //Initialise the value of zeta to zero
4584  for(unsigned i=0;i<ncoord;i++) {zeta[i] = 0.0;}
4585 
4586  //Add the contributions from each nodal dof to the interpolated value
4587  //of zeta.
4588  for(unsigned l=0;l<n_node;l++)
4589  {
4590  for(unsigned k=0;k<n_position_type;k++)
4591  {
4592  //Locally cache the value of the shape function
4593  const double psi_ = psi(l,k);
4594  for(unsigned i=0;i<ncoord;i++)
4595  {
4596  zeta[i] += this->zeta_nodal(l,k,i)*psi_;
4597  }
4598  }
4599  }
4600  }
4601 }
4602 
4603 //==========================================================================
4604 /// For a given value of zeta, the "global" intrinsic coordinate of
4605 /// a mesh of FiniteElements represented as a compound geometric object,
4606 /// find the local coordinate in this element that corresponds to the
4607 /// requested value of zeta.
4608 /// This is achieved in generality by using Newton's method to find the value
4609 /// of the local coordinate, s, such that
4610 /// interpolated_zeta(s) is equal to the requested value of zeta.
4611 /// If zeta cannot be located in this element, geom_object_pt is set
4612 /// to NULL. If zeta is located in this element, we return its "this"
4613 /// pointer.
4614 /// Setting the optional bool argument to true means that the coordinate
4615 /// argument "s" is used as the initial guess. (Default is false).
4616 //=========================================================================
4618  GeomObject*& geom_object_pt, Vector<double> &s,
4619  const bool& use_coordinate_as_initial_guess)
4620 {
4621 
4622  //Find the number of coordinates, the dimension of the element
4623  //This must be the same for the local and intrinsic global coordinate
4624  unsigned ncoord = this->dim();
4625 
4626  // Fast return based on centre of gravity and max. radius of any nodal
4627  // point
4629  {
4630  Vector<double> cog(ncoord);
4631  double max_radius=0.0;
4632  get_centre_of_gravity_and_max_radius_in_terms_of_zeta(cog,max_radius);
4633 
4634  // Get radius
4635  double radius=0.0;
4636  for (unsigned i=0;i<ncoord;i++)
4637  {
4638  radius+=(cog[i]-zeta[i])*(cog[i]-zeta[i]);
4639  }
4640  radius=sqrt(radius);
4641  if (radius>
4643  max_radius)
4644  {
4645  geom_object_pt=0;
4646  return;
4647  }
4648  }
4649 
4650  //Assign storage for the vector and matrix used in Newton's method
4651  Vector<double> dx(ncoord,0.0);
4652  DenseDoubleMatrix jacobian(ncoord,ncoord,0.0);
4653 
4654  // // Make a list of (equally-spaced) local coordinates inside the element
4655  // unsigned n_list=Locate_zeta_helpers::N_local_points;
4656 
4657  // double list_space=(1.0/(double(n_list)-1.0))*(s_max()-s_min());
4658 
4659  // Possible initial guesses for Newton's method
4660  Vector<Vector<double> > s_list;
4661 
4662  // If the boolean argument use_coordinate_as_initial_guess was set
4663  // to true then we don't need to initialise s
4664  if (!use_coordinate_as_initial_guess)
4665  {
4666  //Vector of local coordinates
4667  Vector<double> s_c(ncoord);
4668 
4669  // Loop over plot points
4670  unsigned num_plot_points=nplot_points(Locate_zeta_helpers::N_local_points);
4671  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
4672  {
4673  // Get local coordinates of plot point
4674  get_s_plot(iplot,Locate_zeta_helpers::N_local_points,s_c);
4675  s_list.push_back(s_c);
4676  }
4677  }
4678 
4679  //Counter for the number of Newton steps
4680  unsigned count=0;
4681 
4682  //Control flag for the Newton loop
4683  bool keep_going=true;
4684 
4685  //Storage for the interpolated value of x
4686  Vector<double> inter_x(ncoord);
4687 
4688  // Ifwe have specified the coordinate already
4689  if (use_coordinate_as_initial_guess)
4690  {
4691  //Get the value of x at the initial guess
4692  this->interpolated_zeta(s,inter_x);
4693 
4694  //Set up the residuals
4695  for(unsigned i=0;i<ncoord;i++) {dx[i] = zeta[i] - inter_x[i];}
4696  }
4697  else
4698  {
4699  // Find the smallest residual from the list of coordinates made earlier
4700  double my_min_resid=DBL_MAX;
4701  Vector<double> s_local(ncoord);
4702  Vector<double> work_x(ncoord);
4703  Vector<double> work_dx(ncoord);
4704 
4705  unsigned n_list_coord=s_list.size();
4706 
4707  for (unsigned i_coord=0; i_coord<n_list_coord; i_coord++)
4708  {
4709  for (unsigned i=0;i<ncoord;i++)
4710  {
4711  s_local[i]=s_list[i_coord][i];
4712  }
4713  // get_x for this coordinate
4714  this->interpolated_zeta(s_local,work_x);
4715 
4716  // calculate residuals
4717  for(unsigned i=0;i<ncoord;i++)
4718  {
4719  work_dx[i] = zeta[i] - work_x[i];
4720  }
4721 
4722  double maxres =
4723  std::fabs(*std::max_element(work_dx.begin(),work_dx.end(),
4724  AbsCmp<double>()));
4725 
4726  // test against previous residuals
4727  if (maxres<my_min_resid)
4728  {
4729  my_min_resid=maxres;
4730  dx=work_dx;
4731  inter_x=work_x;
4732  s=s_local;
4733  }
4734 
4735  }
4736  }
4737 
4738  //Main Newton Loop
4739  do // start of do while loop
4740  {
4741  //Increase loop counter
4742  count++;
4743 
4744  //Bail out if necessary (without an error for now...)
4746  {
4747  keep_going=false;
4748  continue;
4749  }
4750 
4751  //If it's the first time round the loop, check the initial residuals
4752  if(count==1)
4753  {
4754  double maxres =
4755  std::fabs(*std::max_element(dx.begin(),dx.end(),AbsCmp<double>()));
4756 
4757  //If it's small enough exit
4759  {
4760  keep_going=false;
4761  continue;
4762  }
4763  }
4764 
4765  //Is there a macro element? If so, assemble the Jacobian by FD-ing
4766  if (macro_elem_pt()!=0)
4767  {
4768  // Assemble jacobian on the fly by finite differencing
4769  Vector<double> work_s=s;
4770  Vector<double> r=inter_x; // i.e. the result of previous call to get_x
4771 
4772  // Finite difference step
4774 
4775  // Storage for calculated r from incremented s
4776  Vector<double> work_r(ncoord,0.0);
4777 
4778  // Loop over s coordinates
4779  for (unsigned i=0; i<ncoord; i++)
4780  {
4781  // Increment work_s by a small amount
4782  work_s[i]+=fd_step;
4783 
4784  // Calculate work_r from macro element
4785  this->interpolated_zeta(work_s,work_r);
4786 
4787  // Loop over r to fill Jacobian
4788  for (unsigned j=0; j<ncoord; j++)
4789  {
4790  jacobian(j,i)=-(work_r[j]-r[j])/fd_step;
4791  }
4792 
4793  // Reset work_s
4794  work_s[i]=s[i];
4795 
4796  }
4797 
4798  }
4799  else // no macro element, so compute Jacobian with shape functions etc.
4800  {
4801  //Compute the entries of the Jacobian matrix
4802  unsigned n_node = this->nnode();
4803  unsigned n_position_type = this->nnodal_position_type();
4804  Shape psi(n_node,n_position_type);
4805  DShape dpsids(n_node,n_position_type,ncoord);
4806 
4807  //Get the local shape functions and their derivatives
4808  dshape_local(s,psi,dpsids);
4809 
4810  //Calculate the values of dxds
4811  DenseMatrix<double> interpolated_dxds(ncoord,ncoord,0.0);
4812 
4813 // MH: No longer needed
4814 // //This implementation will only work for n_position_type=1
4815 // //since the function nodal_position_gen does not yet exist
4816 // #ifdef PARANOID
4817 // if (n_position_type!=1)
4818 // {
4819 // std::ostringstream error_stream;
4820 // error_stream << "This implementation does not exist yet;\n"
4821 // << "it currently uses raw_nodal_position_gen\n"
4822 // << "which does not take hangingness into account\n"
4823 // << "It will work if n_position_type=1\n";
4824 // throw OomphLibError(error_stream.str(),
4825 // OOMPH_CURRENT_FUNCTION,
4826 // OOMPH_EXCEPTION_LOCATION);
4827 // }
4828 // #endif
4829 
4830  // Loop over the nodes
4831  for(unsigned l=0;l<n_node;l++)
4832  {
4833  // Loop over position type even though it should be 1; the
4834  // functionality for n_position_type>1 will exist in the future
4835  for(unsigned k=0;k<n_position_type;k++)
4836  {
4837  // Add the contribution from the nodal coordinates to the matrix
4838  for(unsigned i=0;i<ncoord;i++)
4839  {
4840  for(unsigned j=0;j<ncoord;j++)
4841  {
4842  interpolated_dxds(i,j) +=
4843  this->zeta_nodal(l,k,i)*dpsids(l,k,j);
4844  }
4845  }
4846  }
4847  }
4848 
4849  //The entries of the Jacobian matrix are merely dresiduals/ds
4850  //i.e. \f$ -dx/ds \f$
4851  for(unsigned i=0;i<ncoord;i++)
4852  {
4853  for(unsigned j=0;j<ncoord;j++)
4854  {
4855  jacobian(i,j) = - interpolated_dxds(i,j);
4856  }
4857  }
4858 
4859  }
4860 
4861  //Now solve the damn thing
4862  try
4863  {
4864  jacobian.solve(dx);
4865  }
4866  catch(OomphLibError &error)
4867  {
4868  // I've caught the error so shut up!
4869  error.disable_error_message();
4870 #ifdef PARANOID
4871  oomph_info << "Error in linear solve for "
4872  << "FiniteElement::locate_zeta()"
4873  << std::endl;
4874  oomph_info << "Should not affect the result!" << std::endl;
4875 #endif
4876  }
4877 
4878  //Add the correction to the local coordinates
4879  for(unsigned i=0;i<ncoord;i++) {s[i] -= dx[i];}
4880 
4881  //Get the new residuals
4882  this->interpolated_zeta(s,inter_x);
4883  for(unsigned i=0;i<ncoord;i++) {dx[i] = zeta[i] - inter_x[i];}
4884 
4885  //Get the maximum residuals
4886  double maxres =
4887  std::fabs(*std::max_element(dx.begin(),dx.end(),AbsCmp<double>()));
4888 
4889  //If we have converged jump straight to the test at the end of the loop
4891  {
4892  keep_going=false;
4893  continue;
4894  }
4895  }
4896  while(keep_going);
4897 
4898  //Test whether the local coordinates are valid or not
4899  bool valid=local_coord_is_valid(s);
4900 
4901  // If not valid, experimentally push back into element
4902  // and see if the result is still valid (within the Newton tolerance)
4903  if (!valid)
4904  {
4905  move_local_coord_back_into_element(s);
4906 
4907  // Check residuals again
4908  this->interpolated_zeta(s,inter_x);
4909  for(unsigned i=0;i<ncoord;i++) {dx[i] = zeta[i] - inter_x[i];}
4910 
4911  //Get the maximum residuals
4912  double maxres =
4913  std::fabs(*std::max_element(dx.begin(),dx.end(),AbsCmp<double>()));
4914 
4915  // Are we still OK?
4917  {
4918  // oomph_info
4919  // << "Pushing back inside has violated the Newton tolerance: max_res = "
4920  // << maxres << std::endl;
4921  geom_object_pt=0;
4922  return;
4923  }
4924  }
4925 
4926  // It is also possible now that it may not have converged "correctly",
4927  // i.e. count is greater than Max_newton_iterations
4929  {
4930  // Don't trust the current answer, return null
4931  geom_object_pt=0;
4932  return;
4933  }
4934 
4935  //Otherwise the required point is located in "this" element:
4936  geom_object_pt = this;
4937 }
4938 
4939 
4940 
4941 //=======================================================================
4942 /// Loop over all nodes in the element and update their positions
4943 /// using each node's (algebraic) update function
4944 //=======================================================================
4946  {
4947  const unsigned n_node = nnode();
4948  for(unsigned n=0;n<n_node;n++) {node_pt(n)->node_update();}
4949  }
4950 
4951 //======================================================================
4952 /// The purpose of this function is to identify all possible
4953 /// Data that can affect the fields interpolated by the FiniteElement.
4954 /// The information will typically be used in interaction problems in
4955 /// which the FiniteElement provides a forcing term for an
4956 /// ElementWithExternalElement. The Data must be provided as
4957 /// \c paired_load data containing (a) the pointer to a Data object
4958 /// and (b) the index of the value in that Data object.
4959 /// The generic implementation (should be overloaded in more specific
4960 /// applications) is to include all nodal and internal Data stored in
4961 /// the FiniteElement. Note that the geometric data,
4962 /// which includes the positions
4963 /// of SolidNodes, is treated separately by the function
4964 /// \c identify_geometric_data()
4965 //======================================================================
4967  std::set<std::pair<Data*,unsigned> > &paired_field_data)
4968 {
4969  //Loop over all internal data
4970  const unsigned n_internal = this->ninternal_data();
4971  for(unsigned n=0;n<n_internal;n++)
4972  {
4973  //Cache the data pointer
4974  Data* const dat_pt = this->internal_data_pt(n);
4975  //Find the number of data values stored in the data object
4976  const unsigned n_value = dat_pt->nvalue();
4977  //Add the index of each data value and the pointer to the set
4978  //of pairs
4979  for(unsigned i=0;i<n_value;i++)
4980  {
4981  paired_field_data.insert(std::make_pair(dat_pt,i));
4982  }
4983  }
4984 
4985  //Loop over all the nodes
4986  const unsigned n_node = this->nnode();
4987  for(unsigned n=0;n<n_node;n++)
4988  {
4989  //Cache the node pointer
4990  Node* const nod_pt = this->node_pt(n);
4991  //Find the number of values stored at the node
4992  const unsigned n_value = nod_pt->nvalue();
4993  //Add the index of each data value and the pointer to the set
4994  //of pairs
4995  for(unsigned i=0;i<n_value;i++)
4996  {
4997  paired_field_data.insert(std::make_pair(nod_pt,i));
4998  }
4999  }
5000 }
5001 
5002 void FiniteElement::build_face_element(const int &face_index,
5003  FaceElement* face_element_pt)
5004 {
5005  //Set the nodal dimension
5006  face_element_pt->set_nodal_dimension(nodal_dimension());
5007 
5008  //Set the pointer to the orginal "bulk" element
5009  face_element_pt->bulk_element_pt()=this;
5010 
5011 #ifdef OOMPH_HAS_MPI
5012  // Pass on non-halo proc ID
5013  face_element_pt->set_halo(Non_halo_proc_ID);
5014 #endif
5015 
5016  // Set the face index
5017  face_element_pt->face_index() = face_index;
5018 
5019  // Get number of bulk nodes on a face of this element
5020  const unsigned nnode_face = nnode_on_face();
5021 
5022  // Set the function pointer for face coordinate to bulk coordinate
5023  // mapping
5024  face_element_pt->face_to_bulk_coordinate_fct_pt() =
5025  face_to_bulk_coordinate_fct_pt(face_index);
5026 
5027  // Set the function pointer for the derivative of the face coordinate to
5028  // bulk coordinate mapping
5029  face_element_pt->bulk_coordinate_derivatives_fct_pt() =
5030  bulk_coordinate_derivatives_fct_pt(face_index);
5031 
5032  // Resize storage for the number of values originally stored each of the
5033  // face element's nodes.
5034  face_element_pt->nbulk_value_resize(nnode_face);
5035 
5036  // Resize storage for the bulk node numbers corresponding to the face
5037  // element's nodes.
5038  face_element_pt->bulk_node_number_resize(nnode_face);
5039 
5040  // Copy bulk_node_numbers and nbulk_values
5041  for(unsigned i=0; i<nnode_face; i++)
5042  {
5043  // Find the corresponding bulk node's number
5044  unsigned bulk_number = get_bulk_node_number(face_index, i);
5045 
5046  // Assign the pointer and number into the face element
5047  face_element_pt->node_pt(i) = node_pt(bulk_number);
5048  face_element_pt->bulk_node_number(i) = bulk_number;
5049 
5050  // Set the number of values originally stored at this node
5051  face_element_pt->nbulk_value(i) = required_nvalue(bulk_number);
5052  }
5053 
5054  // Set the outer unit normal sign
5055  face_element_pt->normal_sign() = face_outer_unit_normal_sign(face_index);
5056 
5057 }
5058 
5059 //////////////////////////////////////////////////////////////////////////
5060 //////////////////////////////////////////////////////////////////////////
5061 //////////////////////////////////////////////////////////////////////////
5062 
5063 // Initialise the static variable.
5064 // Do not ignore warning for discontinuous tangent vectors.
5066 
5067 
5068 //========================================================================
5069 /// Output boundary coordinate zeta
5070 //========================================================================
5071 void FaceElement::output_zeta(std::ostream &outfile, const unsigned& nplot)
5072 {
5073  //Vector of local coordinates
5074  unsigned n_dim=dim();
5075  Vector<double> s(n_dim);
5076 
5077  // Tecplot header info
5078  outfile << tecplot_zone_string(nplot);
5079 
5080  // Loop over plot points
5081  unsigned num_plot_points=nplot_points(nplot);
5082  for (unsigned iplot=0;iplot<num_plot_points;iplot++)
5083  {
5084  // Get local coordinates of plot point
5085  get_s_plot(iplot,nplot,s);
5086 
5087  // Spatial coordinates are one higher
5088  for(unsigned i=0;i<n_dim+1;i++)
5089  {
5090  outfile << interpolated_x(s,i) << " ";
5091  }
5092 
5093  // Boundary coordinate
5094  Vector<double> zeta(n_dim);
5095  interpolated_zeta(s,zeta);
5096  for(unsigned i=0;i<n_dim;i++)
5097  {
5098  outfile << zeta[i] << " ";
5099  }
5100  outfile << std::endl;
5101  }
5102 
5103  // Write tecplot footer (e.g. FE connectivity lists)
5104  write_tecplot_zone_footer(outfile,nplot);
5105 }
5106 
5107 
5108 //========================================================================
5109 /// \short Calculate the determinant of the
5110 /// Jacobian of the mapping between local and global
5111 /// coordinates at the position s. Overloaded from FiniteElement.
5112 //========================================================================
5114  {
5115 
5116  //Find out the sptial dimension of the element
5117  unsigned n_dim_el = this->dim();
5118 
5119  // Bail out if we're in a point element -- not sure what
5120  // J_eulerian actually is, but this is harmless
5121  if (n_dim_el==0) return 1.0;
5122 
5123  //Find out how many nodes there are
5124  unsigned n_node = nnode();
5125 
5126  //Find out how many positional dofs there are
5127  unsigned n_position_type = this->nnodal_position_type();
5128 
5129  //Find out the dimension of the node
5130  unsigned n_dim = this->nodal_dimension();
5131 
5132  //Set up memory for the shape functions
5133  Shape psi(n_node,n_position_type);
5134  DShape dpsids(n_node,n_position_type,n_dim_el);
5135 
5136  //Only need to call the local derivatives
5137  dshape_local(s,psi,dpsids);
5138 
5139  //Also calculate the surface Vectors (derivatives wrt local coordinates)
5140  DenseMatrix<double> interpolated_A(n_dim_el,n_dim,0.0);
5141 
5142  //Calculate positions and derivatives
5143  for(unsigned l=0;l<n_node;l++)
5144  {
5145  //Loop over positional dofs
5146  for(unsigned k=0;k<n_position_type;k++)
5147  {
5148  //Loop over coordinates
5149  for(unsigned i=0;i<n_dim;i++)
5150  {
5151  //Loop over LOCAL derivative directions, to calculate the tangent(s)
5152  for(unsigned j=0;j<n_dim_el;j++)
5153  {
5154  interpolated_A(j,i) +=
5155  nodal_position_gen(l,bulk_position_type(k),i)*dpsids(l,k,j);
5156  }
5157  }
5158  }
5159  }
5160  //Now find the local deformed metric tensor from the tangent Vectors
5161  DenseMatrix<double> A(n_dim_el,n_dim_el,0.0);
5162  for(unsigned i=0;i<n_dim_el;i++)
5163  {
5164  for(unsigned j=0;j<n_dim_el;j++)
5165  {
5166  //Take the dot product
5167  for(unsigned k=0;k<n_dim;k++)
5168  {
5169  A(i,j) += interpolated_A(i,k)*interpolated_A(j,k);
5170  }
5171  }
5172  }
5173 
5174  //Find the determinant of the metric tensor
5175  double Adet =0.0;
5176  switch(n_dim_el)
5177  {
5178  case 1:
5179  Adet = A(0,0);
5180  break;
5181  case 2:
5182  Adet = A(0,0)*A(1,1) - A(0,1)*A(1,0);
5183  break;
5184  default:
5185  throw
5186  OomphLibError("Wrong dimension in FaceElement",
5187  "FaceElement::J_eulerian()",
5188  OOMPH_EXCEPTION_LOCATION);
5189  }
5190 
5191  // Return
5192  return sqrt(Adet);
5193  }
5194 
5195 
5196 //========================================================================
5197 /// \short Compute the Jacobian of the mapping between the local and global
5198 /// coordinates at the ipt-th integration point. Overloaded from
5199 /// FiniteElement.
5200 //========================================================================
5201  double FaceElement::J_eulerian_at_knot(const unsigned &ipt)
5202  const
5203  {
5204  //Find the number of nodes
5205  const unsigned n_node = nnode();
5206 
5207  //Find the number of position types
5208  const unsigned n_position_type = nnodal_position_type();
5209 
5210  //Find the dimension of the node and element
5211  const unsigned n_dim = nodal_dimension();
5212  const unsigned n_dim_el = dim();
5213 
5214  //Set up dummy memory for the shape functions
5215  Shape psi(n_node,n_position_type);
5216  DShape dpsids(n_node,n_position_type,n_dim_el);
5217 
5218  //Get the shape functions and local derivatives at the knot
5219  //This call may use the stored versions, which is why this entire
5220  //function doesn't just call J_eulerian(s), after reading out s from
5221  //the knots.
5222  dshape_local_at_knot(ipt,psi,dpsids);
5223 
5224  //Also calculate the surface Vectors (derivatives wrt local coordinates)
5225  DenseMatrix<double> interpolated_A(n_dim_el,n_dim,0.0);
5226 
5227  //Calculate positions and derivatives
5228  for(unsigned l=0;l<n_node;l++)
5229  {
5230  //Loop over positional dofs
5231  for(unsigned k=0;k<n_position_type;k++)
5232  {
5233  //Loop over coordinates
5234  for(unsigned i=0;i<n_dim;i++)
5235  {
5236  //Loop over LOCAL derivative directions, to calculate the tangent(s)
5237  for(unsigned j=0;j<n_dim_el;j++)
5238  {
5239  interpolated_A(j,i) +=
5240  nodal_position_gen(l,bulk_position_type(k),i)*dpsids(l,k,j);
5241  }
5242  }
5243  }
5244  }
5245 
5246  //Now find the local deformed metric tensor from the tangent Vectors
5247  DenseMatrix<double> A(n_dim_el,n_dim_el,0.0);
5248  for(unsigned i=0;i<n_dim_el;i++)
5249  {
5250  for(unsigned j=0;j<n_dim_el;j++)
5251  {
5252  //Take the dot product
5253  for(unsigned k=0;k<n_dim;k++)
5254  {
5255  A(i,j) += interpolated_A(i,k)*interpolated_A(j,k);
5256  }
5257  }
5258  }
5259 
5260  //Find the determinant of the metric tensor
5261  double Adet =0.0;
5262  switch(n_dim_el)
5263  {
5264  case 1:
5265  Adet = A(0,0);
5266  break;
5267  case 2:
5268  Adet = A(0,0)*A(1,1) - A(0,1)*A(1,0);
5269  break;
5270  default:
5271  throw
5272  OomphLibError("Wrong dimension in FaceElement",
5273  "FaceElement::J_eulerian_at_knot()",
5274  OOMPH_EXCEPTION_LOCATION);
5275  }
5276 
5277  // Return
5278  return sqrt(Adet);
5279  }
5280 
5281 //========================================================================
5282 /// \short Check that Jacobian of mapping between local and Eulerian
5283 /// coordinates at all integration points is positive.
5284 //========================================================================
5286  {
5287  // Let's be optimistic...
5288  passed=true;
5289 
5290  //Find the number of nodes
5291  const unsigned n_node = nnode();
5292 
5293  //Find the number of position types
5294  const unsigned n_position_type = nnodal_position_type();
5295 
5296  //Find the dimension of the node and element
5297  const unsigned n_dim = nodal_dimension();
5298  const unsigned n_dim_el = dim();
5299 
5300  //Set up dummy memory for the shape functions
5301  Shape psi(n_node,n_position_type);
5302  DShape dpsids(n_node,n_position_type,n_dim_el);
5303 
5304 
5305  unsigned nintpt=integral_pt()->nweight();
5306  for (unsigned ipt=0;ipt<nintpt;ipt++)
5307  {
5308 
5309  //Get the shape functions and local derivatives at the knot
5310  //This call may use the stored versions, which is why this entire
5311  //function doesn't just call J_eulerian(s), after reading out s from
5312  //the knots.
5313  dshape_local_at_knot(ipt,psi,dpsids);
5314 
5315  //Also calculate the surface Vectors (derivatives wrt local coordinates)
5316  DenseMatrix<double> interpolated_A(n_dim_el,n_dim,0.0);
5317 
5318  //Calculate positions and derivatives
5319  for(unsigned l=0;l<n_node;l++)
5320  {
5321  //Loop over positional dofs
5322  for(unsigned k=0;k<n_position_type;k++)
5323  {
5324  //Loop over coordinates
5325  for(unsigned i=0;i<n_dim;i++)
5326  {
5327  //Loop over LOCAL derivative directions, to calculate the tangent(s)
5328  for(unsigned j=0;j<n_dim_el;j++)
5329  {
5330  interpolated_A(j,i) +=
5331  nodal_position_gen(l,bulk_position_type(k),i)*dpsids(l,k,j);
5332  }
5333  }
5334  }
5335  }
5336 
5337  //Now find the local deformed metric tensor from the tangent Vectors
5338  DenseMatrix<double> A(n_dim_el,n_dim_el,0.0);
5339  for(unsigned i=0;i<n_dim_el;i++)
5340  {
5341  for(unsigned j=0;j<n_dim_el;j++)
5342  {
5343  //Take the dot product
5344  for(unsigned k=0;k<n_dim;k++)
5345  {
5346  A(i,j) += interpolated_A(i,k)*interpolated_A(j,k);
5347  }
5348  }
5349  }
5350 
5351  //Find the determinant of the metric tensor
5352  double Adet =0.0;
5353  switch(n_dim_el)
5354  {
5355  case 1:
5356  Adet = A(0,0);
5357  break;
5358  case 2:
5359  Adet = A(0,0)*A(1,1) - A(0,1)*A(1,0);
5360  break;
5361  default:
5362  throw
5363  OomphLibError("Wrong dimension in FaceElement",
5364  "FaceElement::J_eulerian_at_knot()",
5365  OOMPH_EXCEPTION_LOCATION);
5366  }
5367 
5368 
5369  // Are we dead yet?
5370  if (Adet<=0.0)
5371  {
5372  passed=false;
5373  return;
5374  }
5375  }
5376 
5377  }
5378 
5379 //=======================================================================
5380 /// \short Compute the tangent vector(s) and the outer unit normal
5381 /// vector at the specified local coordinate.
5382 /// In two spatial dimensions, a "tangent direction" is not required.
5383 /// In three spatial dimensions, a tangent direction is required
5384 /// (set via set_tangent_direction(...)), and we project the tanent direction
5385 /// on to the surface. The second tangent vector is taken to be the cross
5386 /// product of the projection and the unit normal.
5387 //=======================================================================
5389 (const Vector<double> &s, Vector<Vector<double> > &tang_vec,
5390  Vector<double> &unit_normal) const
5391 {
5392  //Find the spatial dimension of the FaceElement
5393  const unsigned element_dim = dim();
5394 
5395  //Find the overall dimension of the problem
5396  //(assume that it's the same for all nodes)
5397  const unsigned spatial_dim = nodal_dimension();
5398 
5399 #ifdef PARANOID
5400  //Check the number of local coordinates passed
5401  if(s.size()!=element_dim)
5402  {
5403  std::ostringstream error_stream;
5404  error_stream
5405  << "Local coordinate s passed to outer_unit_normal() has dimension "
5406  << s.size() << std::endl
5407  << "but element dimension is " << element_dim << std::endl;
5408 
5409  throw OomphLibError(error_stream.str(),
5410  OOMPH_CURRENT_FUNCTION,
5411  OOMPH_EXCEPTION_LOCATION);
5412  }
5413 
5414  // Check that if the Tangent_direction_pt is set, then
5415  // it is the same length as the spatial dimension.
5416  if(Tangent_direction_pt != 0 && Tangent_direction_pt->size()!=spatial_dim)
5417  {
5418  std::ostringstream error_stream;
5419  error_stream
5420  << "Tangent direction vector has dimension "
5421  << Tangent_direction_pt->size() << std::endl
5422  << "but spatial dimension is " << spatial_dim << std::endl;
5423 
5424  throw OomphLibError(error_stream.str(),
5425  OOMPH_CURRENT_FUNCTION,
5426  OOMPH_EXCEPTION_LOCATION);
5427  }
5428 
5429  //Check the dimension of the normal vector
5430  if(unit_normal.size()!=spatial_dim)
5431  {
5432  std::ostringstream error_stream;
5433  error_stream
5434  << "Unit normal passed to outer_unit_normal() has dimension "
5435  << unit_normal.size() << std::endl
5436  << "but spatial dimension is " << spatial_dim << std::endl;
5437 
5438  throw OomphLibError(error_stream.str(),
5439  OOMPH_CURRENT_FUNCTION,
5440  OOMPH_EXCEPTION_LOCATION);
5441  }
5442 
5443 
5444  // The number of tangent vectors.
5445  unsigned ntangent_vec = tang_vec.size();
5446 
5447  // For the tangent vector,
5448  // if element_dim = 2, tang_vec is a 2D Vector of length 3.
5449  // if element_dim = 1, tang_vec is a 1D Vector of length 2.
5450  // if element_dim = 0, tang_vec is a 1D Vector of length 1.
5451  switch(element_dim)
5452  {
5453  //Point element, derived from a 1D element.
5454  case 0:
5455  {
5456  // Check that tang_vec is a 1D vector.
5457  if(ntangent_vec != 1)
5458  {
5459  std::ostringstream error_stream;
5460  error_stream
5461  << "This is a 0D FaceElement, we need one tangent vector.\n"
5462  << "You have given me " << tang_vec.size() << " vector(s).\n";
5463  throw OomphLibError(error_stream.str(),
5464  OOMPH_CURRENT_FUNCTION,
5465  OOMPH_EXCEPTION_LOCATION);
5466  }
5467  }
5468  break;
5469 
5470  //Line element, derived from a 2D element.
5471  case 1:
5472  {
5473  // Check that tang_vec is a 1D vector.
5474  if(ntangent_vec != 1)
5475  {
5476  std::ostringstream error_stream;
5477  error_stream
5478  << "This is a 1D FaceElement, we need one tangent vector.\n"
5479  << "You have given me " << tang_vec.size() << " vector(s).\n";
5480  throw OomphLibError(error_stream.str(),
5481  OOMPH_CURRENT_FUNCTION,
5482  OOMPH_EXCEPTION_LOCATION);
5483  }
5484  }
5485  break;
5486 
5487  //Plane element, derived from 3D element.
5488  case 2:
5489  {
5490  // Check that tang_vec is a 2D vector.
5491  if(ntangent_vec != 2)
5492  {
5493  std::ostringstream error_stream;
5494  error_stream
5495  << "This is a 2D FaceElement, we need two tangent vectors.\n"
5496  << "You have given me " << tang_vec.size() << " vector(s).\n";
5497  throw OomphLibError(error_stream.str(),
5498  OOMPH_CURRENT_FUNCTION,
5499  OOMPH_EXCEPTION_LOCATION);
5500  }
5501  }
5502  break;
5503  // There are no other elements!
5504  default:
5505 
5506  throw OomphLibError(
5507  "Cannot have a FaceElement with dimension higher than 2",
5508  OOMPH_CURRENT_FUNCTION,
5509  OOMPH_EXCEPTION_LOCATION);
5510  break;
5511  }
5512 
5513  // Check the lengths of each sub vectors.
5514  for(unsigned vec_i = 0; vec_i < ntangent_vec; vec_i++)
5515  {
5516  if(tang_vec[vec_i].size() != spatial_dim)
5517  {
5518  std::ostringstream error_stream;
5519  error_stream
5520  << "This problem has " << spatial_dim << " spatial dimensions.\n"
5521  << "But the " << vec_i << " tangent vector has size "
5522  << tang_vec[vec_i].size() << std::endl;
5523  throw OomphLibError(error_stream.str(),
5524  OOMPH_CURRENT_FUNCTION,
5525  OOMPH_EXCEPTION_LOCATION);
5526  }
5527  }
5528 
5529 #endif
5530 
5531 
5532  //Now let's consider the different element dimensions
5533  switch(element_dim)
5534  {
5535 
5536  //Point element, derived from a 1D element.
5537  case 0:
5538  {
5539  std::ostringstream error_stream;
5540  error_stream
5541  << "It is unclear how to calculate a tangent and normal vectors for "
5542  << "a point element.\n";
5543  throw OomphLibError(error_stream.str(),
5544  OOMPH_CURRENT_FUNCTION,
5545  OOMPH_EXCEPTION_LOCATION);
5546  }
5547  break;
5548 
5549  //Line element, derived from a 2D element, in this case
5550  //the normal is a mess of cross products
5551  //We need an interior direction, so we must find the local
5552  //derivatives in the BULK element
5553  case 1:
5554  {
5555  //Find the number of nodes in the bulk element
5556  const unsigned n_node_bulk = Bulk_element_pt->nnode();
5557  //Find the number of position types in the bulk element
5558  const unsigned n_position_type_bulk =
5559  Bulk_element_pt->nnodal_position_type();
5560 
5561  //Construct the local coordinate in the bulk element
5562  Vector<double> s_bulk(2);
5563  //Get the local coordinates in the bulk element
5564  get_local_coordinate_in_bulk(s,s_bulk);
5565 
5566  //Allocate storage for the shape functions and their derivatives wrt
5567  //local coordinates
5568  Shape psi(n_node_bulk,n_position_type_bulk);
5569  DShape dpsids(n_node_bulk,n_position_type_bulk,2);
5570  //Get the value of the shape functions at the given local coordinate
5571  Bulk_element_pt->dshape_local(s_bulk,psi,dpsids);
5572 
5573  //Calculate all derivatives of the spatial coordinates
5574  //wrt local coordinates
5575  DenseMatrix<double> interpolated_dxds(2,spatial_dim,0.0);
5576 
5577  //Loop over all parent nodes
5578  for(unsigned l=0;l<n_node_bulk;l++)
5579  {
5580  //Loop over all position types in the bulk
5581  for(unsigned k=0;k<n_position_type_bulk;k++)
5582  {
5583  //Loop over derivative direction
5584  for(unsigned j=0;j<2;j++)
5585  {
5586  //Loop over coordinate directions
5587  for(unsigned i=0;i<spatial_dim;i++)
5588  {
5589  //Compute the spatial derivative
5590  interpolated_dxds(j,i) +=
5591  Bulk_element_pt->nodal_position_gen(l,k,i)*dpsids(l,k,j);
5592  }
5593  }
5594  }
5595  }
5596 
5597  //Initialise the tangent, interior tangent and normal vectors to zero
5598  //The idea is that even if the element is in a two-dimensional space,
5599  //the normal cannot be calculated without embedding the element in three
5600  //dimensions, in which case, the tangent and interior tangent will have
5601  //zero z-components.
5602  Vector<double> tangent(3,0.0), interior_tangent(3,0.0), normal(3,0.0);
5603 
5604  //We must get the relationship between the coordinate along the face
5605  //and the local coordinates in the bulk element
5606  //We must also find an interior direction
5607  DenseMatrix<double> dsbulk_dsface(2,1,0.0);
5608  unsigned interior_direction=0;
5609  get_ds_bulk_ds_face(s,dsbulk_dsface,interior_direction);
5610  //Load in the values for the tangents
5611  for(unsigned i=0;i<spatial_dim;i++)
5612  {
5613  //Tangent to the face is the derivative wrt to the face coordinate
5614  //which is calculated using dsbulk_dsface and the chain rule
5615  tangent[i] = interpolated_dxds(0,i)*dsbulk_dsface(0,0)
5616  + interpolated_dxds(1,i)*dsbulk_dsface(1,0);
5617  //Interior tangent to the face is the derivative in the interior
5618  //direction
5619  interior_tangent[i] = interpolated_dxds(interior_direction,i);
5620  }
5621 
5622  //Now the (3D) normal to the element is the interior tangent
5623  //crossed with the tangent
5624  VectorHelpers::cross(interior_tangent,tangent,normal);
5625 
5626  //We find the line normal by crossing the element normal with the tangent
5627  Vector<double> full_normal(3);
5628  VectorHelpers::cross(normal,tangent,full_normal);
5629 
5630  //Copy the appropriate entries into the unit normal
5631  //Two or Three depending upon the spatial dimension of the system
5632  for(unsigned i=0;i<spatial_dim;i++) {unit_normal[i] = full_normal[i];}
5633 
5634  //Finally normalise unit normal and multiply by the Normal_sign
5635  double length = VectorHelpers::magnitude(unit_normal);
5636  for(unsigned i=0;i<spatial_dim;i++)
5637  {unit_normal[i] *= Normal_sign/length;}
5638 
5639  // Create the tangent vector
5640  tang_vec[0][0]=-unit_normal[1];
5641  tang_vec[0][1]=unit_normal[0];
5642  }
5643  break;
5644 
5645  //Plane element, derived from 3D element, in this case the normal
5646  //is just the cross product of the two surface tangents
5647  //We assume, therefore, that we have three spatial coordinates
5648  //and two surface coordinates
5649  //Then we need only to get the derivatives wrt the local coordinates
5650  //in this face element
5651  case 2:
5652  {
5653 #ifdef PARANOID
5654  //Check that we actually have three spatial dimensions
5655  if(spatial_dim != 3)
5656  {
5657  std::ostringstream error_stream;
5658  error_stream << "There are only " << spatial_dim
5659  << "coordinates at the nodes of this 2D FaceElement,\n"
5660  << "which must have come from a 3D Bulk element\n";
5661  throw OomphLibError(error_stream.str(),
5662  OOMPH_CURRENT_FUNCTION,
5663  OOMPH_EXCEPTION_LOCATION);
5664  }
5665 #endif
5666 
5667  //Find the number of nodes in the element
5668  const unsigned n_node = this->nnode();
5669  //Find the number of position types
5670  const unsigned n_position_type = this->nnodal_position_type();
5671 
5672  //Allocate storage for the shape functions and their derivatives wrt
5673  //local coordinates
5674  Shape psi(n_node,n_position_type);
5675  DShape dpsids(n_node,n_position_type,2);
5676  //Get the value of the shape functions at the given local coordinate
5677  this->dshape_local(s,psi,dpsids);
5678 
5679  //Calculate all derivatives of the spatial coordinates
5680  //wrt local coordinates
5681  Vector<Vector<double> > interpolated_dxds(2,Vector<double>(3,0));
5682 
5683  //Loop over all nodes
5684  for(unsigned l=0;l<n_node;l++)
5685  {
5686  //Loop over all position types
5687  for(unsigned k=0;k<n_position_type;k++)
5688  {
5689  //Loop over derivative directions
5690  for(unsigned j=0;j<2;j++)
5691  {
5692  //Loop over coordinate directions
5693  for(unsigned i=0;i<3;i++)
5694  {
5695  //Compute the spatial derivative
5696  //Remember that we need to translate the position type
5697  //to its location in the bulk node
5698  interpolated_dxds[j][i] +=
5699  this->nodal_position_gen(l,bulk_position_type(k),i)*dpsids(l,k,j);
5700  }
5701  }
5702  }
5703  }
5704 
5705  // We now take the cross product of the two normal vectors
5706  VectorHelpers::cross(interpolated_dxds[0],interpolated_dxds[1],
5707  unit_normal);
5708 
5709  // Finally normalise unit normal
5710  double normal_length = VectorHelpers::magnitude(unit_normal);
5711 
5712  for(unsigned i=0;i<spatial_dim;i++)
5713  {unit_normal[i] *= Normal_sign/normal_length;}
5714 
5715  // Next we create the continuous tangent vectors!
5716  if(Tangent_direction_pt != 0)
5717  // There is a general direction that the first tangent vector should follow.
5718  {
5719 
5720  // Project the Tangent direction vector onto the surface.
5721  // Project Tangent_direction_pt D onto the plane P defined by
5722  // T1 and T2:
5723  // proj_P(D) = proj_T1(D) + proj_T2(D), where D is Tangent_direction_pt,
5724  // recall that proj_u(v) = (u.v)/(u.u) * u
5725 
5726  // Get the direction vector. The vector is NOT copied! :)
5727  Vector<double> &direction_vector = *Tangent_direction_pt;
5728 
5729 #ifdef PARANOID
5730  // Check that the angle between the direction vector and the normal
5731  // is not less than 10 degrees
5732  if(VectorHelpers::angle(direction_vector,unit_normal) <
5733  (10.0 * MathematicalConstants::Pi/180.0))
5734  {
5735  std::ostringstream err_stream;
5736  err_stream << "The angle between the unit normal and the \n"
5737  << "direction vector is less than 10 degrees.";
5738  throw OomphLibError(err_stream.str(),
5739  OOMPH_CURRENT_FUNCTION,
5740  OOMPH_EXCEPTION_LOCATION);
5741  }
5742 #endif
5743 
5744  // Calculate the two scalings, (u.v) / (u.u)
5745  double t1_scaling
5746  = VectorHelpers::dot(interpolated_dxds[0],direction_vector) /
5747  VectorHelpers::dot(interpolated_dxds[0],interpolated_dxds[0]);
5748 
5749  double t2_scaling
5750  = VectorHelpers::dot(interpolated_dxds[1],direction_vector) /
5751  VectorHelpers::dot(interpolated_dxds[1],interpolated_dxds[1]);
5752 
5753  // Finish off the projection.
5754  tang_vec[0][0] = t1_scaling*interpolated_dxds[0][0]
5755  + t2_scaling*interpolated_dxds[1][0];
5756  tang_vec[0][1] = t1_scaling*interpolated_dxds[0][1]
5757  + t2_scaling*interpolated_dxds[1][1];
5758  tang_vec[0][2] = t1_scaling*interpolated_dxds[0][2]
5759  + t2_scaling*interpolated_dxds[1][2];
5760 
5761  // The second tangent vector is the cross product of
5762  // tang_vec[0] and the normal vector N.
5763  VectorHelpers::cross(tang_vec[0],unit_normal,tang_vec[1]);
5764 
5765  // Normalise the tangent vectors
5766  for(unsigned vec_i=0; vec_i<2; vec_i++)
5767  {
5768  // Get the length...
5769  double tang_length = VectorHelpers::magnitude(tang_vec[vec_i]);
5770 
5771  for(unsigned dim_i=0;dim_i<spatial_dim;dim_i++)
5772  {tang_vec[vec_i][dim_i] /= tang_length;}
5773  }
5774  }
5775  else
5776  // A direction for the first tangent vector is not supplied, we do our best
5777  // to keep it continuous. But if the normal vector is aligned with the
5778  // z axis, then the tangent vector may "flip", even within elements.
5779  // This is because we check this by comparing doubles.
5780  // The code is copied from impose_parallel_outflow_element.h,
5781  // NO modification is done except for a few variable name changes.
5782  {
5783  double a,b,c;
5784  a=unit_normal[0];
5785  b=unit_normal[1];
5786  c=unit_normal[2];
5787 
5788  if(a!=0.0 || b!=0.0)
5789  {
5790  double a_sq=a*a;
5791  double b_sq=b*b;
5792  double c_sq=c*c;
5793 
5794  tang_vec[0][0]=-b/sqrt(a_sq+b_sq);
5795  tang_vec[0][1]= a/sqrt(a_sq+b_sq);
5796  tang_vec[0][2]=0;
5797 
5798  double z=(a_sq +b_sq)
5799  /sqrt(a_sq*c_sq +b_sq*c_sq +(a_sq +b_sq)*(a_sq +b_sq)) ;
5800 
5801  tang_vec[1][0]=-(a*c*z)/(a_sq + b_sq) ;
5802  tang_vec[1][1]= -(b*c*z)/(a_sq + b_sq);
5803  tang_vec[1][2]= z;
5804  // NB : we didn't use the fact that N is normalized,
5805  // that's why we have these unsimplified formulas
5806  }
5807  else if (c!=0.0)
5808  {
5809  if(!FaceElement::Ignore_discontinuous_tangent_warning)
5810  {
5811  std::ostringstream warn_stream;
5812  warn_stream << "I have detected that your normal vector is [0,0,1].\n"
5813  << "Since this function compares against 0.0, you may\n"
5814  << "get discontinuous tangent vectors.";
5815  OomphLibWarning(warn_stream.str(),
5816  OOMPH_CURRENT_FUNCTION,
5817  OOMPH_EXCEPTION_LOCATION);
5818  }
5819 
5820  tang_vec[0][0]= 1.0;
5821  tang_vec[0][1]= 0.0;
5822  tang_vec[0][2]= 0.0;
5823 
5824  tang_vec[1][0]=0.0;
5825  tang_vec[1][1]= 1.0;
5826  tang_vec[1][2]= 0.0;
5827  }
5828  else
5829  {
5830  throw
5831  OomphLibError("You have a zero normal vector!! ",
5832  OOMPH_CURRENT_FUNCTION,
5833  OOMPH_EXCEPTION_LOCATION);
5834  }
5835  }
5836 
5837  }
5838  break;
5839 
5840  default:
5841 
5842  throw OomphLibError(
5843  "Cannot have a FaceElement with dimension higher than 2",
5844  OOMPH_CURRENT_FUNCTION,
5845  OOMPH_EXCEPTION_LOCATION);
5846  break;
5847  }
5848 
5849 }
5850 
5851 //=======================================================================
5852 /// \short Compute the tangent vector(s) and the outer unit normal
5853 /// vector at the ipt-th integration point. This is a wrapper around
5854 /// continuous_tangent_and_outer_unit_normal(...) with the integration points
5855 /// converted into local coordinates.
5856 //=======================================================================
5858 (const unsigned &ipt, Vector<Vector<double> > &tang_vec,
5859  Vector<double> &unit_normal) const
5860 {
5861  //Find the dimension of the element
5862  const unsigned element_dim = dim();
5863  //Find the local coordinates of the ipt-th integration point
5864  Vector<double> s(element_dim);
5865  for(unsigned i=0;i<element_dim;i++) {s[i] = integral_pt()->knot(ipt,i);}
5866  //Call the outer unit normal function
5867  continuous_tangent_and_outer_unit_normal(s, tang_vec,unit_normal);
5868 }
5869 
5870 //=======================================================================
5871 /// Compute the outer unit normal at the specified local coordinate
5872 //=======================================================================
5874  Vector<double> &unit_normal) const
5875  {
5876  //Find the spatial dimension of the FaceElement
5877  const unsigned element_dim = dim();
5878 
5879  //Find the overall dimension of the problem
5880  //(assume that it's the same for all nodes)
5881  const unsigned spatial_dim = nodal_dimension();
5882 
5883 #ifdef PARANOID
5884  //Check the number of local coordinates passed
5885  if(s.size()!=element_dim)
5886  {
5887  std::ostringstream error_stream;
5888  error_stream
5889  << "Local coordinate s passed to outer_unit_normal() has dimension "
5890  << s.size() << std::endl
5891  << "but element dimension is " << element_dim << std::endl;
5892 
5893  throw OomphLibError(error_stream.str(),
5894  OOMPH_CURRENT_FUNCTION,
5895  OOMPH_EXCEPTION_LOCATION);
5896  }
5897 
5898  //Check the dimension of the normal vector
5899  if(unit_normal.size()!=spatial_dim)
5900  {
5901  std::ostringstream error_stream;
5902  error_stream
5903  << "Unit normal passed to outer_unit_normal() has dimension "
5904  << unit_normal.size() << std::endl
5905  << "but spatial dimension is " << spatial_dim << std::endl;
5906 
5907  throw OomphLibError(error_stream.str(),
5908  OOMPH_CURRENT_FUNCTION,
5909  OOMPH_EXCEPTION_LOCATION);
5910  }
5911 #endif
5912 
5913 /* //The spatial dimension of the bulk element will be element_dim+1
5914  const unsigned bulk_dim = element_dim + 1;
5915 
5916  //Find the number of nodes in the bulk element
5917  const unsigned n_node_bulk = Bulk_element_pt->nnode();
5918  //Find the number of position types in the bulk element
5919  const unsigned n_position_type_bulk =
5920  Bulk_element_pt->nnodal_position_type();
5921 
5922  //Construct the local coordinate in the bulk element
5923  Vector<double> s_bulk(bulk_dim);
5924  //Set the value of the bulk coordinate that is fixed on the face
5925  //s_bulk[s_fixed_index()] = s_fixed_value();
5926 
5927  //Set the other bulk coordinates
5928  //for(unsigned i=0;i<element_dim;i++) {s_bulk[bulk_s_index(i)] = s[i];}
5929 
5930  get_local_coordinate_in_bulk(s,s_bulk);
5931 
5932  //Allocate storage for the shape functions and their derivatives wrt
5933  //local coordinates
5934  Shape psi(n_node_bulk,n_position_type_bulk);
5935  DShape dpsids(n_node_bulk,n_position_type_bulk,bulk_dim);
5936  //Get the value of the shape functions at the given local coordinate
5937  Bulk_element_pt->dshape_local(s_bulk,psi,dpsids);
5938 
5939  //Calculate all derivatives of the spatial coordinates wrt local coordinates
5940  DenseMatrix<double> interpolated_dxds(bulk_dim,spatial_dim);
5941  //Initialise to zero
5942  for(unsigned j=0;j<bulk_dim;j++)
5943  {for(unsigned i=0;i<spatial_dim;i++) {interpolated_dxds(j,i) = 0.0;}}
5944 
5945  //Loop over all parent nodes
5946  for(unsigned l=0;l<n_node_bulk;l++)
5947  {
5948  //Loop over all position types in the bulk
5949  for(unsigned k=0;k<n_position_type_bulk;k++)
5950  {
5951  //Loop over derivative direction
5952  for(unsigned j=0;j<bulk_dim;j++)
5953  {
5954  //Loop over coordinate directions
5955  for(unsigned i=0;i<spatial_dim;i++)
5956  {
5957  //Compute the spatial derivative
5958  interpolated_dxds(j,i) +=
5959  Bulk_element_pt->nodal_position_gen(l,k,i)*dpsids(l,k,j);
5960  }
5961  }
5962  }
5963  }*/
5964 
5965  //Now let's consider the different element dimensions
5966  switch(element_dim)
5967  {
5968  //Point element, derived from a 1D element, in this case
5969  //the normal is merely the tangent to the bulk element
5970  //and there is only one free coordinate in the bulk element
5971  //Hence we will need to calculate the derivatives wrt the
5972  //local coordinates in the BULK element.
5973  case 0:
5974  {
5975  //Find the number of nodes in the bulk element
5976  const unsigned n_node_bulk = Bulk_element_pt->nnode();
5977  //Find the number of position types in the bulk element
5978  const unsigned n_position_type_bulk =
5979  Bulk_element_pt->nnodal_position_type();
5980 
5981  //Construct the local coordinate in the bulk element
5982  Vector<double> s_bulk(1);
5983 
5984  //Get the local coordinates in the bulk element
5985  get_local_coordinate_in_bulk(s,s_bulk);
5986 
5987  //Allocate storage for the shape functions and their derivatives wrt
5988  //local coordinates
5989  Shape psi(n_node_bulk,n_position_type_bulk);
5990  DShape dpsids(n_node_bulk,n_position_type_bulk,1);
5991  //Get the value of the shape functions at the given local coordinate
5992  Bulk_element_pt->dshape_local(s_bulk,psi,dpsids);
5993 
5994  //Calculate all derivatives of the spatial coordinates wrt
5995  //local coordinates
5996  DenseMatrix<double> interpolated_dxds(1,spatial_dim,0.0);
5997 
5998  //Loop over all parent nodes
5999  for(unsigned l=0;l<n_node_bulk;l++)
6000  {
6001  //Loop over all position types in the bulk
6002  for(unsigned k=0;k<n_position_type_bulk;k++)
6003  {
6004  //Loop over coordinate directions
6005  for(unsigned i=0;i<spatial_dim;i++)
6006  {
6007  //Compute the spatial derivative
6008  interpolated_dxds(0,i) +=
6009  Bulk_element_pt->nodal_position_gen(l,k,i)*dpsids(l,k,0);
6010  }
6011  }
6012  }
6013 
6014  //Now the unit normal is just the derivative of the position vector
6015  //with respect to the single coordinate
6016  for(unsigned i=0;i<spatial_dim;i++)
6017  {unit_normal[i] = interpolated_dxds(0,i);}
6018  }
6019  break;
6020 
6021  //Line element, derived from a 2D element, in this case
6022  //the normal is a mess of cross products
6023  //We need an interior direction, so we must find the local
6024  //derivatives in the BULK element
6025  case 1:
6026  {
6027  //Find the number of nodes in the bulk element
6028  const unsigned n_node_bulk = Bulk_element_pt->nnode();
6029  //Find the number of position types in the bulk element
6030  const unsigned n_position_type_bulk =
6031  Bulk_element_pt->nnodal_position_type();
6032 
6033  //Construct the local coordinate in the bulk element
6034  Vector<double> s_bulk(2);
6035  //Get the local coordinates in the bulk element
6036  get_local_coordinate_in_bulk(s,s_bulk);
6037 
6038  //Allocate storage for the shape functions and their derivatives wrt
6039  //local coordinates
6040  Shape psi(n_node_bulk,n_position_type_bulk);
6041  DShape dpsids(n_node_bulk,n_position_type_bulk,2);
6042  //Get the value of the shape functions at the given local coordinate
6043  Bulk_element_pt->dshape_local(s_bulk,psi,dpsids);
6044 
6045  //Calculate all derivatives of the spatial coordinates
6046  //wrt local coordinates
6047  DenseMatrix<double> interpolated_dxds(2,spatial_dim,0.0);
6048 
6049  //Loop over all parent nodes
6050  for(unsigned l=0;l<n_node_bulk;l++)
6051  {
6052  //Loop over all position types in the bulk
6053  for(unsigned k=0;k<n_position_type_bulk;k++)
6054  {
6055  //Loop over derivative direction
6056  for(unsigned j=0;j<2;j++)
6057  {
6058  //Loop over coordinate directions
6059  for(unsigned i=0;i<spatial_dim;i++)
6060  {
6061  //Compute the spatial derivative
6062  interpolated_dxds(j,i) +=
6063  Bulk_element_pt->nodal_position_gen(l,k,i)*dpsids(l,k,j);
6064  }
6065  }
6066  }
6067  }
6068 
6069  //Initialise the tangent, interior tangent and normal vectors to zero
6070  //The idea is that even if the element is in a two-dimensional space,
6071  //the normal cannot be calculated without embedding the element in three
6072  //dimensions, in which case, the tangent and interior tangent will have
6073  //zero z-components.
6074  Vector<double> tangent(3,0.0), interior_tangent(3,0.0), normal(3,0.0);
6075 
6076  //We must get the relationship between the coordinate along the face
6077  //and the local coordinates in the bulk element
6078  //We must also find an interior direction
6079  DenseMatrix<double> dsbulk_dsface(2,1,0.0);
6080  unsigned interior_direction=0;
6081  get_ds_bulk_ds_face(s,dsbulk_dsface,interior_direction);
6082  //Load in the values for the tangents
6083  for(unsigned i=0;i<spatial_dim;i++)
6084  {
6085  //Tangent to the face is the derivative wrt to the face coordinate
6086  //which is calculated using dsbulk_dsface and the chain rule
6087  tangent[i] = interpolated_dxds(0,i)*dsbulk_dsface(0,0)
6088  + interpolated_dxds(1,i)*dsbulk_dsface(1,0);
6089  //Interior tangent to the face is the derivative in the interior
6090  //direction
6091  interior_tangent[i] = interpolated_dxds(interior_direction,i);
6092  }
6093 
6094  //Now the (3D) normal to the element is the interior tangent
6095  //crossed with the tangent
6096  VectorHelpers::cross(interior_tangent,tangent,normal);
6097 
6098  //We find the line normal by crossing the element normal with the tangent
6099  Vector<double> full_normal(3);
6100  VectorHelpers::cross(normal,tangent,full_normal);
6101 
6102  //Copy the appropriate entries into the unit normal
6103  //Two or Three depending upon the spatial dimension of the system
6104  for(unsigned i=0;i<spatial_dim;i++) {unit_normal[i] = full_normal[i];}
6105  }
6106  break;
6107 
6108  //Plane element, derived from 3D element, in this case the normal
6109  //is just the cross product of the two surface tangents
6110  //We assume, therefore, that we have three spatial coordinates
6111  //and two surface coordinates
6112  //Then we need only to get the derivatives wrt the local coordinates
6113  //in this face element
6114  case 2:
6115  {
6116 #ifdef PARANOID
6117  //Check that we actually have three spatial dimensions
6118  if(spatial_dim != 3)
6119  {
6120  std::ostringstream error_stream;
6121  error_stream << "There are only " << spatial_dim
6122  << "coordinates at the nodes of this 2D FaceElement,\n"
6123  << "which must have come from a 3D Bulk element\n";
6124  throw OomphLibError(error_stream.str(),
6125  OOMPH_CURRENT_FUNCTION,
6126  OOMPH_EXCEPTION_LOCATION);
6127  }
6128 #endif
6129 
6130  //Find the number of nodes in the element
6131  const unsigned n_node = this->nnode();
6132  //Find the number of position types
6133  const unsigned n_position_type = this->nnodal_position_type();
6134 
6135  //Allocate storage for the shape functions and their derivatives wrt
6136  //local coordinates
6137  Shape psi(n_node,n_position_type);
6138  DShape dpsids(n_node,n_position_type,2);
6139  //Get the value of the shape functions at the given local coordinate
6140  this->dshape_local(s,psi,dpsids);
6141 
6142  //Calculate all derivatives of the spatial coordinates
6143  //wrt local coordinates
6144  Vector<Vector<double> > interpolated_dxds(2,Vector<double>(3,0));
6145 
6146  //Loop over all nodes
6147  for(unsigned l=0;l<n_node;l++)
6148  {
6149  //Loop over all position types
6150  for(unsigned k=0;k<n_position_type;k++)
6151  {
6152  //Loop over derivative directions
6153  for(unsigned j=0;j<2;j++)
6154  {
6155  //Loop over coordinate directions
6156  for(unsigned i=0;i<3;i++)
6157  {
6158  //Compute the spatial derivative
6159  //Remember that we need to translate the position type
6160  //to its location in the bulk node
6161  interpolated_dxds[j][i] +=
6162  this->nodal_position_gen(l,bulk_position_type(k),i)*dpsids(l,k,j);
6163  }
6164  }
6165  }
6166  }
6167 
6168  //We now take the cross product of the two normal vectors
6169  VectorHelpers::cross(interpolated_dxds[0],interpolated_dxds[1],
6170  unit_normal);
6171  }
6172  break;
6173 
6174  default:
6175 
6176  throw OomphLibError(
6177  "Cannot have a FaceElement with dimension higher than 2",
6178  OOMPH_CURRENT_FUNCTION,
6179  OOMPH_EXCEPTION_LOCATION);
6180  break;
6181  }
6182 
6183  //Finally normalise unit normal
6184  double length = VectorHelpers::magnitude(unit_normal);
6185  for(unsigned i=0;i<spatial_dim;i++)
6186  {unit_normal[i] *= Normal_sign/length;}
6187  }
6188 
6189 //=======================================================================
6190 /// Compute the outer unit normal at the ipt-th integration point
6191 //=======================================================================
6192  void FaceElement::outer_unit_normal(const unsigned &ipt,
6193  Vector<double> &unit_normal) const
6194  {
6195  //Find the dimension of the element
6196  const unsigned element_dim = dim();
6197  //Find the local coordiantes of the ipt-th integration point
6198  Vector<double> s(element_dim);
6199  for(unsigned i=0;i<element_dim;i++) {s[i] = integral_pt()->knot(ipt,i);}
6200  //Call the outer unit normal function
6201  outer_unit_normal(s,unit_normal);
6202  }
6203 
6204 
6205 
6206 //=======================================================================
6207 /// Return vector of local coordinates in bulk element,
6208 /// given the local coordinates in this FaceElement
6209 //=======================================================================
6211  const Vector<double>& s) const
6212  {
6213  //Find the dimension of the bulk element
6214  unsigned dim_bulk = Bulk_element_pt->dim();
6215 
6216  // Vector of local coordinates in bulk element
6217  Vector<double> s_bulk(dim_bulk);
6218 
6219  //Use the function pointer if it is set
6220  if(Face_to_bulk_coordinate_fct_pt)
6221  {
6222  //Call the translation function
6223  (*Face_to_bulk_coordinate_fct_pt)(s,s_bulk);
6224  }
6225  else
6226  {
6227  throw OomphLibError("Face_to_bulk_coordinate mapping not set",
6228  OOMPH_CURRENT_FUNCTION,
6229  OOMPH_EXCEPTION_LOCATION);
6230  }
6231 
6232  // Return it
6233  return s_bulk;
6234  }
6235 
6236 
6237 
6238 //=======================================================================
6239 /// Calculate the vector of local coordinates in bulk element,
6240 /// given the local coordinates in this FaceElement
6241 //=======================================================================
6243  const Vector<double>& s, Vector<double> &s_bulk) const
6244  {
6245  //Use the function pointer if it is set
6246  if(Face_to_bulk_coordinate_fct_pt)
6247  {
6248  //Call the translation function
6249  (*Face_to_bulk_coordinate_fct_pt)(s,s_bulk);
6250  }
6251  //Otherwise use the existing (not general) interface
6252  else
6253  {
6254  throw OomphLibError("Face_to_bulk_coordinate mapping not set",
6255  OOMPH_CURRENT_FUNCTION,
6256  OOMPH_EXCEPTION_LOCATION);
6257  }
6258  }
6259 
6260 
6261 //=======================================================================
6262 /// Calculate the derivatives of the local coordinates in the
6263 /// bulk element with respect to the local coordinates in this FaceElement.
6264 /// In addition return the index of a bulk local coordinate that varies away
6265 /// from the face.
6266 //=======================================================================
6268  DenseMatrix<double> &dsbulk_dsface,
6269  unsigned &interior_direction) const
6270  {
6271  //Use the function pointer if it is set
6272  if(Bulk_coordinate_derivatives_fct_pt)
6273  {
6274  //Call the translation function
6275  (*Bulk_coordinate_derivatives_fct_pt)(s,dsbulk_dsface,interior_direction);
6276  }
6277  //Otherwise throw an error
6278  else
6279  {
6280  throw OomphLibError(
6281  "No function for derivatives of bulk coords wrt face coords set",
6282  OOMPH_CURRENT_FUNCTION,
6283  OOMPH_EXCEPTION_LOCATION);
6284  }
6285  }
6286 
6287 
6288 
6289 ///////////////////////////////////////////////////////////////////////////
6290 ///////////////////////////////////////////////////////////////////////////
6291 // Functions for elastic general elements
6292 ///////////////////////////////////////////////////////////////////////////
6293 ///////////////////////////////////////////////////////////////////////////
6294 
6295 
6296 
6297 //==================================================================
6298 /// Calculate the L2 norm of the displacement u=R-r to overload the
6299 /// compute_norm function in the GeneralisedElement base class
6300 //==================================================================
6301  void SolidFiniteElement::compute_norm(double& el_norm)
6302  {
6303  // Initialise el_norm to 0.0
6304  el_norm=0.0;
6305 
6306  unsigned n_dim=dim();
6307 
6308  // Vector of local coordinates
6309  Vector<double> s(n_dim);
6310 
6311  // Displacement vector, Lagrangian coordinate vector and Eulerian
6312  // coordinate vector respectively
6313  Vector<double> disp(n_dim,0.0);
6314  Vector<double> xi(n_dim,0.0);
6315  Vector<double> x(n_dim,0.0);
6316 
6317  // Find out how many nodes there are in the element
6318  unsigned n_node=this->nnode();
6319 
6320  // Construct a shape function
6321  Shape psi(n_node);
6322 
6323  // Get the number of integration points
6324  unsigned n_intpt=this->integral_pt()->nweight();
6325 
6326  // Loop over the integration points
6327  for(unsigned ipt=0;ipt<n_intpt;ipt++)
6328  {
6329  // Assign values of s
6330  for(unsigned i=0;i<n_dim;i++)
6331  {
6332  s[i]=this->integral_pt()->knot(ipt,i);
6333  }
6334 
6335  // Get the integral weight
6336  double w=this->integral_pt()->weight(ipt);
6337 
6338  // Get jacobian of mapping
6339  double J=this->J_eulerian(s);
6340 
6341  // Premultiply the weights and the Jacobian
6342  double W=w*J;
6343 
6344  // Get the Lagrangian and Eulerian coordinate at this point, respectively
6345  this->interpolated_xi(s,xi);
6346  this->interpolated_x(s,x);
6347 
6348  // Calculate the displacement vector, u=R-r=x-xi
6349  for (unsigned idim=0;idim<n_dim;idim++)
6350  {
6351  disp[idim]=x[idim]-xi[idim];
6352  }
6353 
6354  // Add to norm
6355  for (unsigned ii=0;ii<n_dim;ii++)
6356  {
6357  el_norm+=(disp[ii]*disp[ii])*W;
6358  }
6359  }
6360  } // End of compute_norm(...)
6361 
6362 
6363 
6364 //=========================================================================
6365 /// \short Function to describe the local dofs of the element. The ostream
6366 /// specifies the output stream to which the description
6367 /// is written; the string stores the currently
6368 /// assembled output that is ultimately written to the
6369 /// output stream by Data::describe_dofs(...); it is typically
6370 /// built up incrementally as we descend through the
6371 /// call hierarchy of this function when called from
6372 /// Problem::describe_dofs(...)
6373 //=========================================================================
6375 describe_local_dofs(std::ostream& out,const std::string& current_string) const
6376 {
6377  // Call the standard finite element description.
6378  FiniteElement::describe_local_dofs(out,current_string);
6379  describe_solid_local_dofs(out,current_string);
6380 }
6381 
6382 
6383 //=========================================================================
6384 /// Internal function that is used to assemble the jacobian of the mapping
6385 /// from local coordinates (s) to the lagrangian coordinates (xi), given the
6386 /// derivatives of the shape functions.
6387 //=========================================================================
6390  DenseMatrix<double> &jacobian) const
6391  {
6392  //Find the the dimension of the element
6393  const unsigned el_dim = dim();
6394  //Find the number of shape functions and shape functions types
6395  //We shall ASSUME (ENFORCE) that Lagrangian coordinates must
6396  //be interpolated through the nodes
6397  const unsigned n_shape = nnode();
6398  const unsigned n_shape_type = nnodal_lagrangian_type();
6399 
6400 #ifdef PARANOID
6401  //Check for dimensional compatibility
6402  if(el_dim != Lagrangian_dimension)
6403  {
6404  std::ostringstream error_message;
6405  error_message << "Dimension mismatch" << std::endl;
6406  error_message << "The elemental dimension: " << el_dim
6407  << " must equal the nodal Lagrangian dimension: "
6408  << Lagrangian_dimension
6409  << " for the jacobian of the mapping to be well-defined"
6410  << std::endl;
6411  throw OomphLibError(error_message.str(),
6412  OOMPH_CURRENT_FUNCTION,
6413  OOMPH_EXCEPTION_LOCATION);
6414  }
6415 #endif
6416 
6417  //Loop over the rows of the jacobian
6418  for(unsigned i=0;i<el_dim;i++)
6419  {
6420  //Loop over the columns of the jacobian
6421  for(unsigned j=0;j<el_dim;j++)
6422  {
6423  //Zero the entry
6424  jacobian(i,j) = 0.0;
6425  //Loop over the shape functions
6426  for(unsigned l=0;l<n_shape;l++)
6427  {
6428  for(unsigned k=0;k<n_shape_type;k++)
6429  {
6430  //Jacobian is dx_j/ds_i, which is represented by the sum
6431  //over the dpsi/ds_i of the nodal points X j
6432  //Call the Non-hanging version of positions
6433  //This is overloaded in refineable elements
6434  jacobian(i,j) += raw_lagrangian_position_gen(l,k,j)*dpsids(l,k,i);
6435  }
6436  }
6437  }
6438  }
6439  }
6440 
6441 //=========================================================================
6442 /// Internal function that is used to assemble the jacobian of second
6443 /// derivatives of the the mapping from local coordinates (s) to the
6444 /// lagrangian coordinates (xi), given the second derivatives of the
6445 /// shape functions.
6446 //=========================================================================
6449  DenseMatrix<double> &jacobian2) const
6450  {
6451  //Find the the dimension of the element
6452  const unsigned el_dim = dim();
6453  //Find the number of shape functions and shape functions types
6454  //We ENFORCE that Lagrangian coordinates must be interpolated through
6455  //the nodes
6456  const unsigned n_shape = nnode();
6457  const unsigned n_shape_type = nnodal_lagrangian_type();
6458  //Find the number of second derivatives
6459  const unsigned n_row = N2deriv[el_dim];
6460 
6461  //Assemble the "jacobian" (d^2 x_j/ds_i^2) for second derivatives of
6462  //shape functions
6463  //Loop over the rows (number of second derivatives)
6464  for(unsigned i=0;i<n_row;i++)
6465  {
6466  //Loop over the columns (element dimension
6467  for(unsigned j=0;j<el_dim;j++)
6468  {
6469  //Zero the entry
6470  jacobian2(i,j) = 0.0;
6471  //Loop over the shape functions
6472  for(unsigned l=0;l<n_shape;l++)
6473  {
6474  //Loop over the shape function types
6475  for(unsigned k=0;k<n_shape_type;k++)
6476  {
6477  //Add the terms to the jacobian entry
6478  //Call the Non-hanging version of positions
6479  //This is overloaded in refineable elements
6480  jacobian2(i,j) += raw_lagrangian_position_gen(l,k,j)*d2psids(l,k,i);
6481  }
6482  }
6483  }
6484  }
6485  }
6486 
6487 //============================================================================
6488 /// \short Destructor for SolidFiniteElement:
6489 //============================================================================
6491  {
6492  //Delete the storage allocated for the positional local equations
6493  delete[] Position_local_eqn;
6494  }
6495 
6496 
6497 //==========================================================================
6498 /// Calculate the mapping from local to lagrangian coordinates
6499 /// assuming that the coordinates are aligned in the direction of the local
6500 /// coordinates, i.e. there are no cross terms and the jacobian is diagonal.
6501 /// The local derivatives are passed as dpsids and the jacobian and
6502 /// inverse jacobian are returned.
6503 //==========================================================================
6504  double SolidFiniteElement::
6506  DenseMatrix<double> &jacobian,
6507  DenseMatrix<double> &inverse_jacobian)
6508  const
6509  {
6510  //Find the dimension of the element
6511  const unsigned el_dim = dim();
6512  //Find the number of shape functions and shape functions types
6513  //We shall ASSUME (ENFORCE) that Lagrangian coordinates must
6514  //be interpolated through the nodes
6515  const unsigned n_shape = nnode();
6516  const unsigned n_shape_type = nnodal_lagrangian_type();
6517 
6518  //In this case we assume that there are no cross terms, that is
6519  //global coordinate 0 is always in the direction of local coordinate 0
6520 
6521  //Loop over the coordinates
6522  for(unsigned i=0;i<el_dim;i++)
6523  {
6524  //Zero the jacobian and inverse jacobian entries
6525  for(unsigned j=0;j<el_dim;j++)
6526  {jacobian(i,j) = 0.0; inverse_jacobian(i,j) = 0.0;}
6527 
6528  //Loop over the shape functions
6529  for(unsigned l=0;l<n_shape;l++)
6530  {
6531  //Loop over the types of dof
6532  for(unsigned k=0;k<n_shape_type;k++)
6533  {
6534  //Derivatives are always dx_{i}/ds_{i}
6535  jacobian(i,i) += raw_lagrangian_position_gen(l,k,i)*dpsids(l,k,i);
6536  }
6537  }
6538  }
6539 
6540  //Now calculate the determinant of the matrix
6541  double det = 1.0;
6542  for(unsigned i=0;i<el_dim;i++) {det *= jacobian(i,i);}
6543 
6544 //Report if Matrix is singular, or negative
6545 #ifdef PARANOID
6546  check_jacobian(det);
6547 #endif
6548 
6549  //Calculate the inverse mapping (trivial in this case)
6550  for(unsigned i=0;i<el_dim;i++)
6551  {inverse_jacobian(i,i) = 1.0/jacobian(i,i);}
6552 
6553  //Return the value of the Jacobian
6554  return(det);
6555  }
6556 
6557 //========================================================================
6558 /// Calculate shape functions and derivatives w.r.t. Lagrangian
6559 /// coordinates at local coordinate s. Returns the Jacobian of the mapping
6560 /// from Lagrangian to local coordinates.
6561 /// General case, may be overloaded
6562 //========================================================================
6563  double SolidFiniteElement::
6564  dshape_lagrangian(const Vector<double> &s, Shape &psi, DShape &dpsi) const
6565  {
6566  //Find the element dimension
6567  const unsigned el_dim = dim();
6568 
6569  //Get the values of the shape function and local derivatives
6570  //Temporarily stored in dpsi
6571  dshape_local(s,psi,dpsi);
6572 
6573  //Allocate memory for the inverse jacobian
6574  DenseMatrix<double> inverse_jacobian(el_dim);
6575  //Now calculate the inverse jacobian
6576  const double det = local_to_lagrangian_mapping(dpsi,inverse_jacobian);
6577 
6578  //Now set the values of the derivatives to be dpsidxi
6579  transform_derivatives(inverse_jacobian,dpsi);
6580  //Return the determinant of the jacobian
6581  return det;
6582  }
6583 
6584 //=========================================================================
6585 /// Compute the geometric shape functions and also first
6586 /// derivatives w.r.t. Lagrangian coordinates at integration point ipt.
6587 /// Most general form of function, but may be over-loaded if desired
6588 //========================================================================
6590  Shape &psi,
6591  DShape &dpsi) const
6592  {
6593  //Find the element dimension
6594  const unsigned el_dim = dim();
6595 
6596  //Shape function for the local derivatives
6597  //Again we ASSUME (insist) that the lagrangian coordinates
6598  //are interpolated through the nodes
6599  //Get the values of the shape function and local derivatives
6600  dshape_local_at_knot(ipt,psi,dpsi);
6601 
6602  //Allocate memory for the inverse jacobian
6603  DenseMatrix<double> inverse_jacobian(el_dim);
6604  //Now calculate the inverse jacobian
6605  const double det = local_to_lagrangian_mapping(dpsi,inverse_jacobian);
6606 
6607  //Now set the values of the derivatives
6608  transform_derivatives(inverse_jacobian,dpsi);
6609  //Return the determinant of the jacobian
6610  return det;
6611  }
6612 
6613 //========================================================================
6614 /// \short Compute the geometric shape functions and also first
6615 /// and second derivatives w.r.t. Lagrangian coordinates at
6616 /// local coordinate s;
6617 /// Returns Jacobian of mapping from Lagrangian to local coordinates.
6618 /// Numbering:
6619 /// \b 1D:
6620 /// d2pidxi(i,0) = \f$ d^2 \psi_j / d \xi^2 \f$
6621 /// \b 2D:
6622 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6623 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6624 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j / \partial \xi_0 \partial \xi_1 \f$
6625 /// \b 3D:
6626 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6627 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6628 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j / \partial \xi_2^2 \f$
6629 /// d2psidxi(i,3) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_1 \f$
6630 /// d2psidxi(i,4) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_2 \f$
6631 /// d2psidxi(i,5) = \f$ \partial^2 \psi_j/\partial \xi_1 \partial \xi_2 \f$
6632 //========================================================================
6633  double SolidFiniteElement::
6635  DShape &dpsi, DShape &d2psi) const
6636  {
6637  //Find the element dimension
6638  const unsigned el_dim = dim();
6639  //Find the number of second derivatives required
6640  const unsigned n_deriv = N2deriv[el_dim];
6641 
6642  //Get the values of the shape function and local derivatives
6643  d2shape_local(s,psi,dpsi,d2psi);
6644 
6645  //Allocate memory for the jacobian and inverse jacobian
6646  DenseMatrix<double> jacobian(el_dim), inverse_jacobian(el_dim);
6647  //Calculate the jacobian and inverse jacobian
6648  const double det =
6649  local_to_lagrangian_mapping(dpsi,jacobian,inverse_jacobian);
6650 
6651  //Allocate memory for the jacobian of second derivatives
6652  DenseMatrix<double> jacobian2(n_deriv,el_dim);
6653  //Assemble the jacobian of second derivatives
6654  assemble_local_to_lagrangian_jacobian2(d2psi,jacobian2);
6655 
6656  //Now set the value of the derivatives
6657  transform_second_derivatives(jacobian,inverse_jacobian,
6658  jacobian2,dpsi,d2psi);
6659  //Return the determinant of the mapping
6660  return det;
6661  }
6662 
6663 //==========================================================================
6664 /// \short Compute the geometric shape functions and also first
6665 /// and second derivatives w.r.t. Lagrangian coordinates at
6666 /// the ipt-th integration point
6667 /// Returns Jacobian of mapping from Lagrangian to local coordinates.
6668 /// Numbering:
6669 /// \b 1D:
6670 /// d2pidxi(i,0) = \f$ d^2 \psi_j / d \xi^2 \f$
6671 /// \b 2D:
6672 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6673 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6674 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_1 \f$
6675 /// \b 3D:
6676 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6677 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6678 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j / \partial \xi_2^2 \f$
6679 /// d2psidxi(i,3) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_1 \f$
6680 /// d2psidxi(i,4) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_2 \f$
6681 /// d2psidxi(i,5) = \f$ \partial^2 \psi_j/\partial \xi_1 \partial \xi_2 \f$
6682 //========================================================================
6684  Shape &psi,
6685  DShape &dpsi,
6686  DShape &d2psi) const
6687  {
6688  //Find the values of the indices of the shape functions
6689  //Find the element dimension
6690  const unsigned el_dim = dim();
6691  //Find the number of second derivatives required
6692  const unsigned n_deriv = N2deriv[el_dim];
6693 
6694  //Get the values of the shape function and local derivatives
6695  d2shape_local_at_knot(ipt,psi,dpsi,d2psi);
6696 
6697  //Allocate memory for the jacobian and inverse jacobian
6698  DenseMatrix<double> jacobian(el_dim), inverse_jacobian(el_dim);
6699  //Calculate the jacobian and inverse jacobian
6700  const double det =
6701  local_to_lagrangian_mapping(dpsi,jacobian,inverse_jacobian);
6702 
6703  //Allocate memory for the jacobian of second derivatives
6704  DenseMatrix<double> jacobian2(n_deriv,el_dim);
6705  //Assemble the jacobian of second derivatives
6706  assemble_local_to_lagrangian_jacobian2(d2psi,jacobian2);
6707 
6708  //Now set the value of the derivatives
6709  transform_second_derivatives(jacobian,inverse_jacobian,
6710  jacobian2,dpsi,d2psi);
6711  //Return the determinant of the mapping
6712  return det;
6713  }
6714 
6715 //============================================================================
6716 /// \short Function to describe the local dofs of the element. The ostream
6717 /// specifies the output stream to which the description
6718 /// is written; the string stores the currently
6719 /// assembled output that is ultimately written to the
6720 /// output stream by Data::describe_dofs(...); it is typically
6721 /// built up incrementally as we descend through the
6722 /// call hierarchy of this function when called from
6723 /// Problem::describe_dofs(...)
6724 //============================================================================
6726 describe_solid_local_dofs(std::ostream& out,
6727  const std::string& current_string) const
6728 {
6729  //Find the number of nodes
6730  const unsigned n_node = nnode();
6731  //Loop over the nodes
6732  for(unsigned n=0;n<n_node;n++)
6733  {
6734  //Cast to a solid node
6735  SolidNode* cast_node_pt = static_cast<SolidNode*>(node_pt(n));
6736  std::stringstream conversion;
6737  conversion<<" of Solid Node "<<n<<current_string;
6738  std::string in(conversion.str());
6739  cast_node_pt->describe_dofs(out,in);
6740  }
6741 }
6742 
6743 //============================================================================
6744 /// Assign local equation numbers for the solid equations in the element.
6745 // This can be done at a high level assuming, as I am, that the equations will
6746 // always be formulated in terms of nodal positions.
6747 /// If the boolean argument is true then pointers to the dofs will be
6748 /// stored in Dof_pt.
6749 //============================================================================
6751  const bool &store_local_dof_pt)
6752  {
6753  //Find the number of nodes
6754  const unsigned n_node = nnode();
6755 
6756  //Check there are nodes!
6757  if(n_node > 0)
6758  {
6759  //Find the number of position types and dimensions of the nodes
6760  //Local caching
6761  const unsigned n_position_type = nnodal_position_type();
6762  const unsigned nodal_dim = nodal_dimension();
6763 
6764  //Delete the existing storage
6765  delete[] Position_local_eqn;
6766  //Resize the storage for the positional equation numbers
6767  Position_local_eqn = new int[n_node*n_position_type*nodal_dim];
6768 
6769  //A local queue to store the global equation numbers
6770  std::deque<unsigned long> global_eqn_number_queue;
6771 
6772  //Get the number of dofs so far, this must be outside both loops
6773  //so that both can use it
6774  unsigned local_eqn_number = ndof();
6775 
6776  //Loop over the nodes
6777  for(unsigned n=0;n<n_node;n++)
6778  {
6779  //Cast to a solid node
6780  SolidNode* cast_node_pt = static_cast<SolidNode*>(node_pt(n));
6781 
6782  //Loop over the number of position dofs
6783  for(unsigned j=0;j<n_position_type;j++)
6784  {
6785  //Loop over the dimension of each node
6786  for(unsigned k=0;k<nodal_dim;k++)
6787  {
6788  //Get equation number
6789  //Note eqn_number is long !
6790  long eqn_number = cast_node_pt->position_eqn_number(j,k);
6791  //If equation_number positive add to array
6792  if(eqn_number >= 0)
6793  {
6794  //Add to global array
6795  global_eqn_number_queue.push_back(eqn_number);
6796  //Add pointer to the dof to the queue if required
6797  if(store_local_dof_pt)
6798  {
6799  GeneralisedElement::Dof_pt_deque.push_back(
6800  &(cast_node_pt->x_gen(j,k)));
6801  }
6802 
6803  //Add to look-up scheme
6804  Position_local_eqn[(n*n_position_type + j)*nodal_dim + k] =
6805  local_eqn_number;
6806  //Increment the local equation number
6807  local_eqn_number++;
6808  }
6809  else
6810  {
6811  Position_local_eqn[(n*n_position_type + j)*nodal_dim + k] =
6813  }
6814  }
6815  }
6816  } //End of loop over nodes
6817 
6818  //Now add our global equations numbers to the internal element storage
6819  add_global_eqn_numbers(global_eqn_number_queue,
6820  GeneralisedElement::Dof_pt_deque);
6821  //Clear the memory used in the deque
6822  if(store_local_dof_pt)
6823  {std::deque<double*>().swap(GeneralisedElement::Dof_pt_deque);}
6824 
6825  } //End of the case when there are nodes
6826  }
6827 
6828 
6829 //============================================================================
6830 /// This function calculates the entries of Jacobian matrix, used in
6831 /// the Newton method, associated with the elastic problem in which the
6832 /// nodal position is a variable. It does this using finite differences,
6833 /// rather than an analytical formulation, so can be done in total generality.
6834 //==========================================================================
6837  DenseMatrix<double> &jacobian)
6838  {
6839 
6840  // Flag to indicate if we use first or second order FD
6841  // bool use_first_order_fd=false;
6842 
6843  //Find the number of nodes
6844  const unsigned n_node = nnode();
6845 
6846  //If there aren't any nodes, then return straight away
6847  if(n_node == 0) {return;}
6848 
6849  //Call the update function to ensure that the element is in
6850  //a consistent state before finite differencing starts
6851  update_before_solid_position_fd();
6852 
6853  //Get the number of position dofs and dimensions at the node
6854  const unsigned n_position_type = nnodal_position_type();
6855  const unsigned nodal_dim = nodal_dimension();
6856 
6857  //Find the number of dofs in the element
6858  const unsigned n_dof = this->ndof();
6859 
6860  //Create newres vectors
6861  Vector<double> newres(n_dof);
6862  // Vector<double> newres_minus(n_dof);
6863 
6864  //Integer storage for local unknown
6865  int local_unknown=0;
6866 
6867  //Should probably give this a more global scope
6868  const double fd_step = Default_fd_jacobian_step;
6869 
6870  //Loop over the nodes
6871  for(unsigned n=0;n<n_node;n++)
6872  {
6873  //Loop over position dofs
6874  for(unsigned k=0;k<n_position_type;k++)
6875  {
6876  //Loop over dimension
6877  for(unsigned i=0;i<nodal_dim;i++)
6878  {
6879  //If the variable is free
6880  local_unknown = position_local_eqn(n,k,i);
6881  if(local_unknown >= 0)
6882  {
6883  //Store a pointer to the (generalised) Eulerian nodal position
6884  double* const value_pt = &(node_pt(n)->x_gen(k,i));
6885 
6886  //Save the old value of the (generalised) Eulerian nodal position
6887  const double old_var = *value_pt;
6888 
6889  //Increment the (generalised) Eulerian nodal position
6890  *value_pt += fd_step;
6891 
6892  // Perform any auxialiary node updates
6893  node_pt(n)->perform_auxiliary_node_update_fct();
6894 
6895  // Update any other slaved variables
6896  update_in_solid_position_fd(n);
6897 
6898  //Calculate the new residuals
6899  get_residuals(newres);
6900 
6901 // if (use_first_order_fd)
6902  {
6903  //Do forward finite differences
6904  for(unsigned m=0;m<n_dof;m++)
6905  {
6906  //Stick the entry into the Jacobian matrix
6907  jacobian(m,local_unknown) = (newres[m] - residuals[m])/fd_step;
6908  }
6909  }
6910 // else
6911 // {
6912 // //Take backwards step for the (generalised) Eulerian nodal
6913 // // position
6914 // node_pt(n)->x_gen(k,i) = old_var-fd_step;
6915 
6916 // //Calculate the new residuals at backward position
6917 // get_residuals(newres_minus);
6918 
6919 // //Do central finite differences
6920 // for(unsigned m=0;m<n_dof;m++)
6921 // {
6922 // //Stick the entry into the Jacobian matrix
6923 // jacobian(m,local_unknown) =
6924 // (newres[m] - newres_minus[m])/(2.0*fd_step);
6925 // }
6926 // }
6927 
6928  //Reset the (generalised) Eulerian nodal position
6929  *value_pt = old_var;
6930 
6931 
6932  // Perform any auxialiary node updates
6933  node_pt(n)->perform_auxiliary_node_update_fct();
6934 
6935  // Reset any other slaved variables
6936  reset_in_solid_position_fd(n);
6937  }
6938  }
6939  }
6940  }
6941 
6942  //End of finite difference loop
6943  //Final reset of any slaved data
6944  reset_after_solid_position_fd();
6945  }
6946 
6947 //============================================================================
6948 /// Return i-th FE-interpolated Lagrangian coordinate at
6949 /// local coordinate s.
6950 //============================================================================
6952  const unsigned &i) const
6953  {
6954  //Find the number of nodes
6955  const unsigned n_node = nnode();
6956 
6957  //Find the number of lagrangian types from the first node
6958  const unsigned n_lagrangian_type = nnodal_lagrangian_type();
6959 
6960  //Assign storage for the local shape function
6961  Shape psi(n_node,n_lagrangian_type);
6962 
6963  //Find the values of shape function
6964  shape(s,psi);
6965 
6966  //Initialise value of xi
6967  double interpolated_xi = 0.0;
6968 
6969  //Loop over the local nodes
6970  for(unsigned l=0;l<n_node;l++)
6971  {
6972  //Loop over the number of dofs
6973  for(unsigned k=0;k<n_lagrangian_type;k++)
6974  {
6975  interpolated_xi += lagrangian_position_gen(l,k,i)*psi(l,k);
6976  }
6977  }
6978 
6979  return(interpolated_xi);
6980  }
6981 
6982 //============================================================================
6983 /// Compute FE-interpolated Lagrangian coordinate vector xi[] at
6984 /// local coordinate s.
6985 //============================================================================
6987  Vector<double> &xi) const
6988  {
6989  //Find the number of nodes
6990  const unsigned n_node = nnode();
6991 
6992  //Find the number of lagrangian types from the first node
6993  const unsigned n_lagrangian_type = nnodal_lagrangian_type();
6994 
6995  //Assign storage for the local shape function
6996  Shape psi(n_node,n_lagrangian_type);
6997 
6998  //Find the values of shape function
6999  shape(s,psi);
7000 
7001  //Read out the number of lagrangian coordinates from the node
7002  const unsigned n_lagrangian = lagrangian_dimension();
7003 
7004  //Loop over the number of lagrangian coordinates
7005  for(unsigned i=0;i<n_lagrangian;i++)
7006  {
7007  //Initialise component to zero
7008  xi[i] = 0.0;
7009 
7010  //Loop over the local nodes
7011  for(unsigned l=0;l<n_node;l++)
7012  {
7013  //Loop over the number of dofs
7014  for(unsigned k=0;k<n_lagrangian_type;k++)
7015  {
7016  xi[i] += lagrangian_position_gen(l,k,i)*psi(l,k);
7017  }
7018  }
7019  }
7020  }
7021 
7022 
7023 
7024 
7025 
7026 //============================================================================
7027 /// Compute derivatives of FE-interpolated Lagrangian coordinates xi
7028 /// with respect to local coordinates: dxids[i][j]=dxi_i/ds_j
7029 //============================================================================
7031  DenseMatrix<double> &dxids) const
7032 {
7033  //Find the number of nodes
7034  const unsigned n_node = nnode();
7035 
7036  //Find the number of lagrangian types from the first node
7037  const unsigned n_lagrangian_type = nnodal_lagrangian_type();
7038 
7039  // Dimension of the element = number of local coordinates
7040  unsigned el_dim=dim();
7041 
7042  //Assign storage for the local shape function
7043  Shape psi(n_node,n_lagrangian_type);
7044  DShape dpsi(n_node,n_lagrangian_type,el_dim);
7045 
7046  //Find the values of shape function and its derivs w.r.t. to local coords
7047  dshape_local(s,psi,dpsi);
7048 
7049  //Read out the number of lagrangian coordinates from the node
7050  const unsigned n_lagrangian = lagrangian_dimension();
7051 
7052  //Loop over the number of lagrangian and local coordinates
7053  for(unsigned i=0;i<n_lagrangian;i++)
7054  {
7055  for(unsigned j=0;j<el_dim;j++)
7056  {
7057  //Initialise component to zero
7058  dxids(i,j) = 0.0;
7059 
7060  //Loop over the local nodes
7061  for(unsigned l=0;l<n_node;l++)
7062  {
7063  //Loop over the number of dofs
7064  for(unsigned k=0;k<n_lagrangian_type;k++)
7065  {
7066  dxids(i,j) += lagrangian_position_gen(l,k,i)*dpsi(l,k,j);
7067  }
7068  }
7069  }
7070  }
7071 }
7072 
7073 //=======================================================================
7074 /// Add jacobian and residuals for consistent assignment of
7075 /// initial "accelerations" in Newmark scheme. Jacobian is the mass matrix.
7076 //=======================================================================
7079 {
7080 
7081 #ifdef PARANOID
7082  // Check that we're computing the real residuals, not the
7083  // ones corresponding to the assignement of a prescribed displacement
7084  if ((Solid_ic_pt!=0)||(!Solve_for_consistent_newmark_accel_flag))
7085  {
7086  std::ostringstream error_stream;
7087  error_stream << "Can only call fill_in_jacobian_for_newmark_accel()\n"
7088  << "With Solve_for_consistent_newmark_accel_flag:"
7089  << Solve_for_consistent_newmark_accel_flag << std::endl;
7090  error_stream << "Solid_ic_pt: " << Solid_ic_pt << std::endl;
7091 
7092  throw OomphLibError(error_stream.str(),
7093  OOMPH_CURRENT_FUNCTION,
7094  OOMPH_EXCEPTION_LOCATION);
7095  }
7096 #endif
7097 
7098  //Find the number of nodes
7099  const unsigned n_node = nnode();
7100  const unsigned n_position_type = nnodal_position_type();
7101  const unsigned nodal_dim = nodal_dimension();
7102 
7103  //Set the number of Lagrangian coordinates
7104  const unsigned n_lagrangian = dim();
7105 
7106  //Integer storage for local equation and unknown
7107  int local_eqn=0, local_unknown=0;
7108 
7109  // Set up memory for the shape functions:
7110  // # of nodes, # of positional dofs
7111  Shape psi(n_node,n_position_type);
7112 
7113  // # of nodes, # of positional dofs, # of lagrangian coords (for deriv)
7114  // Not needed but they come for free when compute the Jacobian.
7115  DShape dpsidxi(n_node,n_position_type,n_lagrangian);
7116 
7117  //Set # of integration points
7118  const unsigned n_intpt = integral_pt()->nweight();
7119 
7120  //Set the Vector to hold local coordinates
7121  Vector<double> s(n_lagrangian);
7122 
7123  // Get positional timestepper from first nodal point
7124  TimeStepper* timestepper_pt= node_pt(0)->position_time_stepper_pt();
7125 
7126 #ifdef PARANOID
7127  // Of course all this only works if we're actually using a
7128  // Newmark timestepper!
7129  if (timestepper_pt->type()!="Newmark")
7130  {
7131  std::ostringstream error_stream;
7132  error_stream
7133  << "Assignment of Newmark accelerations obviously only works\n"
7134  << "for Newmark timesteppers. You've called me with "
7135  << timestepper_pt->type() << std::endl;
7136 
7137  throw OomphLibError(error_stream.str(),
7138  OOMPH_CURRENT_FUNCTION,
7139  OOMPH_EXCEPTION_LOCATION);
7140  }
7141 #endif
7142 
7143  // "Acceleration" is the last history value:
7144  const unsigned ntstorage=timestepper_pt->ntstorage();
7145  const double accel_weight=timestepper_pt->weight(2,ntstorage-1);
7146 
7147  //Loop over the integration points
7148  for(unsigned ipt=0;ipt<n_intpt;ipt++)
7149  {
7150 
7151  //Assign values of s
7152  for(unsigned i=0;i<n_lagrangian;i++) {s[i] = integral_pt()->knot(ipt,i);}
7153 
7154  //Get the integral weight
7155  double w = integral_pt()->weight(ipt);
7156 
7157  // Jacobian of mapping between local and Lagrangian coords and shape
7158  // functions
7159  double J = dshape_lagrangian(s,psi,dpsidxi);
7160 
7161  // Get Lagrangian coordinate
7162  Vector<double> xi(n_lagrangian);
7163  interpolated_xi(s,xi);
7164 
7165  // Get multiplier for inertia terms
7166  double factor=multiplier(xi);
7167 
7168  //Premultiply the weights and the Jacobian
7169  double W = w*J;
7170 
7171  //Loop over the nodes
7172  for(unsigned l0=0;l0<n_node;l0++)
7173  {
7174  //Loop over the positional dofs: 'Type': 0: displacement; 1: deriv
7175  for(unsigned k0=0;k0<n_position_type;k0++)
7176  {
7177  // Loop over Eulerian directions
7178  for(unsigned i0=0;i0<nodal_dim;i0++)
7179  {
7180  local_eqn = position_local_eqn(l0,k0,i0);
7181  //If it's a degree of freedom, add contribution
7182  if(local_eqn >= 0)
7183  {
7184  //Loop over the nodes
7185  for(unsigned l1=0;l1<n_node;l1++)
7186  {
7187  //Loop over the positional dofs: 'Type': 0: displacement;
7188  //1: deriv
7189  for(unsigned k1=0;k1<n_position_type;k1++)
7190  {
7191  // Loop over Eulerian directions
7192  unsigned i1=i0;
7193  {
7194  local_unknown = position_local_eqn(l1,k1,i1);
7195  //If it's a degree of freedom, add contribution
7196  if(local_unknown >= 0)
7197  {
7198  // Add contribution: Mass matrix, multiplied by
7199  // weight for "acceleration" in Newmark scheme
7200  // and general multiplier
7201  jacobian(local_eqn,local_unknown) +=
7202  factor*accel_weight*psi(l0,k0)*psi(l1,k1)*W;
7203  }
7204  }
7205  }
7206  }
7207  }
7208  }
7209  }
7210  }
7211 
7212  } //End of loop over the integration points
7213 
7214 }
7215 
7216 
7217 
7218 
7219 
7220 //=======================================================================
7221 /// Helper function to fill in the residuals and (if flag==1) the Jacobian
7222 /// for the setup of an initial condition. The global equations are:
7223 /// \f[
7224 /// 0 = \int \left( \sum_{j=1}^N \sum_{k=1}^K X_{ijk} \psi_{jk}(\xi_n)
7225 /// - \frac{\partial^D R^{(IC)}_i(\xi_n)}{\partial t^D}
7226 /// \right) \psi_{lm}(\xi_n) \ dv
7227 /// \mbox{ \ \ \ \ for \ \ \ $l=1,...,N, \ \ m=1,...,K$}
7228 /// \f]
7229 /// where \f$ N \f$ is the number of nodes in the mesh and \f$ K \f$
7230 /// the number of generalised nodal coordinates. The initial shape
7231 /// of the solid body, \f$ {\bf R}^{(IC)},\f$ and its time-derivatives
7232 /// are specified via the \c GeomObject that is stored in the
7233 /// \c SolidFiniteElement::SolidInitialCondition object. The latter also
7234 /// stores the order of the time-derivative \f$ D \f$ to be assigned.
7235 //=======================================================================
7237  Vector<double> &residuals,
7238  DenseMatrix<double> &jacobian,
7239  const unsigned& flag)
7240 {
7241  //Find the number of nodes and position types
7242  const unsigned n_node = nnode();
7243  const unsigned n_position_type = nnodal_position_type();
7244 
7245  //Set the dimension of the global coordinates
7246  const unsigned nodal_dim = nodal_dimension();
7247 
7248  //Find the number of lagragian types from the first node
7249  const unsigned n_lagrangian_type = nnodal_lagrangian_type();
7250 
7251  //Set the number of lagrangian coordinates
7252  const unsigned n_lagrangian = dim();
7253 
7254  //Integer storage for local equation number
7255  int local_eqn=0;
7256  int local_unknown=0;
7257 
7258  // # of nodes, # of positional dofs
7259  Shape psi(n_node,n_position_type);
7260 
7261  // # of nodes, # of positional dofs, # of lagrangian coords (for deriv)
7262  // not needed but they come for free when we compute the Jacobian
7263  //DShape dpsidxi(n_node,n_position_type,n_lagrangian);
7264 
7265  //Set # of integration points
7266  const unsigned n_intpt = integral_pt()->nweight();
7267 
7268  //Set the Vector to hold local coordinates
7269  Vector<double> s(n_lagrangian);
7270 
7271  //Loop over the integration points
7272  for(unsigned ipt=0;ipt<n_intpt;ipt++)
7273  {
7274 
7275  //Assign values of s
7276  for(unsigned i=0;i<n_lagrangian;i++)
7277  {s[i] = integral_pt()->knot(ipt,i);}
7278 
7279  //Get the integral weight
7280  double w = integral_pt()->weight(ipt);
7281 
7282  // Shape fcts
7283  shape(s,psi);
7284 
7285  // Get Lagrangian coordinate
7286  Vector<double> xi(n_lagrangian,0.0);
7287 
7288  //Loop over the number of lagrangian coordinates
7289  for(unsigned i=0;i<n_lagrangian;i++)
7290  {
7291  //Loop over the local nodes
7292  for(unsigned l=0;l<n_node;l++)
7293  {
7294  //Loop over the number of dofs
7295  for(unsigned k=0;k<n_lagrangian_type;k++)
7296  {
7297  xi[i] += lagrangian_position_gen(l,k,i)*psi(l,k);
7298  }
7299  }
7300  }
7301 
7302  // Get initial condition
7303  Vector<double> drdt_ic(nodal_dim);
7304  Solid_ic_pt->geom_object_pt()->
7305  dposition_dt(xi,Solid_ic_pt->ic_time_deriv(),drdt_ic);
7306 
7307  // Weak form of assignment of initial guess
7308 
7309  //Loop over the number of node
7310  for (unsigned l=0;l<n_node;l++)
7311  {
7312  //Loop over the type of degree of freedom
7313  for (unsigned k=0;k<n_position_type;k++)
7314  {
7315  //Loop over the coordinate directions
7316  for (unsigned i=0;i<nodal_dim;i++)
7317  {
7318  local_eqn = position_local_eqn(l,k,i);
7319 
7320  //If it's not a boundary condition
7321  if(local_eqn >= 0)
7322  {
7323  // Note we're ignoring the mapping between local and
7324  // global Lagrangian coords -- doesn't matter here;
7325  // we're just enforcing a slightly different
7326  // weak form.
7327  residuals[local_eqn] += (interpolated_x(s,i)-drdt_ic[i])*
7328  psi(l,k)*w;
7329 
7330 
7331  // Do Jacobian too?
7332  if (flag==1)
7333  {
7334 
7335  //Loop over the number of node
7336  for (unsigned ll=0;ll<n_node;ll++)
7337  {
7338  //Loop over the type of degree of freedom
7339  for (unsigned kk=0;kk<n_position_type;kk++)
7340  {
7341 
7342  // Only diagonal term
7343  unsigned ii=i;
7344 
7345  local_unknown = position_local_eqn(ll,kk,ii);
7346 
7347  //If it's not a boundary condition
7348  if(local_unknown >= 0)
7349  {
7350  // Note we're ignoring the mapping between local and
7351  // global Lagrangian coords -- doesn't matter here;
7352  // we're just enforcing a slightly different
7353  // weak form.
7354  jacobian(local_eqn,local_unknown) +=
7355  psi(ll,kk)*psi(l,k)*w;
7356  }
7357  else
7358  {
7359  oomph_info
7360  << "WARNING: You should really free all Data"
7361  << std::endl
7362  << " before setup of initial guess"
7363  << std::endl
7364  << "ll, kk, ii " << ll << " " << kk << " " << ii
7365  << std::endl;
7366  }
7367  }
7368  }
7369 
7370  }
7371  }
7372  else
7373  {
7374  oomph_info
7375  << "WARNING: You should really free all Data" << std::endl
7376  << " before setup of initial guess" << std::endl
7377  << "l, k, i " << l << " " << k << " " << i << std::endl;
7378  }
7379  }
7380  }
7381  }
7382 
7383  } //End of loop over the integration points
7384 }
7385 
7386 
7387 //===============================================================
7388 /// Return the geometric shape function at the local coordinate s
7389 //===============================================================
7390  void PointElement::shape(const Vector<double> &s, Shape &psi) const
7391  {
7392  // Single shape function always has value 1
7393  psi[0]=1.0;
7394  }
7395 
7396 //=======================================================================
7397 /// Assign the static Default_integration_scheme
7398 //=======================================================================
7400 
7401 
7402 }
double dshape_lagrangian(const Vector< double > &s, Shape &psi, DShape &dpsidxi) const
Calculate shape functions and derivatives w.r.t. Lagrangian coordinates at local coordinate s...
Definition: elements.cc:6564
virtual void get_residuals(Vector< double > &residuals)
Calculate the vector of residuals of the equations in the element. By default initialise the vector t...
Definition: elements.h:975
void check_J_eulerian_at_knots(bool &passed) const
Check that Jacobian of mapping between local and Eulerian coordinates at all integration points is po...
Definition: elements.cc:4155
virtual void reset_after_internal_fd()
Function that is call after the finite differencing of the internal data. This may be overloaded to r...
Definition: elements.h:460
Class of matrices containing doubles, and stored as a DenseMatrix<double>, but with solving functiona...
Definition: matrices.h:1234
double J_eulerian_at_knot(const unsigned &ipt) const
Return the Jacobian of the mapping from local to global coordinates at the ipt-th integration point O...
Definition: elements.cc:5201
double value(const unsigned &i) const
Return i-th stored value. This function is not virtual so that it can be inlined. This means that if ...
Definition: nodes.h:291
void assign_internal_eqn_numbers(unsigned long &global_number, Vector< double *> &Dof_pt)
Assign the global equation numbers to the internal Data. The arguments are the current highest global...
Definition: elements.cc:501
double dJ_eulerian_at_knot(const unsigned &ipt, Shape &psi, DenseMatrix< double > &djacobian_dX) const
Compute the geometric shape functions (psi) at integration point ipt. Return the determinant of the j...
Definition: elements.cc:3283
void clear_global_eqn_numbers()
Clear the storage for the global equation numbers and pointers to dofs (if stored) ...
Definition: elements.h:213
void read_internal_data_values_from_vector(const Vector< double > &vector_of_values, unsigned &index)
Read all internal data and time history values from the vector starting from index. On return the index will be set to the value at the end of the data that has been read in.
Definition: elements.cc:612
unsigned long nrow() const
Return the number of rows of the matrix.
Definition: matrices.h:504
virtual void assign_additional_local_eqn_numbers()
Setup any additional look-up schemes for local equation numbers. Examples of use include using local ...
Definition: elements.h:264
double Radius_multiplier_for_fast_exit_from_locate_zeta
Multiplier for (zeta-based) outer radius of element used for deciding that point is outside element...
Definition: elements.cc:1632
void add_global_eqn_numbers(std::deque< unsigned long > const &global_eqn_numbers, std::deque< double *> const &global_dof_pt)
Add the contents of the queue global_eqn_numbers to the local storage for the local-to-global transla...
Definition: elements.cc:149
std::vector< bool > Data_fd
Storage for boolean flags of size Ninternal_data + Nexternal_data that correspond to the data used in...
Definition: elements.h:127
void output_zeta(std::ostream &outfile, const unsigned &nplot)
Output boundary coordinate zeta.
Definition: elements.cc:5071
virtual void reset_in_internal_fd(const unsigned &i)
Function called within the finite difference loop for internal data after the values in the i-th exte...
Definition: elements.h:470
virtual void assemble_local_to_lagrangian_jacobian(const DShape &dpsids, DenseMatrix< double > &jacobian) const
Assemble the jacobian matrix for the mapping from local to lagrangian coordinates, given the derivatives of the shape function.
Definition: elements.cc:6389
virtual unsigned required_nvalue(const unsigned &n) const
Number of values that must be stored at local node n by the element. The default is 0...
Definition: elements.h:2346
virtual void d2shape_local_at_knot(const unsigned &ipt, Shape &psi, DShape &dpsids, DShape &d2psids) const
Return the geometric shape function and its first and second derivatives w.r.t. the local coordinates...
Definition: elements.cc:3207
virtual void describe_nodal_local_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the local dofs of the element[s]. The ostream specifies the output stream to whi...
Definition: elements.cc:1701
static bool Ignore_discontinuous_tangent_warning
Ignores the warning when the tangent vectors from continuous_tangent_and_outer_unit_normal may not be...
Definition: elements.h:4177
unsigned long ncol() const
Return the number of columns of the matrix.
Definition: matrices.h:507
virtual double dshape_eulerian_at_knot(const unsigned &ipt, Shape &psi, DShape &dpsidx) const
Return the geometric shape functions and also first derivatives w.r.t. global coordinates at the ipt-...
Definition: elements.cc:3254
unsigned long * Eqn_number
Storage for the global equation numbers represented by the degrees of freedom in the element...
Definition: elements.h:82
virtual Node * get_node_at_local_coordinate(const Vector< double > &s) const
If there is a node at this local coordinate, return the pointer to the node.
Definition: elements.cc:3806
void fill_in_jacobian_from_internal_by_fd(Vector< double > &residuals, DenseMatrix< double > &jacobian, const bool &fd_all_data=false)
Calculate the contributions to the jacobian from the internal degrees of freedom using finite differe...
Definition: elements.cc:1064
unsigned Max_newton_iterations
Maximum number of newton iterations.
Definition: elements.cc:1627
cstr elem_len * i
Definition: cfortran.h:607
virtual double d2shape_lagrangian_at_knot(const unsigned &ipt, Shape &psi, DShape &dpsidxi, DShape &d2psidxi) const
Return the geometric shape functions and also first and second derivatives w.r.t. Lagrangian coordina...
Definition: elements.cc:6683
double * value_pt(const unsigned &i) const
Return the pointer to the i-the stored value. Typically this is required when direct access to the st...
Definition: nodes.h:322
int & face_index()
Index of the face (a number that uniquely identifies the face in the element)
Definition: elements.h:4412
virtual void set_integration_scheme(Integral *const &integral_pt)
Set the spatial integration scheme.
Definition: elements.cc:3150
void get_ds_bulk_ds_face(const Vector< double > &s, DenseMatrix< double > &dsbulk_dsface, unsigned &interior_direction) const
Calculate the derivatives of the local coordinates in the bulk element with respect to the local coor...
Definition: elements.cc:6267
void perform_auxiliary_node_update_fct()
Execute auxiliary update function (if any) – this can be used to update any nodal values following t...
Definition: nodes.h:1510
virtual void fill_in_contribution_to_djacobian_and_dmass_matrix_dparameter(double *const &parameter_pt, Vector< double > &dres_dparam, DenseMatrix< double > &djac_dparam, DenseMatrix< double > &dmass_matrix_dparam)
Add the elemental contribution to the derivative of the jacobian matrix, mass matrix and the residual...
Definition: elements.cc:1416
const double Pi
50 digits from maple
virtual void fill_in_contribution_to_hessian_vector_products(Vector< double > const &Y, DenseMatrix< double > const &C, DenseMatrix< double > &product)
Fill in contribution to the product of the Hessian (derivative of Jacobian with respect to all variab...
Definition: elements.cc:1469
virtual void fill_in_contribution_to_inner_products(Vector< std::pair< unsigned, unsigned > > const &history_index, Vector< double > &inner_product)
Fill in the contribution to the inner products between given pairs of history values.
Definition: elements.cc:1511
static bool Accept_negative_jacobian
Boolean that if set to true allows a negative jacobian in the transform between global and local coor...
Definition: elements.h:1739
virtual void add_value_pt_to_map(std::map< unsigned, double *> &map_of_value_pt)
Add pointers to all unpinned and unconstrained data to a map indexed by (global) equation number...
Definition: nodes.cc:1041
void outer_unit_normal(const Vector< double > &s, Vector< double > &unit_normal) const
Compute outer unit normal at the specified local coordinate.
Definition: elements.cc:5873
virtual unsigned self_test()
Self-test: Check inversion of element & do self-test for GeneralisedElement. Return 0 if OK...
Definition: elements.cc:4333
void disable_error_message()
Suppress issueing of the error message in destructor (useful if error is caught successfully!) ...
virtual void describe_local_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the local dofs of the element[s]. The ostream specifies the output stream to whi...
Definition: elements.cc:1682
void describe_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the dofs of the Node. The ostream specifies the output stream to which the descr...
Definition: nodes.cc:3565
virtual void update_in_external_fd(const unsigned &i)
Function called within the finite difference loop for external data after a change in any values in t...
Definition: elements.h:487
BulkCoordinateDerivativesFctPt & bulk_coordinate_derivatives_fct_pt()
Return the pointer to the function that returns the derivatives of the bulk coordinates wrt the face ...
Definition: elements.h:4540
virtual void fill_in_contribution_to_djacobian_dparameter(double *const &parameter_pt, Vector< double > &dres_dparam, DenseMatrix< double > &djac_dparam)
Add the elemental contribution to the derivatives of the elemental Jacobian matrix and residuals with...
Definition: elements.cc:1363
A general Finite Element class.
Definition: elements.h:1274
void(* UnsteadyExactSolutionFctPt)(const double &, const Vector< double > &, Vector< double > &)
Function pointer for function that computes Vector-valued time-dependent function as ...
Definition: elements.h:1729
char t
Definition: cfortran.h:572
virtual double interpolated_dxdt(const Vector< double > &s, const unsigned &i, const unsigned &t)
Return t-th time-derivative of the i-th FE-interpolated Eulerian coordinate at local coordinate s...
Definition: elements.cc:4484
static PointIntegral Default_integration_scheme
Return spatial dimension of element (=number of local coordinates needed to parametrise the element) ...
Definition: elements.h:3260
virtual void dJ_eulerian_dnodal_coordinates(const DenseMatrix< double > &jacobian, const DShape &dpsids, DenseMatrix< double > &djacobian_dX) const
A template-free interface that calculates the derivative of the jacobian of a mapping with respect to...
Definition: elements.cc:2611
bool is_halo() const
Is this element a halo?
Definition: elements.h:1141
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition: nodes.h:852
void describe_solid_local_dofs(std::ostream &out, const std::string &current_string) const
Classifies dofs locally for solid specific aspects.
Definition: elements.cc:6726
OomphInfo oomph_info
virtual double J_eulerian_at_knot(const unsigned &ipt) const
Return the Jacobian of the mapping from local to global coordinates at the ipt-th integration point...
Definition: elements.cc:4086
long & eqn_number(const unsigned &i)
Return the equation number of the i-th stored variable.
Definition: nodes.h:365
unsigned nvalue() const
Return number of values stored in data object (incl pinned ones).
Definition: nodes.h:448
virtual void describe_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the dofs of the Node. The ostream specifies the output stream to which the descr...
Definition: nodes.cc:905
unsigned & bulk_node_number(const unsigned &n)
Return the bulk node number that corresponds to the n-th local node number.
Definition: elements.h:4584
e
Definition: cfortran.h:575
virtual void interpolated_dxids(const Vector< double > &s, DenseMatrix< double > &dxids) const
Compute derivatives of FE-interpolated Lagrangian coordinates xi with respect to local coordinates: d...
Definition: elements.cc:7030
unsigned long nindex2() const
Return the range of index 2 of the derivatives of the shape functions.
Definition: shape.h:492
A Rank 4 Tensor class.
Definition: matrices.h:1625
void get_local_coordinate_in_bulk(const Vector< double > &s, Vector< double > &s_bulk) const
Calculate the vector of local coordinate in the bulk element given the local coordinates in this Face...
Definition: elements.cc:6242
Vector< Data * > external_interaction_field_data_pt() const
Return vector of pointers to the field Data objects that affect the interactions on the element...
unsigned long nindex2() const
Return the range of index 2 of the tensor.
Definition: matrices.h:1912
void fill_in_jacobian_from_external_by_fd(Vector< double > &residuals, DenseMatrix< double > &jacobian, const bool &fd_all_data=false)
Calculate the contributions to the jacobian from the external degrees of freedom using finite differe...
Definition: elements.cc:1158
double size() const
Definition: elements.cc:4207
unsigned add_external_data(Data *const &data_pt, const bool &fd=true)
Definition: elements.cc:293
unsigned Ndof
Number of degrees of freedom.
Definition: elements.h:109
void shape(const Vector< double > &s, Shape &psi) const
Broken assignment operator.
Definition: elements.cc:7390
double J_eulerian(const Vector< double > &s) const
Return the Jacobian of mapping from local to global coordinates at local position s...
Definition: elements.cc:5113
void solve(DoubleVector &rhs)
Complete LU solve (replaces matrix by its LU decomposition and overwrites RHS with solution)...
Definition: matrices.cc:55
void flush_external_data()
Flush all external data.
Definition: elements.cc:365
int & normal_sign()
Sign of outer unit normal (relative to cross-products of tangent vectors in the corresponding "bulk" ...
Definition: elements.h:4404
unsigned nexternal_data() const
Return the number of external data objects.
Definition: elements.h:831
virtual void fill_in_contribution_to_dresiduals_dparameter(double *const &parameter_pt, Vector< double > &dres_dparam)
Add the elemental contribution to the derivatives of the residuals with respect to a parameter...
Definition: elements.cc:1317
void check_J_eulerian_at_knots(bool &passed) const
Check that Jacobian of mapping between local and Eulerian.
Definition: elements.cc:5285
unsigned ndim() const
Return (Eulerian) spatial dimension of the node.
Definition: nodes.h:992
void get_centre_of_gravity_and_max_radius_in_terms_of_zeta(Vector< double > &cog, double &max_radius) const
Compute centre of gravity of all nodes and radius of node that is furthest from it. Used to assess approximately if a point is likely to be contained with an element in locate_zeta-like operations.
Definition: elements.cc:3844
unsigned ndof() const
Return the number of equations/dofs in the element.
Definition: elements.h:834
virtual void local_fraction_of_node(const unsigned &j, Vector< double > &s_fraction)
Get the local fraction of the node j in the element A dumb, but correct default implementation is pro...
Definition: elements.cc:3131
virtual void assign_nodal_local_eqn_numbers(const bool &store_local_dof_pt)
Assign the local equation numbers for Data stored at the nodes Virtual so that it can be overloaded b...
Definition: elements.cc:3475
void cross(const Vector< double > &A, const Vector< double > &B, Vector< double > &C)
Cross product using "proper" output (move semantics means this is ok nowadays).
Definition: Vector.h:317
void integrate_fct(FiniteElement::SteadyExactSolutionFctPt integrand_fct_pt, Vector< double > &integral)
Evaluate integral of a Vector-valued function over the element.
Definition: elements.cc:4283
int Non_halo_proc_ID
Non-halo processor ID for Data; -1 if it&#39;s not a halo.
Definition: elements.h:134
virtual unsigned self_test()
Self-test: Have all internal values been classified as pinned/unpinned? Return 0 if OK...
Definition: elements.cc:1580
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition: nodes.h:995
void(* SteadyExactSolutionFctPt)(const Vector< double > &, Vector< double > &)
Function pointer for function that computes vector-valued steady "exact solution" as ...
Definition: elements.h:1723
virtual double interpolated_x(const Vector< double > &s, const unsigned &i) const
Return FE interpolated coordinate x[i] at local coordinate s.
Definition: elements.cc:3881
virtual void assemble_eulerian_base_vectors(const DShape &dpsids, DenseMatrix< double > &interpolated_G) const
Assemble the covariant Eulerian base vectors, assuming that the derivatives of the shape functions wi...
Definition: elements.cc:1989
virtual void transform_derivatives(const DenseMatrix< double > &inverse_jacobian, DShape &dbasis) const
Convert derivative w.r.t.local coordinates to derivatives w.r.t the coordinates used to assemble the ...
Definition: elements.cc:2764
double angle(const Vector< double > &a, const Vector< double > &b)
Get the angle between two vector.
Definition: Vector.h:307
A Rank 3 Tensor class.
Definition: matrices.h:1337
static bool Suppress_warning_about_repeated_external_data
Static boolean to suppress warnings about repeated external data. Defaults to true.
Definition: elements.h:705
double dshape_eulerian(const Vector< double > &s, Shape &psi, DShape &dpsidx) const
Compute the geometric shape functions and also first derivatives w.r.t. global coordinates at local c...
Definition: elements.cc:3227
virtual void add_values_to_vector(Vector< double > &vector_of_values)
Add all data and time history values to the vector in the internal storage order. ...
Definition: nodes.cc:1065
unsigned long nindex4() const
Return the range of index 4 of the tensor.
Definition: matrices.h:1918
static long Is_pinned
Static "Magic number" used in place of the equation number to indicate that the value is pinned...
Definition: nodes.h:192
virtual void assign_solid_local_eqn_numbers(const bool &store_local_dof)
Assigns local equation numbers for the generic solid local equation numbering schemes. If the boolean flag is true the the degrees of freedom are stored in Dof_pt.
Definition: elements.cc:6750
virtual void reset_after_external_fd()
Function that is call after the finite differencing of the external data. This may be overloaded to r...
Definition: elements.h:482
virtual void fill_in_jacobian_from_solid_position_by_fd(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Use finite differences to calculate the Jacobian entries corresponding to the solid positions...
Definition: elements.cc:6836
virtual ~SolidFiniteElement()
Destructor to clean up any allocated memory.
Definition: elements.cc:6490
void interpolated_zeta(const Vector< double > &s, Vector< double > &zeta) const
Calculate the interpolated value of zeta, the intrinsic coordinate of the element when viewed as a co...
Definition: elements.cc:4564
void set_halo(const unsigned &non_halo_proc_ID)
Label the element as halo and specify processor that holds non-halo counterpart.
Definition: elements.h:1132
static char t char * s
Definition: cfortran.h:572
virtual void shape_at_knot(const unsigned &ipt, Shape &psi) const
Return the geometric shape function at the ipt-th integration point.
Definition: elements.cc:3160
double ** Dof_pt
Storage for array of pointers to degrees of freedom ordered by local equation number. This information is not needed, except in continuation, bifurcation tracking and periodic orbit calculations. It is not set up unless the control flag Problem::Store_local_dof_pts_in_elements = true.
Definition: elements.h:89
virtual double dshape_lagrangian_at_knot(const unsigned &ipt, Shape &psi, DShape &dpsidxi) const
Return the geometric shape functions and also first derivatives w.r.t. Lagrangian coordinates at ipt-...
Definition: elements.cc:6589
virtual void reset_in_external_fd(const unsigned &i)
Function called within the finite difference loop for external data after the values in the i-th exte...
Definition: elements.h:492
static bool Suppress_output_while_checking_for_inverted_elements
Static boolean to suppress output while checking for inverted elements.
Definition: elements.h:1743
virtual double invert_jacobian_mapping(const DenseMatrix< double > &jacobian, DenseMatrix< double > &inverse_jacobian) const
A template-free interface that takes the matrix passed as jacobian and return its inverse in inverse_...
Definition: elements.cc:2139
Data *& internal_data_pt(const unsigned &i)
Return a pointer to i-th internal data object.
Definition: elements.h:623
double magnitude(const Vector< double > &a)
Get the magnitude of a vector.
Definition: Vector.h:301
unsigned long eqn_number(const unsigned &ieqn_local) const
Return the global equation number corresponding to the ieqn_local-th local equation number...
Definition: elements.h:709
static double Tolerance_for_singular_jacobian
Tolerance below which the jacobian is considered singular.
Definition: elements.h:1734
unsigned long nindex3() const
Return the range of index 3 of the tensor.
Definition: matrices.h:1915
void check_jacobian(const double &jacobian) const
Helper function used to check for singular or negative Jacobians in the transform from local to globa...
Definition: elements.cc:1724
unsigned long nindex1() const
Return the range of index 1 of the tensor.
Definition: matrices.h:1909
void compute_norm(double &el_norm)
Calculate the L2 norm of the displacement u=R-r to overload the compute_norm function in the Generali...
Definition: elements.cc:6301
void nbulk_value_resize(const unsigned &i)
Resize the storage for the number of values originally stored at the local nodes to i entries...
Definition: elements.h:4607
void describe_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the dofs of the element. The ostream specifies the output stream to which the de...
Definition: elements.cc:522
A class that represents a collection of data; each Data object may contain many different individual ...
Definition: nodes.h:89
virtual void update_in_internal_fd(const unsigned &i)
Function called within the finite difference loop for internal data after a change in any values in t...
Definition: elements.h:465
double & x_gen(const unsigned &k, const unsigned &i)
Reference to the generalised position x(k,i).
Definition: nodes.h:1055
virtual void read_values_from_vector(const Vector< double > &vector_of_values, unsigned &index)
Read all data and time history values from the vector starting from index. On return the index will b...
Definition: nodes.cc:1129
unsigned nexternal_interaction_field_data() const
Return the number of Data items that affect the external interactions in this element. This includes e.g. fluid velocities and pressures in adjacent fluid elements in an FSI problem.
Data *const & variable_position_pt() const
Pointer to variable_position data (const version)
Definition: nodes.h:1655
unsigned nexternal_interaction_geometric_data() const
Return the number of geometric Data items that affect the external interactions in this element: i...
virtual double d2shape_eulerian_at_knot(const unsigned &ipt, Shape &psi, DShape &dpsidx, DShape &d2psidx) const
Return the geometric shape functions and also first and second derivatives w.r.t. global coordinates ...
Definition: elements.cc:3432
Data ** Data_pt
Storage for pointers to internal and external data. The data is of the same type and so can be addres...
Definition: elements.h:97
static const unsigned Default_Initial_Nvalue
Default return value for required_nvalue(n) which gives the number of "data" values required by the e...
Definition: elements.h:1333
void add_internal_eqn_numbers_to_vector(Vector< long > &vector_of_eqn_numbers)
Add all equation numbers associated with internal data to the vector in the internal storage order...
Definition: elements.cc:624
static const unsigned N2deriv[]
Static array that holds the number of second derivatives as a function of the dimension of the elemen...
Definition: elements.h:1440
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition: elements.h:2109
static const double Node_location_tolerance
Default value for the tolerance to be used when locating nodes via local coordinates.
Definition: elements.h:1337
virtual void assign_eqn_numbers(unsigned long &global_ndof, Vector< double *> &dof_pt)
Assign global equation numbers; increment global number of unknowns, global_ndof; and add any new dof...
Definition: nodes.cc:861
void shape(const double &s, double *Psi)
Definition for 1D Lagrange shape functions. The value of all the shape functions at the local coordin...
Definition: shape.h:549
virtual double interpolated_xi(const Vector< double > &s, const unsigned &i) const
Return i-th FE-interpolated Lagrangian coordinate xi[i] at local coordinate s.
Definition: elements.cc:6951
virtual void update_before_internal_fd()
Function that is called before the finite differencing of any internal data. This may be overloaded t...
Definition: elements.h:455
unsigned Nexternal_data
Number of external data.
Definition: elements.h:115
virtual void assemble_local_to_lagrangian_jacobian2(const DShape &d2psids, DenseMatrix< double > &jacobian2) const
Assemble the the "jacobian" matrix of second derivatives, given the second derivatives of the shape f...
Definition: elements.cc:6448
unsigned ntstorage() const
Return the number of doubles required to represent history (one for steady)
Definition: timesteppers.h:562
virtual void assemble_local_to_eulerian_jacobian(const DShape &dpsids, DenseMatrix< double > &jacobian) const
Assemble the jacobian matrix for the mapping from local to Eulerian coordinates, given the derivative...
Definition: elements.cc:1883
void fill_in_jacobian_for_newmark_accel(DenseMatrix< double > &jacobian)
Fill in the contributions of the Jacobian matrix for the consistent assignment of the initial "accele...
Definition: elements.cc:7078
void add_internal_data_values_to_vector(Vector< double > &vector_of_values)
Add all internal data and time history values to the vector in the internal storage order...
Definition: elements.cc:600
unsigned long nindex1() const
Return the range of index 1 of the derivatives of the shape functions.
Definition: shape.h:489
const long & position_eqn_number(const unsigned &k, const unsigned &i) const
Return the equation number for generalised Eulerian coordinate: type of coordinate: k...
Definition: nodes.h:1680
Data *& external_data_pt(const unsigned &i)
Return a pointer to i-th external data object.
Definition: elements.h:662
double raw_nodal_position(const unsigned &n, const unsigned &i) const
Return the i-th coordinate at local node n. Do not use the hanging node representation. NOTE: Moved to cc file because of a possible compiler bug in gcc (yes, really!). The move to the cc file avoids inlining which appears to cause problems (only) when compiled with gcc and -O3; offensive "illegal read" is in optimised-out section of code and data that is allegedly illegal is readily readable (by other means) just before this function is called so I can&#39;t really see how we could possibly be responsible for this...
Definition: elements.cc:1659
virtual void assign_local_eqn_numbers(const bool &store_local_dof_pt)
Setup the arrays of local equation numbers for the element. If the optional boolean argument is true...
Definition: elements.cc:656
virtual void fill_in_jacobian_from_nodal_by_fd(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Calculate the contributions to the jacobian from the nodal degrees of freedom using finite difference...
Definition: elements.cc:3583
std::string type() const
Return string that indicates the type of the timestepper (e.g. "BDF", "Newmark", etc.)
Definition: timesteppers.h:465
unsigned ninternal_data() const
Return the number of internal data objects.
Definition: elements.h:828
A Class for nodes that deform elastically (i.e. position is an unknown in the problem). The idea is that the Eulerian positions are stored in a Data object and the Lagrangian coordinates are stored in addition. The pointer that addresses the Eulerian positions is set to the pointer to Value in the Data object. Hence, SolidNode uses knowledge of the internal structure of Data and must be a friend of the Data class. In order to allow a mesh to deform via an elastic-style equation in deforming-domain problems, the positions are stored separately from the values, so that elastic problems may be combined with any other type of problem.
Definition: nodes.h:1569
virtual void dshape_local_at_knot(const unsigned &ipt, Shape &psi, DShape &dpsids) const
Return the geometric shape function and its derivative w.r.t. the local coordinates at the ipt-th int...
Definition: elements.cc:3176
double dot(const Vector< double > &a, const Vector< double > &b)
Probably not always best/fastest because not optimised for dimension but useful...
Definition: Vector.h:289
virtual void fill_in_contribution_to_mass_matrix(Vector< double > &residuals, DenseMatrix< double > &mass_matrix)
Add the elemental contribution to the mass matrix matrix. and the residuals vector. Note that this function should NOT initialise the residuals vector or the mass matrix. It must be called after the residuals vector and jacobian matrix have been initialised to zero. The default is deliberately broken.
Definition: elements.cc:1248
int get_node_number(Node *const &node_pt) const
Return the number of the node *node_pt if this node is in the element, else return -1;...
Definition: elements.cc:3737
void continuous_tangent_and_outer_unit_normal(const Vector< double > &s, Vector< Vector< double > > &tang_vec, Vector< double > &unit_normal) const
Compute the tangent vector(s) and the outer unit normal vector at the specified local coordinate...
Definition: elements.cc:5389
std::string string(const unsigned &i)
Return the i-th string or "" if the relevant string hasn&#39;t been defined.
static long Is_unclassified
Static "Magic number" used in place of the equation number to denote a value that hasn&#39;t been classif...
Definition: nodes.h:201
FiniteElement *& bulk_element_pt()
Pointer to higher-dimensional "bulk" element.
Definition: elements.h:4515
virtual void identify_field_data_for_interactions(std::set< std::pair< Data *, unsigned > > &paired_field_data)
The purpose of this function is to identify all possible Data that can affect the fields interpolated...
Definition: elements.cc:4966
void set_nodal_dimension(const unsigned &nodal_dim)
Set the dimension of the nodes in the element. This will typically only be required when constructing...
Definition: elements.h:1351
static std::deque< double * > Dof_pt_deque
Static storage for deque used to add_global_equation_numbers when pointers to the dofs in each elemen...
Definition: elements.h:232
void transform_derivatives_diagonal(const DenseMatrix< double > &inverse_jacobian, DShape &dbasis) const
Convert derivative w.r.t local coordinates to derivatives w.r.t the coordinates used to assemble the ...
Definition: elements.cc:2806
void locate_zeta(const Vector< double > &zeta, GeomObject *&geom_object_pt, Vector< double > &s, const bool &use_coordinate_as_initial_guess=false)
For a given value of zeta, the "global" intrinsic coordinate of a mesh of FiniteElements represented ...
Definition: elements.cc:4617
void read_internal_eqn_numbers_from_vector(const Vector< long > &vector_of_eqn_numbers, unsigned &index)
Read all equation numbers associated with internal data from the vector starting from index...
Definition: elements.cc:637
virtual void fill_in_contribution_to_jacobian_and_mass_matrix(Vector< double > &residuals, DenseMatrix< double > &jacobian, DenseMatrix< double > &mass_matrix)
Add the elemental contribution to the jacobian matrix, mass matrix and the residuals vector...
Definition: elements.cc:1281
unsigned add_internal_data(Data *const &data_pt, const bool &fd=true)
Add a (pointer to an) internal data object to the element and return the index required to obtain it ...
Definition: elements.cc:66
virtual void build_face_element(const int &face_index, FaceElement *face_element_pt)
Function for building a lower dimensional FaceElement on the specified face of the FiniteElement...
Definition: elements.cc:5002
CoordinateMappingFctPt & face_to_bulk_coordinate_fct_pt()
Return the pointer to the function that maps the face coordinate to the bulk coordinate.
Definition: elements.h:4529
virtual void assemble_local_to_eulerian_jacobian2(const DShape &d2psids, DenseMatrix< double > &jacobian2) const
Assemble the the "jacobian" matrix of second derivatives of the mapping from local to Eulerian coordi...
Definition: elements.cc:1944
double d2shape_lagrangian(const Vector< double > &s, Shape &psi, DShape &dpsidxi, DShape &d2psidxi) const
Compute the geometric shape functions and also first and second derivatives w.r.t. Lagrangian coordinates at local coordinate s; Returns Jacobian of mapping from Lagrangian to local coordinates. Numbering: 1D: d2pidxi(i,0) = 2D: d2psidxi(i,0) = d2psidxi(i,1) = d2psidxi(i,2) = 3D: d2psidxi(i,0) = d2psidxi(i,1) = d2psidxi(i,2) = d2psidxi(i,3) = d2psidxi(i,4) = d2psidxi(i,5) = .
Definition: elements.cc:6634
void bulk_node_number_resize(const unsigned &i)
Resize the storage for the bulk node numbers.
Definition: elements.h:4580
double Newton_tolerance
Convergence tolerance for the newton solver.
Definition: elements.cc:1624
unsigned & nbulk_value(const unsigned &n)
Return the number of values originally stored at local node n (before the FaceElement added additiona...
Definition: elements.h:4597
void add_internal_value_pt_to_map(std::map< unsigned, double *> &map_of_value_pt)
Add pointers to the internal data values to map indexed by the global equation number.
Definition: elements.cc:583
virtual void describe_local_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the local dofs of the element. The ostream specifies the output stream to which ...
Definition: elements.cc:545
virtual ~FiniteElement()
The destructor cleans up the static memory allocated for shape function storage. Internal and externa...
Definition: elements.cc:3113
double d2shape_eulerian(const Vector< double > &s, Shape &psi, DShape &dpsidx, DShape &d2psidx) const
Compute the geometric shape functions and also first and second derivatives w.r.t. global coordinates at local coordinate s; Returns Jacobian of mapping from global to local coordinates. Numbering: 1D: d2psidx(i,0) = 2D: d2psidx(i,0) = d2psidx(i,1) = d2psidx(i,2) = 3D: d2psidx(i,0) = d2psidx(i,1) = d2psidx(i,2) = d2psidx(i,3) = d2psidx(i,4) = d2psidx(i,5) = .
Definition: elements.cc:3378
virtual void assign_all_generic_local_eqn_numbers(const bool &store_local_dof_pt)
Assign all the local equation numbering schemes that can be applied generically for the element...
Definition: elements.h:254
virtual void fill_in_contribution_to_inner_product_vectors(Vector< unsigned > const &history_index, Vector< Vector< double > > &inner_product_vector)
Fill in the contributions to the vectors that when taken as dot product with other history values giv...
Definition: elements.cc:1543
virtual void update_before_external_fd()
Function that is called before the finite differencing of any external data. This may be overloaded t...
Definition: elements.h:477
int local_eqn_number(const unsigned long &ieqn_global) const
Return the local equation number corresponding to the ieqn_global-th global equation number...
Definition: elements.h:731
virtual void d_dshape_eulerian_dnodal_coordinates(const double &det_jacobian, const DenseMatrix< double > &jacobian, const DenseMatrix< double > &djacobian_dX, const DenseMatrix< double > &inverse_jacobian, const DShape &dpsids, RankFourTensor< double > &d_dpsidx_dX) const
A template-free interface that calculates the derivative w.r.t. the nodal coordinates of the derivat...
Definition: elements.cc:2697
unsigned nnode() const
Return the number of nodes.
Definition: elements.h:2146
Vector< double > local_coordinate_in_bulk(const Vector< double > &s) const
Return vector of local coordinates in bulk element, given the local coordinates in this FaceElement...
Definition: elements.cc:6210
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
Definition: timesteppers.h:219
virtual double weight(const unsigned &i, const unsigned &j) const
Access function for j-th weight for the i-th derivative.
Definition: timesteppers.h:557
virtual void read_eqn_numbers_from_vector(const Vector< long > &vector_of_eqn_numbers, unsigned &index)
Read all equation numbers from the vector starting from index. On return the index will be set to the...
Definition: nodes.cc:1216
virtual void assign_internal_and_external_local_eqn_numbers(const bool &store_local_dof_pt)
Assign the local equation numbers for the internal and external Data This must be called after the gl...
Definition: elements.cc:909
int ** Data_local_eqn
Pointer to array storage for local equation numbers associated with internal and external data...
Definition: elements.h:106
virtual void add_eqn_numbers_to_vector(Vector< long > &vector_of_eqn_numbers)
Add all equation numbers to the vector in the internal storage order.
Definition: nodes.cc:1181
static double Default_fd_jacobian_step
Double used for the default finite difference step in elemental jacobian calculations.
Definition: elements.h:1164
static bool Suppress_warning_about_repeated_internal_data
Static boolean to suppress warnings about repeated internal data. Defaults to false.
Definition: elements.h:701
virtual double J_eulerian(const Vector< double > &s) const
Return the Jacobian of mapping from local to global coordinates at local position s...
Definition: elements.cc:4022
virtual void transform_second_derivatives(const DenseMatrix< double > &jacobian, const DenseMatrix< double > &inverse_jacobian, const DenseMatrix< double > &jacobian2, DShape &dbasis, DShape &d2basis) const
Convert derivatives and second derivatives w.r.t. local coordiantes to derivatives and second derivat...
Definition: elements.cc:3059
virtual double local_to_eulerian_mapping_diagonal(const DShape &dpsids, DenseMatrix< double > &jacobian, DenseMatrix< double > &inverse_jacobian) const
Calculate the mapping from local to Eulerian coordinates given the derivatives of the shape functions...
Definition: elements.cc:2536
bool internal_data_fd(const unsigned &i) const
Return the status of the boolean flag indicating whether the internal data is included in the finite ...
Definition: elements.h:152
int external_local_eqn(const unsigned &i, const unsigned &j)
Return the local equation number corresponding to the j-th value stored at the i-th external data...
Definition: elements.h:313
static DenseMatrix< double > Dummy_matrix
Empty dense matrix used as a dummy argument to combined residual and jacobian functions in the case w...
Definition: elements.h:228
unsigned Ninternal_data
Number of internal data.
Definition: elements.h:112
void describe_local_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the local dofs of the element. The ostream specifies the output stream to which ...
Definition: elements.cc:6375
virtual double local_to_lagrangian_mapping_diagonal(const DShape &dpsids, DenseMatrix< double > &jacobian, DenseMatrix< double > &inverse_jacobian) const
Calculate the mapping from local to Lagrangian coordinates given the derivatives of the shape functio...
Definition: elements.cc:6505
void fill_in_generic_jacobian_for_solid_ic(Vector< double > &residuals, DenseMatrix< double > &jacobian, const unsigned &flag)
Helper function to fill in the residuals and (if flag==1) the Jacobian for the setup of an initial co...
Definition: elements.cc:7236
bool external_data_fd(const unsigned &i) const
Return the status of the boolean flag indicating whether the external data is included in the finite ...
Definition: elements.h:759
virtual void get_dresidual_dnodal_coordinates(RankThreeTensor< double > &dresidual_dnodal_coordinates)
Compute derivatives of elemental residual vector with respect to nodal coordinates. Default implementation by FD can be overwritten for specific elements. dresidual_dnodal_coordinates(l,i,j) = d res(l) / dX_{ij}.
Definition: elements.cc:3666
int internal_local_eqn(const unsigned &i, const unsigned &j) const
Return the local equation number corresponding to the j-th value stored at the i-th internal data...
Definition: elements.h:268
virtual void node_update()
Update the positions of all nodes in the element using each node update function. The default impleme...
Definition: elements.cc:4945