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