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-2025 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
38namespace 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 //=======================================================================
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
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
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 {
121 }
122
123 // Now add the new value to the end of the internal data
125
126 // Copy the external values across
127 for (unsigned i = 0; i < n_external_data; i++)
128 {
130 }
131
132 // Delete the storage associated with the previous values
133 delete[] Data_pt;
134
135 // Set the pointer to the new storage
137
138 // Resize the array of boolean flags
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 {
144 }
145 // Now add the new flag to the end of the internal data
147
148 // Increase the number of internals
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 {
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
208 {
209 std::ostringstream 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(
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
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
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 //==========================================================================
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 //=======================================================================
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;
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
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 {
365 }
366
367 // Add the new data pointer to the end of the array
369
370 // Delete the storage associated with the previous values
371 delete[] Data_pt;
372
373 // Set the pointer to the new storage
375
376 // Resize the array of boolean flags
378 // Now add the new flag to the end of the external data
380
381 // Increase the number of externals
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
410 // Copy over the internal data only
411 for (unsigned i = 0; i < n_internal_data; i++)
412 {
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
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 {
470 }
471
472 // Copy over all the internal data
473 for (unsigned i = 0; i < n_internal_data; i++)
474 {
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
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
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
504
505 // Issue a warning if there will be external data remaining
506 if (Nexternal_data > 1)
507 {
508 std::ostringstream 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;
516 "GeneralisedElement::flush_external_data()",
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 {
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());
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());
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());
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 {
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 //=========================================================================
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 {
653 }
654 }
655
656 //=========================================================================
657 /// Add all equation numbers associated with internal data
658 /// to the vector in the internal storage order
659 //=========================================================================
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 {
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 {
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 {
717 {
718 error_stream << "Repeated global eqn: " << Eqn_number[n] << std::endl;
719 is_repeated[Eqn_number[n]] = true;
720 }
722 }
723
724 // If the sizes do not match we have a repeat, throw an error
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
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;
800 Vector<Data*> data_pt(e_el_pt->external_interaction_field_data_pt());
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;
834 e_el_pt->external_interaction_geometric_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 {
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 {
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 {
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;
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
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
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 {
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
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
1052 // Add pointer to the dof to the queue if required
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
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
1089 // Add pointer to the dof to the queue if required
1091 {
1092 GeneralisedElement::Dof_pt_deque.push_back(data_pt->value_pt(j));
1093 }
1094 // Add the local equation number to the local scheme
1096 // Increase the local number
1098 }
1099 else
1100 {
1101 // Set the local scheme to be pinned
1103 }
1104 }
1105 }
1106
1107 // Now add our global equations numbers to the internal element storage
1110 // Clear the memory used in the deque
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 //==========================================================================
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
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
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
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
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 //==========================================================================
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
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
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
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
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 //=====================================================================
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()",
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 //=====================================================================
1352 DenseMatrix<double>& jacobian,
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()",
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()",
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,
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()",
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,
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()",
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,
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()",
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,
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(
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 //==========================================================================
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(
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
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.
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());
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;
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 {
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(
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;
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 {
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
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.
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 //=========================================================================
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) +=
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 //=====================================================================
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) +=
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,
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,
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
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,
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
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,
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
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,
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(
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,
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,
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,
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,
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,
2352 const DShape& dpsids,
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,
2375 const DShape& dpsids,
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,
2407 const DShape& dpsids,
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,
2464 const DShape& dpsids,
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,
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
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,
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:
2738 jacobian, dpsids, djacobian_dX);
2739 break;
2740 case 1:
2742 jacobian, dpsids, djacobian_dX);
2743 break;
2744 case 2:
2746 jacobian, dpsids, djacobian_dX);
2747 break;
2748 case 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(
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,
2784 const DShape& dpsids,
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:
2813 jacobian,
2816 dpsids,
2817 d_dpsidx_dX);
2818 break;
2819 case 1:
2822 jacobian,
2825 dpsids,
2826 d_dpsidx_dX);
2827 break;
2828 case 2:
2831 jacobian,
2834 dpsids,
2835 d_dpsidx_dX);
2836 break;
2837 case 3:
2840 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(
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 //======================================================================
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 {
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 //======================================================================
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,
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)
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,
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
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
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,
3094 DShape& dbasis,
3095 DShape& d2basis) const
3096 {
3097 FiniteElement::transform_second_derivatives_template<1>(
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,
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
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,
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:
3170 break;
3171 case 2:
3174 break;
3175
3176 case 3:
3177 throw OomphLibError("Not implemented yet ... maybe one day",
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(
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 //==============================================================
3223 {
3224 // Default implementation is rather dumb
3225 // Get the local coordinate and scale by local coordinate range
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
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 //=========================================================================
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
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
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 //=========================================================================
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
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
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
3338
3339 // Allocate memory for the inverse jacobian
3341 // Now calculate the inverse jacobian
3343
3344 // Now set the values of the derivatives to be dpsidx
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 //========================================================================
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
3365
3366 // Allocate memory for the inverse jacobian
3368 // Now calculate the inverse jacobian
3370
3371 // Now set the values of the derivatives to dpsidx
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();
3394
3395 // Allocate memory for the jacobian and the inverse of the jacobian
3397
3398 // Now calculate the inverse jacobian
3399 const double det =
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
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,
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
3433
3434 // Allocate memory for the jacobian and the inverse of the jacobian
3436
3437 // Now calculate the inverse jacobian
3438 const double det =
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
3445
3446 // Calculate the derivative of dpsidx w.r.t. nodal coordinates
3447 // Note: this function also requires dpsids rather than dpsidx
3450
3451 // Now set the values of the derivatives to dpsidx
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
3492
3493 // Allocate memory for the jacobian and inverse jacobian
3495 // Calculate the jacobian and inverse jacobian
3496 const double det =
3498
3499 // Allocate memory for the jacobian of second derivatives
3501 // Assemble the jacobian of second derivatives
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 //==========================================================================
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
3546
3547 // Allocate memory for the jacobian and inverse jacobian
3549 // Calculate the jacobian and inverse jacobian
3550 const double det =
3552
3553 // Allocate memory for the jacobian of second derivatives
3555 // Assemble the jacobian of second derivatives
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 {
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
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
3654 // Add pointer to the dof to the queue if required
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
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
3675 // Clear the memory used in the deque
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 //==========================================================================
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
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
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
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 /// /=======================================================================
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
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
3814 nod_pt->perform_auxiliary_node_update_fct();
3815
3816 // Get advanced residual
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 {
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
3834 nod_pt->perform_auxiliary_node_update_fct();
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 //===============================================================
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(
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;
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
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 }
3985 }
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
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 {
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
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 {
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
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
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
4145 // Get the shape functions and local derivatives
4147
4148 // Right calculate the base vectors
4151
4152 // Calculate the metric tensor of the element
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",
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
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.
4216
4217 // Right calculate the base vectors
4220
4221 // Calculate the metric tensor of the element
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",
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
4291
4292 unsigned nintpt = integral_pt()->nweight();
4293 for (unsigned ipt = 0; ipt < nintpt; ipt++)
4294 {
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();
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
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();
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 //==================================================================
4409 Vector<double>& integral)
4410 {
4411 // Initialise all components of integral Vector
4412 const unsigned ncomponents = integral.size();
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
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();
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
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
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()",
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
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
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",
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;
4579 "FiniteElement::self_test()",
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;
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
4600 "FiniteElement::self_test()",
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
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 {
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,
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
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 {
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
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,
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 {
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::
4790 Radius_multiplier_for_fast_exit_from_locate_zeta *
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
4809
4810 // If the boolean argument use_coordinate_as_initial_guess was set
4811 // to true then we don't need to initialise s
4813 {
4814 // Vector of local coordinates
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
4836
4837 // Ifwe have specified the coordinate already
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;
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
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
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();
4954
4955 // Get the local shape functions and their derivatives
4957
4958 // Calculate the values of dxds
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
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
5118 /// - the pointer to a Data object
5119 /// - the index of the value in that Data object
5120 /// The generic implementation (should be overloaded in more specific
5121 /// applications) is to include all nodal and internal Data stored in
5122 /// the FiniteElement. Note that the geometric data,
5123 /// which includes the positions
5124 /// of SolidNodes, is treated separately by the function
5125 /// \c identify_geometric_data()
5126 //======================================================================
5128 std::set<std::pair<Data*, unsigned>>& paired_field_data)
5129 {
5130 // Loop over all internal data
5131 const unsigned n_internal = this->ninternal_data();
5132 for (unsigned n = 0; n < n_internal; n++)
5133 {
5134 // Cache the data pointer
5135 Data* const dat_pt = this->internal_data_pt(n);
5136 // Find the number of data values stored in the data object
5137 const unsigned n_value = dat_pt->nvalue();
5138 // Add the index of each data value and the pointer to the set
5139 // of pairs
5140 for (unsigned i = 0; i < n_value; i++)
5141 {
5142 paired_field_data.insert(std::make_pair(dat_pt, i));
5143 }
5144 }
5145
5146 // Loop over all the nodes
5147 const unsigned n_node = this->nnode();
5148 for (unsigned n = 0; n < n_node; n++)
5149 {
5150 // Cache the node pointer
5151 Node* const nod_pt = this->node_pt(n);
5152 // Find the number of values stored at the node
5153 const unsigned n_value = nod_pt->nvalue();
5154 // Add the index of each data value and the pointer to the set
5155 // of pairs
5156 for (unsigned i = 0; i < n_value; i++)
5157 {
5158 paired_field_data.insert(std::make_pair(nod_pt, i));
5159 }
5160 }
5161 }
5162
5163 void FiniteElement::build_face_element(const int& face_index,
5164 FaceElement* face_element_pt)
5165 {
5166 // Set the nodal dimension
5167 face_element_pt->set_nodal_dimension(nodal_dimension());
5168
5169 // Set the pointer to the orginal "bulk" element
5170 face_element_pt->bulk_element_pt() = this;
5171
5172#ifdef OOMPH_HAS_MPI
5173 // Pass on non-halo proc ID
5174 face_element_pt->set_halo(Non_halo_proc_ID);
5175#endif
5176
5177 // Set the face index
5178 face_element_pt->face_index() = face_index;
5179
5180 // Get number of bulk nodes on a face of this element
5181 const unsigned nnode_face = nnode_on_face();
5182
5183 // Set the function pointer for face coordinate to bulk coordinate
5184 // mapping
5185 face_element_pt->face_to_bulk_coordinate_fct_pt() =
5187
5188 // Set the function pointer for the derivative of the face coordinate to
5189 // bulk coordinate mapping
5190 face_element_pt->bulk_coordinate_derivatives_fct_pt() =
5192
5193 // Resize storage for the number of values originally stored each of the
5194 // face element's nodes.
5195 face_element_pt->nbulk_value_resize(nnode_face);
5196
5197 // Resize storage for the bulk node numbers corresponding to the face
5198 // element's nodes.
5199 face_element_pt->bulk_node_number_resize(nnode_face);
5200
5201 // Copy bulk_node_numbers and nbulk_values
5202 for (unsigned i = 0; i < nnode_face; i++)
5203 {
5204 // Find the corresponding bulk node's number
5205 unsigned bulk_number = get_bulk_node_number(face_index, i);
5206
5207 // Assign the pointer and number into the face element
5208 face_element_pt->node_pt(i) = node_pt(bulk_number);
5209 face_element_pt->bulk_node_number(i) = bulk_number;
5210
5211 // Set the number of values originally stored at this node
5212 face_element_pt->nbulk_value(i) = required_nvalue(bulk_number);
5213 }
5214
5215 // Set the outer unit normal sign
5216 face_element_pt->normal_sign() = face_outer_unit_normal_sign(face_index);
5217 }
5218
5219 //////////////////////////////////////////////////////////////////////////
5220 //////////////////////////////////////////////////////////////////////////
5221 //////////////////////////////////////////////////////////////////////////
5222
5223 // Initialise the static variable.
5224 // Do not ignore warning for discontinuous tangent vectors.
5226
5227
5228 //========================================================================
5229 /// Output boundary coordinate zeta
5230 //========================================================================
5231 void FaceElement::output_zeta(std::ostream& outfile, const unsigned& nplot)
5232 {
5233 // Vector of local coordinates
5234 unsigned n_dim = dim();
5236
5237 // Tecplot header info
5239
5240 // Loop over plot points
5242 for (unsigned iplot = 0; iplot < num_plot_points; iplot++)
5243 {
5244 // Get local coordinates of plot point
5246
5247 // Spatial coordinates are one higher
5248 for (unsigned i = 0; i < n_dim + 1; i++)
5249 {
5250 outfile << interpolated_x(s, i) << " ";
5251 }
5252
5253 // Boundary coordinate
5256 for (unsigned i = 0; i < n_dim; i++)
5257 {
5258 outfile << zeta[i] << " ";
5259 }
5260 outfile << std::endl;
5261 }
5262
5263 // Write tecplot footer (e.g. FE connectivity lists)
5265 }
5266
5267
5268 //========================================================================
5269 /// Calculate the determinant of the
5270 /// Jacobian of the mapping between local and global
5271 /// coordinates at the position s. Overloaded from FiniteElement.
5272 //========================================================================
5274 {
5275 // Find out the sptial dimension of the element
5276 unsigned n_dim_el = this->dim();
5277
5278 // Bail out if we're in a point element -- not sure what
5279 // J_eulerian actually is, but this is harmless
5280 if (n_dim_el == 0) return 1.0;
5281
5282 // Find out how many nodes there are
5283 unsigned n_node = nnode();
5284
5285 // Find out how many positional dofs there are
5286 unsigned n_position_type = this->nnodal_position_type();
5287
5288 // Find out the dimension of the node
5289 unsigned n_dim = this->nodal_dimension();
5290
5291 // Set up memory for the shape functions
5294
5295 // Only need to call the local derivatives
5297
5298 // Also calculate the surface Vectors (derivatives wrt local coordinates)
5300
5301 // Calculate positions and derivatives
5302 for (unsigned l = 0; l < n_node; l++)
5303 {
5304 // Loop over positional dofs
5305 for (unsigned k = 0; k < n_position_type; k++)
5306 {
5307 // Loop over coordinates
5308 for (unsigned i = 0; i < n_dim; i++)
5309 {
5310 // Loop over LOCAL derivative directions, to calculate the tangent(s)
5311 for (unsigned j = 0; j < n_dim_el; j++)
5312 {
5313 interpolated_A(j, i) +=
5315 }
5316 }
5317 }
5318 }
5319 // Now find the local deformed metric tensor from the tangent Vectors
5321 for (unsigned i = 0; i < n_dim_el; i++)
5322 {
5323 for (unsigned j = 0; j < n_dim_el; j++)
5324 {
5325 // Take the dot product
5326 for (unsigned k = 0; k < n_dim; k++)
5327 {
5328 A(i, j) += interpolated_A(i, k) * interpolated_A(j, k);
5329 }
5330 }
5331 }
5332
5333 // Find the determinant of the metric tensor
5334 double Adet = 0.0;
5335 switch (n_dim_el)
5336 {
5337 case 1:
5338 Adet = A(0, 0);
5339 break;
5340 case 2:
5341 Adet = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
5342 break;
5343 default:
5344 throw OomphLibError("Wrong dimension in FaceElement",
5345 "FaceElement::J_eulerian()",
5347 }
5348
5349 // Return
5350 return sqrt(Adet);
5351 }
5352
5353
5354 //========================================================================
5355 /// Compute the Jacobian of the mapping between the local and global
5356 /// coordinates at the ipt-th integration point. Overloaded from
5357 /// FiniteElement.
5358 //========================================================================
5359 double FaceElement::J_eulerian_at_knot(const unsigned& ipt) const
5360 {
5361 // Find the number of nodes
5362 const unsigned n_node = nnode();
5363
5364 // Find the number of position types
5365 const unsigned n_position_type = nnodal_position_type();
5366
5367 // Find the dimension of the node and element
5368 const unsigned n_dim = nodal_dimension();
5369 const unsigned n_dim_el = dim();
5370
5371 // Set up dummy memory for the shape functions
5374
5375 // Get the shape functions and local derivatives at the knot
5376 // This call may use the stored versions, which is why this entire
5377 // function doesn't just call J_eulerian(s), after reading out s from
5378 // the knots.
5380
5381 // Also calculate the surface Vectors (derivatives wrt local coordinates)
5383
5384 // Calculate positions and derivatives
5385 for (unsigned l = 0; l < n_node; l++)
5386 {
5387 // Loop over positional dofs
5388 for (unsigned k = 0; k < n_position_type; k++)
5389 {
5390 // Loop over coordinates
5391 for (unsigned i = 0; i < n_dim; i++)
5392 {
5393 // Loop over LOCAL derivative directions, to calculate the tangent(s)
5394 for (unsigned j = 0; j < n_dim_el; j++)
5395 {
5396 interpolated_A(j, i) +=
5398 }
5399 }
5400 }
5401 }
5402
5403 // Now find the local deformed metric tensor from the tangent Vectors
5405 for (unsigned i = 0; i < n_dim_el; i++)
5406 {
5407 for (unsigned j = 0; j < n_dim_el; j++)
5408 {
5409 // Take the dot product
5410 for (unsigned k = 0; k < n_dim; k++)
5411 {
5412 A(i, j) += interpolated_A(i, k) * interpolated_A(j, k);
5413 }
5414 }
5415 }
5416
5417 // Find the determinant of the metric tensor
5418 double Adet = 0.0;
5419 switch (n_dim_el)
5420 {
5421 case 1:
5422 Adet = A(0, 0);
5423 break;
5424 case 2:
5425 Adet = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
5426 break;
5427 default:
5428 throw OomphLibError("Wrong dimension in FaceElement",
5429 "FaceElement::J_eulerian_at_knot()",
5431 }
5432
5433 // Return
5434 return sqrt(Adet);
5435 }
5436
5437 //========================================================================
5438 /// Check that Jacobian of mapping between local and Eulerian
5439 /// coordinates at all integration points is positive.
5440 //========================================================================
5442 {
5443 // Let's be optimistic...
5444 passed = true;
5445
5446 // Find the number of nodes
5447 const unsigned n_node = nnode();
5448
5449 // Find the number of position types
5450 const unsigned n_position_type = nnodal_position_type();
5451
5452 // Find the dimension of the node and element
5453 const unsigned n_dim = nodal_dimension();
5454 const unsigned n_dim_el = dim();
5455
5456 // Set up dummy memory for the shape functions
5459
5460
5461 unsigned nintpt = integral_pt()->nweight();
5462 for (unsigned ipt = 0; ipt < nintpt; ipt++)
5463 {
5464 // Get the shape functions and local derivatives at the knot
5465 // This call may use the stored versions, which is why this entire
5466 // function doesn't just call J_eulerian(s), after reading out s from
5467 // the knots.
5469
5470 // Also calculate the surface Vectors (derivatives wrt local coordinates)
5472
5473 // Calculate positions and derivatives
5474 for (unsigned l = 0; l < n_node; l++)
5475 {
5476 // Loop over positional dofs
5477 for (unsigned k = 0; k < n_position_type; k++)
5478 {
5479 // Loop over coordinates
5480 for (unsigned i = 0; i < n_dim; i++)
5481 {
5482 // Loop over LOCAL derivative directions, to calculate the
5483 // tangent(s)
5484 for (unsigned j = 0; j < n_dim_el; j++)
5485 {
5486 interpolated_A(j, i) +=
5488 dpsids(l, k, j);
5489 }
5490 }
5491 }
5492 }
5493
5494 // Now find the local deformed metric tensor from the tangent Vectors
5496 for (unsigned i = 0; i < n_dim_el; i++)
5497 {
5498 for (unsigned j = 0; j < n_dim_el; j++)
5499 {
5500 // Take the dot product
5501 for (unsigned k = 0; k < n_dim; k++)
5502 {
5503 A(i, j) += interpolated_A(i, k) * interpolated_A(j, k);
5504 }
5505 }
5506 }
5507
5508 // Find the determinant of the metric tensor
5509 double Adet = 0.0;
5510 switch (n_dim_el)
5511 {
5512 case 1:
5513 Adet = A(0, 0);
5514 break;
5515 case 2:
5516 Adet = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
5517 break;
5518 default:
5519 throw OomphLibError("Wrong dimension in FaceElement",
5520 "FaceElement::J_eulerian_at_knot()",
5522 }
5523
5524
5525 // Are we dead yet?
5526 if (Adet <= 0.0)
5527 {
5528 passed = false;
5529 return;
5530 }
5531 }
5532 }
5533
5534 //=======================================================================
5535 /// Compute the tangent vector(s) and the outer unit normal
5536 /// vector at the specified local coordinate.
5537 /// In two spatial dimensions, a "tangent direction" is not required.
5538 /// In three spatial dimensions, a tangent direction is required
5539 /// (set via set_tangent_direction(...)), and we project the tanent direction
5540 /// on to the surface. The second tangent vector is taken to be the cross
5541 /// product of the projection and the unit normal.
5542 //=======================================================================
5544 const Vector<double>& s,
5547 {
5548 // Find the spatial dimension of the FaceElement
5549 const unsigned element_dim = dim();
5550
5551 // Find the overall dimension of the problem
5552 //(assume that it's the same for all nodes)
5553 const unsigned spatial_dim = nodal_dimension();
5554
5555#ifdef PARANOID
5556 // Check the number of local coordinates passed
5557 if (s.size() != element_dim)
5558 {
5559 std::ostringstream error_stream;
5561 << "Local coordinate s passed to outer_unit_normal() has dimension "
5562 << s.size() << std::endl
5563 << "but element dimension is " << element_dim << std::endl;
5564
5565 throw OomphLibError(
5567 }
5568
5569 // Check that if the Tangent_direction_pt is set, then
5570 // it is the same length as the spatial dimension.
5571 if (Tangent_direction_pt != 0 &&
5573 {
5574 std::ostringstream error_stream;
5575 error_stream << "Tangent direction vector has dimension "
5576 << Tangent_direction_pt->size() << std::endl
5577 << "but spatial dimension is " << spatial_dim << std::endl;
5578
5579 throw OomphLibError(
5581 }
5582
5583 // Check the dimension of the normal vector
5584 if (unit_normal.size() != spatial_dim)
5585 {
5586 std::ostringstream error_stream;
5587 error_stream << "Unit normal passed to outer_unit_normal() has dimension "
5588 << unit_normal.size() << std::endl
5589 << "but spatial dimension is " << spatial_dim << std::endl;
5590
5591 throw OomphLibError(
5593 }
5594
5595
5596 // The number of tangent vectors.
5597 unsigned ntangent_vec = tang_vec.size();
5598
5599 // For the tangent vector,
5600 // if element_dim = 2, tang_vec is a 2D Vector of length 3.
5601 // if element_dim = 1, tang_vec is a 1D Vector of length 2.
5602 // if element_dim = 0, tang_vec is a 1D Vector of length 1.
5603 switch (element_dim)
5604 {
5605 // Point element, derived from a 1D element.
5606 case 0:
5607 {
5608 // Check that tang_vec is a 1D vector.
5609 if (ntangent_vec != 1)
5610 {
5611 std::ostringstream error_stream;
5613 << "This is a 0D FaceElement, we need one tangent vector.\n"
5614 << "You have given me " << tang_vec.size() << " vector(s).\n";
5615 throw OomphLibError(error_stream.str(),
5618 }
5619 }
5620 break;
5621
5622 // Line element, derived from a 2D element.
5623 case 1:
5624 {
5625 // Check that tang_vec is a 1D vector.
5626 if (ntangent_vec != 1)
5627 {
5628 std::ostringstream error_stream;
5630 << "This is a 1D FaceElement, we need one tangent vector.\n"
5631 << "You have given me " << tang_vec.size() << " vector(s).\n";
5632 throw OomphLibError(error_stream.str(),
5635 }
5636 }
5637 break;
5638
5639 // Plane element, derived from 3D element.
5640 case 2:
5641 {
5642 // Check that tang_vec is a 2D vector.
5643 if (ntangent_vec != 2)
5644 {
5645 std::ostringstream error_stream;
5647 << "This is a 2D FaceElement, we need two tangent vectors.\n"
5648 << "You have given me " << tang_vec.size() << " vector(s).\n";
5649 throw OomphLibError(error_stream.str(),
5652 }
5653 }
5654 break;
5655 // There are no other elements!
5656 default:
5657
5658 throw OomphLibError(
5659 "Cannot have a FaceElement with dimension higher than 2",
5662 break;
5663 }
5664
5665 // Check the lengths of each sub vectors.
5666 for (unsigned vec_i = 0; vec_i < ntangent_vec; vec_i++)
5667 {
5668 if (tang_vec[vec_i].size() != spatial_dim)
5669 {
5670 std::ostringstream error_stream;
5671 error_stream << "This problem has " << spatial_dim
5672 << " spatial dimensions.\n"
5673 << "But the " << vec_i << " tangent vector has size "
5674 << tang_vec[vec_i].size() << std::endl;
5675 throw OomphLibError(
5677 }
5678 }
5679
5680#endif
5681
5682
5683 // Now let's consider the different element dimensions
5684 switch (element_dim)
5685 {
5686 // Point element, derived from a 1D element.
5687 case 0:
5688 {
5689 std::ostringstream error_stream;
5691 << "It is unclear how to calculate a tangent and normal vectors for "
5692 << "a point element.\n";
5693 throw OomphLibError(
5695 }
5696 break;
5697
5698 // Line element, derived from a 2D element, in this case
5699 // the normal is a mess of cross products
5700 // We need an interior direction, so we must find the local
5701 // derivatives in the BULK element
5702 case 1:
5703 {
5704 // Find the number of nodes in the bulk element
5705 const unsigned n_node_bulk = Bulk_element_pt->nnode();
5706 // Find the number of position types in the bulk element
5707 const unsigned n_position_type_bulk =
5709
5710 // Construct the local coordinate in the bulk element
5712 // Get the local coordinates in the bulk element
5714
5715 // Allocate storage for the shape functions and their derivatives wrt
5716 // local coordinates
5719 // Get the value of the shape functions at the given local coordinate
5721
5722 // Calculate all derivatives of the spatial coordinates
5723 // wrt local coordinates
5725
5726 // Loop over all parent nodes
5727 for (unsigned l = 0; l < n_node_bulk; l++)
5728 {
5729 // Loop over all position types in the bulk
5730 for (unsigned k = 0; k < n_position_type_bulk; k++)
5731 {
5732 // Loop over derivative direction
5733 for (unsigned j = 0; j < 2; j++)
5734 {
5735 // Loop over coordinate directions
5736 for (unsigned i = 0; i < spatial_dim; i++)
5737 {
5738 // Compute the spatial derivative
5739 interpolated_dxds(j, i) +=
5741 dpsids(l, k, j);
5742 }
5743 }
5744 }
5745 }
5746
5747 // Initialise the tangent, interior tangent and normal vectors to zero
5748 // The idea is that even if the element is in a two-dimensional space,
5749 // the normal cannot be calculated without embedding the element in
5750 // three dimensions, in which case, the tangent and interior tangent
5751 // will have zero z-components.
5752 Vector<double> tangent(3, 0.0), interior_tangent(3, 0.0),
5753 normal(3, 0.0);
5754
5755 // We must get the relationship between the coordinate along the face
5756 // and the local coordinates in the bulk element
5757 // We must also find an interior direction
5759 unsigned interior_direction = 0;
5761 // Load in the values for the tangents
5762 for (unsigned i = 0; i < spatial_dim; i++)
5763 {
5764 // Tangent to the face is the derivative wrt to the face coordinate
5765 // which is calculated using dsbulk_dsface and the chain rule
5766 tangent[i] = interpolated_dxds(0, i) * dsbulk_dsface(0, 0) +
5767 interpolated_dxds(1, i) * dsbulk_dsface(1, 0);
5768 // Interior tangent to the face is the derivative in the interior
5769 // direction
5771 }
5772
5773 // Now the (3D) normal to the element is the interior tangent
5774 // crossed with the tangent
5776
5777 // We find the line normal by crossing the element normal with the
5778 // tangent
5781
5782 // Copy the appropriate entries into the unit normal
5783 // Two or Three depending upon the spatial dimension of the system
5784 for (unsigned i = 0; i < spatial_dim; i++)
5785 {
5787 }
5788
5789 // Finally normalise unit normal and multiply by the Normal_sign
5791 for (unsigned i = 0; i < spatial_dim; i++)
5792 {
5794 }
5795
5796 // Create the tangent vector
5797 tang_vec[0][0] = -unit_normal[1];
5798 tang_vec[0][1] = unit_normal[0];
5799 }
5800 break;
5801
5802 // Plane element, derived from 3D element, in this case the normal
5803 // is just the cross product of the two surface tangents
5804 // We assume, therefore, that we have three spatial coordinates
5805 // and two surface coordinates
5806 // Then we need only to get the derivatives wrt the local coordinates
5807 // in this face element
5808 case 2:
5809 {
5810#ifdef PARANOID
5811 // Check that we actually have three spatial dimensions
5812 if (spatial_dim != 3)
5813 {
5814 std::ostringstream error_stream;
5815 error_stream << "There are only " << spatial_dim
5816 << "coordinates at the nodes of this 2D FaceElement,\n"
5817 << "which must have come from a 3D Bulk element\n";
5818 throw OomphLibError(error_stream.str(),
5821 }
5822#endif
5823
5824 // Find the number of nodes in the element
5825 const unsigned n_node = this->nnode();
5826 // Find the number of position types
5827 const unsigned n_position_type = this->nnodal_position_type();
5828
5829 // Allocate storage for the shape functions and their derivatives wrt
5830 // local coordinates
5833 // Get the value of the shape functions at the given local coordinate
5834 this->dshape_local(s, psi, dpsids);
5835
5836 // Calculate all derivatives of the spatial coordinates
5837 // wrt local coordinates
5839
5840 // Loop over all nodes
5841 for (unsigned l = 0; l < n_node; l++)
5842 {
5843 // Loop over all position types
5844 for (unsigned k = 0; k < n_position_type; k++)
5845 {
5846 // Loop over derivative directions
5847 for (unsigned j = 0; j < 2; j++)
5848 {
5849 // Loop over coordinate directions
5850 for (unsigned i = 0; i < 3; i++)
5851 {
5852 // Compute the spatial derivative
5853 // Remember that we need to translate the position type
5854 // to its location in the bulk node
5855 interpolated_dxds[j][i] +=
5857 dpsids(l, k, j);
5858 }
5859 }
5860 }
5861 }
5862
5863 // We now take the cross product of the two normal vectors
5866
5867 // Finally normalise unit normal
5869
5870 for (unsigned i = 0; i < spatial_dim; i++)
5871 {
5873 }
5874
5875 // Next we create the continuous tangent vectors!
5876 if (Tangent_direction_pt != 0)
5877 // There is a general direction that the first tangent vector should
5878 // follow.
5879 {
5880 // Project the Tangent direction vector onto the surface.
5881 // Project Tangent_direction_pt D onto the plane P defined by
5882 // T1 and T2:
5883 // proj_P(D) = proj_T1(D) + proj_T2(D), where D is
5884 // Tangent_direction_pt, recall that proj_u(v) = (u.v)/(u.u) * u
5885
5886 // Get the direction vector. The vector is NOT copied! :)
5888
5889#ifdef PARANOID
5890 // Check that the angle between the direction vector and the normal
5891 // is not less than 10 degrees
5893 (10.0 * MathematicalConstants::Pi / 180.0))
5894 {
5895 std::ostringstream err_stream;
5896 err_stream << "The angle between the unit normal and the \n"
5897 << "direction vector is less than 10 degrees.";
5898 throw OomphLibError(err_stream.str(),
5901 }
5902#endif
5903
5904 // Calculate the two scalings, (u.v) / (u.u)
5905 double t1_scaling =
5908
5909 double t2_scaling =
5912
5913 // Finish off the projection.
5914 tang_vec[0][0] = t1_scaling * interpolated_dxds[0][0] +
5916 tang_vec[0][1] = t1_scaling * interpolated_dxds[0][1] +
5918 tang_vec[0][2] = t1_scaling * interpolated_dxds[0][2] +
5920
5921 // The second tangent vector is the cross product of
5922 // tang_vec[0] and the normal vector N.
5924
5925 // Normalise the tangent vectors
5926 for (unsigned vec_i = 0; vec_i < 2; vec_i++)
5927 {
5928 // Get the length...
5930
5931 for (unsigned dim_i = 0; dim_i < spatial_dim; dim_i++)
5932 {
5934 }
5935 }
5936 }
5937 else
5938 // A direction for the first tangent vector is not supplied, we do our
5939 // best to keep it continuous. But if the normal vector is aligned with
5940 // the z axis, then the tangent vector may "flip", even within elements.
5941 // This is because we check this by comparing doubles.
5942 // The code is copied from impose_parallel_outflow_element.h,
5943 // NO modification is done except for a few variable name changes.
5944 {
5945 double a, b, c;
5946 a = unit_normal[0];
5947 b = unit_normal[1];
5948 c = unit_normal[2];
5949
5950 if (a != 0.0 || b != 0.0)
5951 {
5952 double a_sq = a * a;
5953 double b_sq = b * b;
5954 double c_sq = c * c;
5955
5956 tang_vec[0][0] = -b / sqrt(a_sq + b_sq);
5957 tang_vec[0][1] = a / sqrt(a_sq + b_sq);
5958 tang_vec[0][2] = 0;
5959
5960 double z = (a_sq + b_sq) / sqrt(a_sq * c_sq + b_sq * c_sq +
5961 (a_sq + b_sq) * (a_sq + b_sq));
5962
5963 tang_vec[1][0] = -(a * c * z) / (a_sq + b_sq);
5964 tang_vec[1][1] = -(b * c * z) / (a_sq + b_sq);
5965 tang_vec[1][2] = z;
5966 // NB : we didn't use the fact that N is normalized,
5967 // that's why we have these unsimplified formulas
5968 }
5969 else if (c != 0.0)
5970 {
5972 {
5973 std::ostringstream warn_stream;
5975 << "I have detected that your normal vector is [0,0,1].\n"
5976 << "Since this function compares against 0.0, you may\n"
5977 << "get discontinuous tangent vectors.";
5981 }
5982
5983 tang_vec[0][0] = 1.0;
5984 tang_vec[0][1] = 0.0;
5985 tang_vec[0][2] = 0.0;
5986
5987 tang_vec[1][0] = 0.0;
5988 tang_vec[1][1] = 1.0;
5989 tang_vec[1][2] = 0.0;
5990 }
5991 else
5992 {
5993 throw OomphLibError("You have a zero normal vector!! ",
5996 }
5997 }
5998 }
5999 break;
6000
6001 default:
6002
6003 throw OomphLibError(
6004 "Cannot have a FaceElement with dimension higher than 2",
6007 break;
6008 }
6009 }
6010
6011 //=======================================================================
6012 /// Compute the tangent vector(s) and the outer unit normal
6013 /// vector at the ipt-th integration point. This is a wrapper around
6014 /// continuous_tangent_and_outer_unit_normal(...) with the integration points
6015 /// converted into local coordinates.
6016 //=======================================================================
6018 const unsigned& ipt,
6021 {
6022 // Find the dimension of the element
6023 const unsigned element_dim = dim();
6024 // Find the local coordinates of the ipt-th integration point
6026 for (unsigned i = 0; i < element_dim; i++)
6027 {
6028 s[i] = integral_pt()->knot(ipt, i);
6029 }
6030 // Call the outer unit normal function
6032 }
6033
6034 //=======================================================================
6035 /// Compute the outer unit normal at the specified local coordinate
6036 //=======================================================================
6039 {
6040 // Find the spatial dimension of the FaceElement
6041 const unsigned element_dim = dim();
6042
6043 // Find the overall dimension of the problem
6044 //(assume that it's the same for all nodes)
6045 const unsigned spatial_dim = nodal_dimension();
6046
6047#ifdef PARANOID
6048 // Check the number of local coordinates passed
6049 if (s.size() != element_dim)
6050 {
6051 std::ostringstream error_stream;
6053 << "Local coordinate s passed to outer_unit_normal() has dimension "
6054 << s.size() << std::endl
6055 << "but element dimension is " << element_dim << std::endl;
6056
6057 throw OomphLibError(
6059 }
6060
6061 // Check the dimension of the normal vector
6062 if (unit_normal.size() != spatial_dim)
6063 {
6064 std::ostringstream error_stream;
6065 error_stream << "Unit normal passed to outer_unit_normal() has dimension "
6066 << unit_normal.size() << std::endl
6067 << "but spatial dimension is " << spatial_dim << std::endl;
6068
6069 throw OomphLibError(
6071 }
6072#endif
6073
6074 /* //The spatial dimension of the bulk element will be element_dim+1
6075 const unsigned bulk_dim = element_dim + 1;
6076
6077 //Find the number of nodes in the bulk element
6078 const unsigned n_node_bulk = Bulk_element_pt->nnode();
6079 //Find the number of position types in the bulk element
6080 const unsigned n_position_type_bulk =
6081 Bulk_element_pt->nnodal_position_type();
6082
6083 //Construct the local coordinate in the bulk element
6084 Vector<double> s_bulk(bulk_dim);
6085 //Set the value of the bulk coordinate that is fixed on the face
6086 //s_bulk[s_fixed_index()] = s_fixed_value();
6087
6088 //Set the other bulk coordinates
6089 //for(unsigned i=0;i<element_dim;i++) {s_bulk[bulk_s_index(i)] = s[i];}
6090
6091 get_local_coordinate_in_bulk(s,s_bulk);
6092
6093 //Allocate storage for the shape functions and their derivatives wrt
6094 //local coordinates
6095 Shape psi(n_node_bulk,n_position_type_bulk);
6096 DShape dpsids(n_node_bulk,n_position_type_bulk,bulk_dim);
6097 //Get the value of the shape functions at the given local coordinate
6098 Bulk_element_pt->dshape_local(s_bulk,psi,dpsids);
6099
6100 //Calculate all derivatives of the spatial coordinates wrt local
6101 coordinates DenseMatrix<double> interpolated_dxds(bulk_dim,spatial_dim);
6102 //Initialise to zero
6103 for(unsigned j=0;j<bulk_dim;j++)
6104 {for(unsigned i=0;i<spatial_dim;i++) {interpolated_dxds(j,i) = 0.0;}}
6105
6106 //Loop over all parent nodes
6107 for(unsigned l=0;l<n_node_bulk;l++)
6108 {
6109 //Loop over all position types in the bulk
6110 for(unsigned k=0;k<n_position_type_bulk;k++)
6111 {
6112 //Loop over derivative direction
6113 for(unsigned j=0;j<bulk_dim;j++)
6114 {
6115 //Loop over coordinate directions
6116 for(unsigned i=0;i<spatial_dim;i++)
6117 {
6118 //Compute the spatial derivative
6119 interpolated_dxds(j,i) +=
6120 Bulk_element_pt->nodal_position_gen(l,k,i)*dpsids(l,k,j);
6121 }
6122 }
6123 }
6124 }*/
6125
6126 // Now let's consider the different element dimensions
6127 switch (element_dim)
6128 {
6129 // Point element, derived from a 1D element, in this case
6130 // the normal is merely the tangent to the bulk element
6131 // and there is only one free coordinate in the bulk element
6132 // Hence we will need to calculate the derivatives wrt the
6133 // local coordinates in the BULK element.
6134 case 0:
6135 {
6136 // Find the number of nodes in the bulk element
6137 const unsigned n_node_bulk = Bulk_element_pt->nnode();
6138 // Find the number of position types in the bulk element
6139 const unsigned n_position_type_bulk =
6141
6142 // Construct the local coordinate in the bulk element
6144
6145 // Get the local coordinates in the bulk element
6147
6148 // Allocate storage for the shape functions and their derivatives wrt
6149 // local coordinates
6152 // Get the value of the shape functions at the given local coordinate
6154
6155 // Calculate all derivatives of the spatial coordinates wrt
6156 // local coordinates
6158
6159 // Loop over all parent nodes
6160 for (unsigned l = 0; l < n_node_bulk; l++)
6161 {
6162 // Loop over all position types in the bulk
6163 for (unsigned k = 0; k < n_position_type_bulk; k++)
6164 {
6165 // Loop over coordinate directions
6166 for (unsigned i = 0; i < spatial_dim; i++)
6167 {
6168 // Compute the spatial derivative
6169 interpolated_dxds(0, i) +=
6171 }
6172 }
6173 }
6174
6175 // Now the unit normal is just the derivative of the position vector
6176 // with respect to the single coordinate
6177 for (unsigned i = 0; i < spatial_dim; i++)
6178 {
6180 }
6181 }
6182 break;
6183
6184 // Line element, derived from a 2D element, in this case
6185 // the normal is a mess of cross products
6186 // We need an interior direction, so we must find the local
6187 // derivatives in the BULK element
6188 case 1:
6189 {
6190 // Find the number of nodes in the bulk element
6191 const unsigned n_node_bulk = Bulk_element_pt->nnode();
6192 // Find the number of position types in the bulk element
6193 const unsigned n_position_type_bulk =
6195
6196 // Construct the local coordinate in the bulk element
6198 // Get the local coordinates in the bulk element
6200
6201 // Allocate storage for the shape functions and their derivatives wrt
6202 // local coordinates
6205 // Get the value of the shape functions at the given local coordinate
6207
6208 // Calculate all derivatives of the spatial coordinates
6209 // wrt local coordinates
6211
6212 // Loop over all parent nodes
6213 for (unsigned l = 0; l < n_node_bulk; l++)
6214 {
6215 // Loop over all position types in the bulk
6216 for (unsigned k = 0; k < n_position_type_bulk; k++)
6217 {
6218 // Loop over derivative direction
6219 for (unsigned j = 0; j < 2; j++)
6220 {
6221 // Loop over coordinate directions
6222 for (unsigned i = 0; i < spatial_dim; i++)
6223 {
6224 // Compute the spatial derivative
6225 interpolated_dxds(j, i) +=
6227 dpsids(l, k, j);
6228 }
6229 }
6230 }
6231 }
6232
6233 // Initialise the tangent, interior tangent and normal vectors to zero
6234 // The idea is that even if the element is in a two-dimensional space,
6235 // the normal cannot be calculated without embedding the element in
6236 // three dimensions, in which case, the tangent and interior tangent
6237 // will have zero z-components.
6238 Vector<double> tangent(3, 0.0), interior_tangent(3, 0.0),
6239 normal(3, 0.0);
6240
6241 // We must get the relationship between the coordinate along the face
6242 // and the local coordinates in the bulk element
6243 // We must also find an interior direction
6245 unsigned interior_direction = 0;
6247 // Load in the values for the tangents
6248 for (unsigned i = 0; i < spatial_dim; i++)
6249 {
6250 // Tangent to the face is the derivative wrt to the face coordinate
6251 // which is calculated using dsbulk_dsface and the chain rule
6252 tangent[i] = interpolated_dxds(0, i) * dsbulk_dsface(0, 0) +
6253 interpolated_dxds(1, i) * dsbulk_dsface(1, 0);
6254 // Interior tangent to the face is the derivative in the interior
6255 // direction
6257 }
6258
6259 // Now the (3D) normal to the element is the interior tangent
6260 // crossed with the tangent
6262
6263 // We find the line normal by crossing the element normal with the
6264 // tangent
6267
6268 // Copy the appropriate entries into the unit normal
6269 // Two or Three depending upon the spatial dimension of the system
6270 for (unsigned i = 0; i < spatial_dim; i++)
6271 {
6273 }
6274 }
6275 break;
6276
6277 // Plane element, derived from 3D element, in this case the normal
6278 // is just the cross product of the two surface tangents
6279 // We assume, therefore, that we have three spatial coordinates
6280 // and two surface coordinates
6281 // Then we need only to get the derivatives wrt the local coordinates
6282 // in this face element
6283 case 2:
6284 {
6285#ifdef PARANOID
6286 // Check that we actually have three spatial dimensions
6287 if (spatial_dim != 3)
6288 {
6289 std::ostringstream error_stream;
6290 error_stream << "There are only " << spatial_dim
6291 << "coordinates at the nodes of this 2D FaceElement,\n"
6292 << "which must have come from a 3D Bulk element\n";
6293 throw OomphLibError(error_stream.str(),
6296 }
6297#endif
6298
6299 // Find the number of nodes in the element
6300 const unsigned n_node = this->nnode();
6301 // Find the number of position types
6302 const unsigned n_position_type = this->nnodal_position_type();
6303
6304 // Allocate storage for the shape functions and their derivatives wrt
6305 // local coordinates
6308 // Get the value of the shape functions at the given local coordinate
6309 this->dshape_local(s, psi, dpsids);
6310
6311 // Calculate all derivatives of the spatial coordinates
6312 // wrt local coordinates
6314
6315 // Loop over all nodes
6316 for (unsigned l = 0; l < n_node; l++)
6317 {
6318 // Loop over all position types
6319 for (unsigned k = 0; k < n_position_type; k++)
6320 {
6321 // Loop over derivative directions
6322 for (unsigned j = 0; j < 2; j++)
6323 {
6324 // Loop over coordinate directions
6325 for (unsigned i = 0; i < 3; i++)
6326 {
6327 // Compute the spatial derivative
6328 // Remember that we need to translate the position type
6329 // to its location in the bulk node
6330 interpolated_dxds[j][i] +=
6332 dpsids(l, k, j);
6333 }
6334 }
6335 }
6336 }
6337
6338 // We now take the cross product of the two normal vectors
6341 }
6342 break;
6343
6344 default:
6345
6346 throw OomphLibError(
6347 "Cannot have a FaceElement with dimension higher than 2",
6350 break;
6351 }
6352
6353 // Finally normalise unit normal
6355 for (unsigned i = 0; i < spatial_dim; i++)
6356 {
6358 }
6359 }
6360
6361 //=======================================================================
6362 /// Compute the outer unit normal at the ipt-th integration point
6363 //=======================================================================
6366 {
6367 // Find the dimension of the element
6368 const unsigned element_dim = dim();
6369 // Find the local coordiantes of the ipt-th integration point
6371 for (unsigned i = 0; i < element_dim; i++)
6372 {
6373 s[i] = integral_pt()->knot(ipt, i);
6374 }
6375 // Call the outer unit normal function
6377 }
6378
6379
6380 //=======================================================================
6381 /// Return vector of local coordinates in bulk element,
6382 /// given the local coordinates in this FaceElement
6383 //=======================================================================
6385 const Vector<double>& s) const
6386 {
6387 // Find the dimension of the bulk element
6388 unsigned dim_bulk = Bulk_element_pt->dim();
6389
6390 // Vector of local coordinates in bulk element
6392
6393 // Use the function pointer if it is set
6395 {
6396 // Call the translation function
6397 (*Face_to_bulk_coordinate_fct_pt)(s, s_bulk);
6398 }
6399 else
6400 {
6401 throw OomphLibError("Face_to_bulk_coordinate mapping not set",
6404 }
6405
6406 // Return it
6407 return s_bulk;
6408 }
6409
6410
6411 //=======================================================================
6412 /// Calculate the vector of local coordinates in bulk element,
6413 /// given the local coordinates in this FaceElement
6414 //=======================================================================
6416 Vector<double>& s_bulk) const
6417 {
6418 // Use the function pointer if it is set
6420 {
6421 // Call the translation function
6422 (*Face_to_bulk_coordinate_fct_pt)(s, s_bulk);
6423 }
6424 // Otherwise use the existing (not general) interface
6425 else
6426 {
6427 throw OomphLibError("Face_to_bulk_coordinate mapping not set",
6430 }
6431 }
6432
6433
6434 //=======================================================================
6435 /// Calculate the derivatives of the local coordinates in the
6436 /// bulk element with respect to the local coordinates in this FaceElement.
6437 /// In addition return the index of a bulk local coordinate that varies away
6438 /// from the face.
6439 //=======================================================================
6442 unsigned& interior_direction) const
6443 {
6444 // Use the function pointer if it is set
6446 {
6447 // Call the translation function
6448 (*Bulk_coordinate_derivatives_fct_pt)(
6450 }
6451 // Otherwise throw an error
6452 else
6453 {
6454 throw OomphLibError(
6455 "No function for derivatives of bulk coords wrt face coords set",
6458 }
6459 }
6460
6461
6462 ///////////////////////////////////////////////////////////////////////////
6463 ///////////////////////////////////////////////////////////////////////////
6464 // Functions for elastic general elements
6465 ///////////////////////////////////////////////////////////////////////////
6466 ///////////////////////////////////////////////////////////////////////////
6467
6468
6469 //==================================================================
6470 /// Calculate the L2 norm of the displacement u=R-r to overload the
6471 /// compute_norm function in the GeneralisedElement base class
6472 //==================================================================
6474 {
6475 // Initialise el_norm to 0.0
6476 el_norm = 0.0;
6477
6478 unsigned n_dim = dim();
6479
6480 // Vector of local coordinates
6482
6483 // Displacement vector, Lagrangian coordinate vector and Eulerian
6484 // coordinate vector respectively
6486 Vector<double> xi(n_dim, 0.0);
6487 Vector<double> x(n_dim, 0.0);
6488
6489 // Find out how many nodes there are in the element
6490 unsigned n_node = this->nnode();
6491
6492 // Construct a shape function
6493 Shape psi(n_node);
6494
6495 // Get the number of integration points
6496 unsigned n_intpt = this->integral_pt()->nweight();
6497
6498 // Loop over the integration points
6499 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
6500 {
6501 // Assign values of s
6502 for (unsigned i = 0; i < n_dim; i++)
6503 {
6504 s[i] = this->integral_pt()->knot(ipt, i);
6505 }
6506
6507 // Get the integral weight
6508 double w = this->integral_pt()->weight(ipt);
6509
6510 // Get jacobian of mapping
6511 double J = this->J_eulerian(s);
6512
6513 // Premultiply the weights and the Jacobian
6514 double W = w * J;
6515
6516 // Get the Lagrangian and Eulerian coordinate at this point, respectively
6517 this->interpolated_xi(s, xi);
6518 this->interpolated_x(s, x);
6519
6520 // Calculate the displacement vector, u=R-r=x-xi
6521 for (unsigned idim = 0; idim < n_dim; idim++)
6522 {
6523 disp[idim] = x[idim] - xi[idim];
6524 }
6525
6526 // Add to norm
6527 for (unsigned ii = 0; ii < n_dim; ii++)
6528 {
6529 el_norm += (disp[ii] * disp[ii]) * W;
6530 }
6531 }
6532 } // End of compute_norm(...)
6533
6534
6535 //=========================================================================
6536 /// Function to describe the local dofs of the element. The ostream
6537 /// specifies the output stream to which the description
6538 /// is written; the string stores the currently
6539 /// assembled output that is ultimately written to the
6540 /// output stream by Data::describe_dofs(...); it is typically
6541 /// built up incrementally as we descend through the
6542 /// call hierarchy of this function when called from
6543 /// Problem::describe_dofs(...)
6544 //=========================================================================
6546 std::ostream& out, const std::string& current_string) const
6547 {
6548 // Call the standard finite element description.
6551 }
6552
6553
6554 //=========================================================================
6555 /// Internal function that is used to assemble the jacobian of the mapping
6556 /// from local coordinates (s) to the lagrangian coordinates (xi), given the
6557 /// derivatives of the shape functions.
6558 //=========================================================================
6560 const DShape& dpsids, DenseMatrix<double>& jacobian) const
6561 {
6562 // Find the the dimension of the element
6563 const unsigned el_dim = dim();
6564 // Find the number of shape functions and shape functions types
6565 // We shall ASSUME (ENFORCE) that Lagrangian coordinates must
6566 // be interpolated through the nodes
6567 const unsigned n_shape = nnode();
6568 const unsigned n_shape_type = nnodal_lagrangian_type();
6569
6570#ifdef PARANOID
6571 // Check for dimensional compatibility
6573 {
6574 std::ostringstream error_message;
6575 error_message << "Dimension mismatch" << std::endl;
6576 error_message << "The elemental dimension: " << el_dim
6577 << " must equal the nodal Lagrangian dimension: "
6579 << " for the jacobian of the mapping to be well-defined"
6580 << std::endl;
6581 throw OomphLibError(
6582 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
6583 }
6584#endif
6585
6586 // Loop over the rows of the jacobian
6587 for (unsigned i = 0; i < el_dim; i++)
6588 {
6589 // Loop over the columns of the jacobian
6590 for (unsigned j = 0; j < el_dim; j++)
6591 {
6592 // Zero the entry
6593 jacobian(i, j) = 0.0;
6594 // Loop over the shape functions
6595 for (unsigned l = 0; l < n_shape; l++)
6596 {
6597 for (unsigned k = 0; k < n_shape_type; k++)
6598 {
6599 // Jacobian is dx_j/ds_i, which is represented by the sum
6600 // over the dpsi/ds_i of the nodal points X j
6601 // Call the Non-hanging version of positions
6602 // This is overloaded in refineable elements
6603 jacobian(i, j) +=
6605 }
6606 }
6607 }
6608 }
6609 }
6610
6611 //=========================================================================
6612 /// Internal function that is used to assemble the jacobian of second
6613 /// derivatives of the the mapping from local coordinates (s) to the
6614 /// lagrangian coordinates (xi), given the second derivatives of the
6615 /// shape functions.
6616 //=========================================================================
6619 {
6620 // Find the the dimension of the element
6621 const unsigned el_dim = dim();
6622 // Find the number of shape functions and shape functions types
6623 // We ENFORCE that Lagrangian coordinates must be interpolated through
6624 // the nodes
6625 const unsigned n_shape = nnode();
6626 const unsigned n_shape_type = nnodal_lagrangian_type();
6627 // Find the number of second derivatives
6628 const unsigned n_row = N2deriv[el_dim];
6629
6630 // Assemble the "jacobian" (d^2 x_j/ds_i^2) for second derivatives of
6631 // shape functions
6632 // Loop over the rows (number of second derivatives)
6633 for (unsigned i = 0; i < n_row; i++)
6634 {
6635 // Loop over the columns (element dimension
6636 for (unsigned j = 0; j < el_dim; j++)
6637 {
6638 // Zero the entry
6639 jacobian2(i, j) = 0.0;
6640 // Loop over the shape functions
6641 for (unsigned l = 0; l < n_shape; l++)
6642 {
6643 // Loop over the shape function types
6644 for (unsigned k = 0; k < n_shape_type; k++)
6645 {
6646 // Add the terms to the jacobian entry
6647 // Call the Non-hanging version of positions
6648 // This is overloaded in refineable elements
6649 jacobian2(i, j) +=
6651 }
6652 }
6653 }
6654 }
6655 }
6656
6657 //============================================================================
6658 /// Destructor for SolidFiniteElement:
6659 //============================================================================
6661 {
6662 // Delete the storage allocated for the positional local equations
6663 delete[] Position_local_eqn;
6664 }
6665
6666
6667 //==========================================================================
6668 /// Calculate the mapping from local to lagrangian coordinates
6669 /// assuming that the coordinates are aligned in the direction of the local
6670 /// coordinates, i.e. there are no cross terms and the jacobian is diagonal.
6671 /// The local derivatives are passed as dpsids and the jacobian and
6672 /// inverse jacobian are returned.
6673 //==========================================================================
6675 const DShape& dpsids,
6676 DenseMatrix<double>& jacobian,
6678 {
6679 // Find the dimension of the element
6680 const unsigned el_dim = dim();
6681 // Find the number of shape functions and shape functions types
6682 // We shall ASSUME (ENFORCE) that Lagrangian coordinates must
6683 // be interpolated through the nodes
6684 const unsigned n_shape = nnode();
6685 const unsigned n_shape_type = nnodal_lagrangian_type();
6686
6687 // In this case we assume that there are no cross terms, that is
6688 // global coordinate 0 is always in the direction of local coordinate 0
6689
6690 // Loop over the coordinates
6691 for (unsigned i = 0; i < el_dim; i++)
6692 {
6693 // Zero the jacobian and inverse jacobian entries
6694 for (unsigned j = 0; j < el_dim; j++)
6695 {
6696 jacobian(i, j) = 0.0;
6697 inverse_jacobian(i, j) = 0.0;
6698 }
6699
6700 // Loop over the shape functions
6701 for (unsigned l = 0; l < n_shape; l++)
6702 {
6703 // Loop over the types of dof
6704 for (unsigned k = 0; k < n_shape_type; k++)
6705 {
6706 // Derivatives are always dx_{i}/ds_{i}
6707 jacobian(i, i) +=
6709 }
6710 }
6711 }
6712
6713 // Now calculate the determinant of the matrix
6714 double det = 1.0;
6715 for (unsigned i = 0; i < el_dim; i++)
6716 {
6717 det *= jacobian(i, i);
6718 }
6719
6720// Report if Matrix is singular, or negative
6721#ifdef PARANOID
6723#endif
6724
6725 // Calculate the inverse mapping (trivial in this case)
6726 for (unsigned i = 0; i < el_dim; i++)
6727 {
6728 inverse_jacobian(i, i) = 1.0 / jacobian(i, i);
6729 }
6730
6731 // Return the value of the Jacobian
6732 return (det);
6733 }
6734
6735 //========================================================================
6736 /// Calculate shape functions and derivatives w.r.t. Lagrangian
6737 /// coordinates at local coordinate s. Returns the Jacobian of the mapping
6738 /// from Lagrangian to local coordinates.
6739 /// General case, may be overloaded
6740 //========================================================================
6742 Shape& psi,
6743 DShape& dpsi) const
6744 {
6745 // Find the element dimension
6746 const unsigned el_dim = dim();
6747
6748 // Get the values of the shape function and local derivatives
6749 // Temporarily stored in dpsi
6751
6752 // Allocate memory for the inverse jacobian
6754 // Now calculate the inverse jacobian
6756
6757 // Now set the values of the derivatives to be dpsidxi
6759 // Return the determinant of the jacobian
6760 return det;
6761 }
6762
6763 //=========================================================================
6764 /// Compute the geometric shape functions and also first
6765 /// derivatives w.r.t. Lagrangian coordinates at integration point ipt.
6766 /// Most general form of function, but may be over-loaded if desired
6767 //========================================================================
6769 Shape& psi,
6770 DShape& dpsi) const
6771 {
6772 // Find the element dimension
6773 const unsigned el_dim = dim();
6774
6775 // Shape function for the local derivatives
6776 // Again we ASSUME (insist) that the lagrangian coordinates
6777 // are interpolated through the nodes
6778 // Get the values of the shape function and local derivatives
6780
6781 // Allocate memory for the inverse jacobian
6783 // Now calculate the inverse jacobian
6785
6786 // Now set the values of the derivatives
6788 // Return the determinant of the jacobian
6789 return det;
6790 }
6791
6792 //========================================================================
6793 /// Compute the geometric shape functions and also first
6794 /// and second derivatives w.r.t. Lagrangian coordinates at
6795 /// local coordinate s;
6796 /// Returns Jacobian of mapping from Lagrangian to local coordinates.
6797 /// Numbering:
6798 /// \b 1D:
6799 /// d2pidxi(i,0) = \f$ d^2 \psi_j / d \xi^2 \f$
6800 /// \b 2D:
6801 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6802 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6803 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j / \partial \xi_0 \partial \xi_1 \f$
6804 /// \b 3D:
6805 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6806 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6807 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j / \partial \xi_2^2 \f$
6808 /// d2psidxi(i,3) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_1 \f$
6809 /// d2psidxi(i,4) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_2 \f$
6810 /// d2psidxi(i,5) = \f$ \partial^2 \psi_j/\partial \xi_1 \partial \xi_2 \f$
6811 //========================================================================
6813 Shape& psi,
6814 DShape& dpsi,
6815 DShape& d2psi) const
6816 {
6817 // Find the element dimension
6818 const unsigned el_dim = dim();
6819 // Find the number of second derivatives required
6820 const unsigned n_deriv = N2deriv[el_dim];
6821
6822 // Get the values of the shape function and local derivatives
6824
6825 // Allocate memory for the jacobian and inverse jacobian
6827 // Calculate the jacobian and inverse jacobian
6828 const double det =
6830
6831 // Allocate memory for the jacobian of second derivatives
6833 // Assemble the jacobian of second derivatives
6835
6836 // Now set the value of the derivatives
6838 jacobian, inverse_jacobian, jacobian2, dpsi, d2psi);
6839 // Return the determinant of the mapping
6840 return det;
6841 }
6842
6843 //==========================================================================
6844 /// Compute the geometric shape functions and also first
6845 /// and second derivatives w.r.t. Lagrangian coordinates at
6846 /// the ipt-th integration point
6847 /// Returns Jacobian of mapping from Lagrangian to local coordinates.
6848 /// Numbering:
6849 /// \b 1D:
6850 /// d2pidxi(i,0) = \f$ d^2 \psi_j / d \xi^2 \f$
6851 /// \b 2D:
6852 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6853 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6854 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_1 \f$
6855 /// \b 3D:
6856 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6857 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6858 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j / \partial \xi_2^2 \f$
6859 /// d2psidxi(i,3) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_1 \f$
6860 /// d2psidxi(i,4) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_2 \f$
6861 /// d2psidxi(i,5) = \f$ \partial^2 \psi_j/\partial \xi_1 \partial \xi_2 \f$
6862 //========================================================================
6864 Shape& psi,
6865 DShape& dpsi,
6866 DShape& d2psi) const
6867 {
6868 // Find the values of the indices of the shape functions
6869 // Find the element dimension
6870 const unsigned el_dim = dim();
6871 // Find the number of second derivatives required
6872 const unsigned n_deriv = N2deriv[el_dim];
6873
6874 // Get the values of the shape function and local derivatives
6876
6877 // Allocate memory for the jacobian and inverse jacobian
6879 // Calculate the jacobian and inverse jacobian
6880 const double det =
6882
6883 // Allocate memory for the jacobian of second derivatives
6885 // Assemble the jacobian of second derivatives
6887
6888 // Now set the value of the derivatives
6890 jacobian, inverse_jacobian, jacobian2, dpsi, d2psi);
6891 // Return the determinant of the mapping
6892 return det;
6893 }
6894
6895 //============================================================================
6896 /// Function to describe the local dofs of the element. The ostream
6897 /// specifies the output stream to which the description
6898 /// is written; the string stores the currently
6899 /// assembled output that is ultimately written to the
6900 /// output stream by Data::describe_dofs(...); it is typically
6901 /// built up incrementally as we descend through the
6902 /// call hierarchy of this function when called from
6903 /// Problem::describe_dofs(...)
6904 //============================================================================
6906 std::ostream& out, const std::string& current_string) const
6907 {
6908 // Find the number of nodes
6909 const unsigned n_node = nnode();
6910 // Loop over the nodes
6911 for (unsigned n = 0; n < n_node; n++)
6912 {
6913 // Cast to a solid node
6914 SolidNode* cast_node_pt = static_cast<SolidNode*>(node_pt(n));
6915 std::stringstream conversion;
6916 conversion << " of Solid Node " << n << current_string;
6917 std::string in(conversion.str());
6919 }
6920 }
6921
6922 //============================================================================
6923 /// Assign local equation numbers for the solid equations in the element.
6924 // This can be done at a high level assuming, as I am, that the equations
6925 // will always be formulated in terms of nodal positions.
6926 /// If the boolean argument is true then pointers to the dofs will be
6927 /// stored in Dof_pt.
6928 //============================================================================
6930 const bool& store_local_dof_pt)
6931 {
6932 // Find the number of nodes
6933 const unsigned n_node = nnode();
6934
6935 // Check there are nodes!
6936 if (n_node > 0)
6937 {
6938 // Find the number of position types and dimensions of the nodes
6939 // Local caching
6940 const unsigned n_position_type = nnodal_position_type();
6941 const unsigned nodal_dim = nodal_dimension();
6942
6943 // Delete the existing storage
6944 delete[] Position_local_eqn;
6945 // Resize the storage for the positional equation numbers
6947
6948 // A local queue to store the global equation numbers
6949 std::deque<unsigned long> global_eqn_number_queue;
6950
6951 // Get the number of dofs so far, this must be outside both loops
6952 // so that both can use it
6953 unsigned local_eqn_number = ndof();
6954
6955 // Loop over the nodes
6956 for (unsigned n = 0; n < n_node; n++)
6957 {
6958 // Cast to a solid node
6959 SolidNode* cast_node_pt = static_cast<SolidNode*>(node_pt(n));
6960
6961 // Loop over the number of position dofs
6962 for (unsigned j = 0; j < n_position_type; j++)
6963 {
6964 // Loop over the dimension of each node
6965 for (unsigned k = 0; k < nodal_dim; k++)
6966 {
6967 // Get equation number
6968 // Note eqn_number is long !
6969 long eqn_number = cast_node_pt->position_eqn_number(j, k);
6970 // If equation_number positive add to array
6971 if (eqn_number >= 0)
6972 {
6973 // Add to global array
6975 // Add pointer to the dof to the queue if required
6977 {
6979 &(cast_node_pt->x_gen(j, k)));
6980 }
6981
6982 // Add to look-up scheme
6985 // Increment the local equation number
6987 }
6988 else
6989 {
6992 }
6993 }
6994 }
6995 } // End of loop over nodes
6996
6997 // Now add our global equations numbers to the internal element storage
7000 // Clear the memory used in the deque
7002 {
7003 std::deque<double*>().swap(GeneralisedElement::Dof_pt_deque);
7004 }
7005
7006 } // End of the case when there are nodes
7007 }
7008
7009
7010 //============================================================================
7011 /// This function calculates the entries of Jacobian matrix, used in
7012 /// the Newton method, associated with the elastic problem in which the
7013 /// nodal position is a variable. It does this using finite differences,
7014 /// rather than an analytical formulation, so can be done in total generality.
7015 //==========================================================================
7018 {
7019 // Flag to indicate if we use first or second order FD
7020 // bool use_first_order_fd=false;
7021
7022 // Find the number of nodes
7023 const unsigned n_node = nnode();
7024
7025 // If there aren't any nodes, then return straight away
7026 if (n_node == 0)
7027 {
7028 return;
7029 }
7030
7031 // Call the update function to ensure that the element is in
7032 // a consistent state before finite differencing starts
7034
7035 // Get the number of position dofs and dimensions at the node
7036 const unsigned n_position_type = nnodal_position_type();
7037 const unsigned nodal_dim = nodal_dimension();
7038
7039 // Find the number of dofs in the element
7040 const unsigned n_dof = this->ndof();
7041
7042 // Create newres vectors
7044 // Vector<double> newres_minus(n_dof);
7045
7046 // Integer storage for local unknown
7047 int local_unknown = 0;
7048
7049 // Should probably give this a more global scope
7050 const double fd_step = Default_fd_jacobian_step;
7051
7052 // Loop over the nodes
7053 for (unsigned n = 0; n < n_node; n++)
7054 {
7055 // Loop over position dofs
7056 for (unsigned k = 0; k < n_position_type; k++)
7057 {
7058 // Loop over dimension
7059 for (unsigned i = 0; i < nodal_dim; i++)
7060 {
7061 // If the variable is free
7063 if (local_unknown >= 0)
7064 {
7065 // Store a pointer to the (generalised) Eulerian nodal position
7066 double* const value_pt = &(node_pt(n)->x_gen(k, i));
7067
7068 // Save the old value of the (generalised) Eulerian nodal position
7069 const double old_var = *value_pt;
7070
7071 // Increment the (generalised) Eulerian nodal position
7072 *value_pt += fd_step;
7073
7074 // Perform any auxialiary node updates
7076
7077 // Update any other dependent variables
7079
7080 // Calculate the new residuals
7082
7083 // if (use_first_order_fd)
7084 {
7085 // Do forward finite differences
7086 for (unsigned m = 0; m < n_dof; m++)
7087 {
7088 // Stick the entry into the Jacobian matrix
7089 jacobian(m, local_unknown) =
7090 (newres[m] - residuals[m]) / fd_step;
7091 }
7092 }
7093 // else
7094 // {
7095 // //Take backwards step for the (generalised) Eulerian
7096 // nodal
7097 // // position
7098 // node_pt(n)->x_gen(k,i) = old_var-fd_step;
7099
7100 // //Calculate the new residuals at backward position
7101 // get_residuals(newres_minus);
7102
7103 // //Do central finite differences
7104 // for(unsigned m=0;m<n_dof;m++)
7105 // {
7106 // //Stick the entry into the Jacobian matrix
7107 // jacobian(m,local_unknown) =
7108 // (newres[m] - newres_minus[m])/(2.0*fd_step);
7109 // }
7110 // }
7111
7112 // Reset the (generalised) Eulerian nodal position
7113 *value_pt = old_var;
7114
7115
7116 // Perform any auxialiary node updates
7118
7119 // Reset any other dependent variables
7121 }
7122 }
7123 }
7124 }
7125
7126 // End of finite difference loop
7127 // Final reset of any dependent data
7129 }
7130
7131 //============================================================================
7132 /// Return i-th FE-interpolated Lagrangian coordinate at
7133 /// local coordinate s.
7134 //============================================================================
7136 const unsigned& i) const
7137 {
7138 // Find the number of nodes
7139 const unsigned n_node = nnode();
7140
7141 // Find the number of lagrangian types from the first node
7142 const unsigned n_lagrangian_type = nnodal_lagrangian_type();
7143
7144 // Assign storage for the local shape function
7146
7147 // Find the values of shape function
7148 shape(s, psi);
7149
7150 // Initialise value of xi
7151 double interpolated_xi = 0.0;
7152
7153 // Loop over the local nodes
7154 for (unsigned l = 0; l < n_node; l++)
7155 {
7156 // Loop over the number of dofs
7157 for (unsigned k = 0; k < n_lagrangian_type; k++)
7158 {
7160 }
7161 }
7162
7163 return (interpolated_xi);
7164 }
7165
7166 //============================================================================
7167 /// Compute FE-interpolated Lagrangian coordinate vector xi[] at
7168 /// local coordinate s.
7169 //============================================================================
7171 Vector<double>& xi) const
7172 {
7173 // Find the number of nodes
7174 const unsigned n_node = nnode();
7175
7176 // Find the number of lagrangian types from the first node
7177 const unsigned n_lagrangian_type = nnodal_lagrangian_type();
7178
7179 // Assign storage for the local shape function
7181
7182 // Find the values of shape function
7183 shape(s, psi);
7184
7185 // Read out the number of lagrangian coordinates from the node
7186 const unsigned n_lagrangian = lagrangian_dimension();
7187
7188 // Loop over the number of lagrangian coordinates
7189 for (unsigned i = 0; i < n_lagrangian; i++)
7190 {
7191 // Initialise component to zero
7192 xi[i] = 0.0;
7193
7194 // Loop over the local nodes
7195 for (unsigned l = 0; l < n_node; l++)
7196 {
7197 // Loop over the number of dofs
7198 for (unsigned k = 0; k < n_lagrangian_type; k++)
7199 {
7200 xi[i] += lagrangian_position_gen(l, k, i) * psi(l, k);
7201 }
7202 }
7203 }
7204 }
7205
7206
7207 //============================================================================
7208 /// Compute derivatives of FE-interpolated Lagrangian coordinates xi
7209 /// with respect to local coordinates: dxids[i][j]=dxi_i/ds_j
7210 //============================================================================
7213 {
7214 // Find the number of nodes
7215 const unsigned n_node = nnode();
7216
7217 // Find the number of lagrangian types from the first node
7218 const unsigned n_lagrangian_type = nnodal_lagrangian_type();
7219
7220 // Dimension of the element = number of local coordinates
7221 unsigned el_dim = dim();
7222
7223 // Assign storage for the local shape function
7226
7227 // Find the values of shape function and its derivs w.r.t. to local coords
7229
7230 // Read out the number of lagrangian coordinates from the node
7231 const unsigned n_lagrangian = lagrangian_dimension();
7232
7233 // Loop over the number of lagrangian and local coordinates
7234 for (unsigned i = 0; i < n_lagrangian; i++)
7235 {
7236 for (unsigned j = 0; j < el_dim; j++)
7237 {
7238 // Initialise component to zero
7239 dxids(i, j) = 0.0;
7240
7241 // Loop over the local nodes
7242 for (unsigned l = 0; l < n_node; l++)
7243 {
7244 // Loop over the number of dofs
7245 for (unsigned k = 0; k < n_lagrangian_type; k++)
7246 {
7247 dxids(i, j) += lagrangian_position_gen(l, k, i) * dpsi(l, k, j);
7248 }
7249 }
7250 }
7251 }
7252 }
7253
7254 //=======================================================================
7255 /// Add jacobian and residuals for consistent assignment of
7256 /// initial "accelerations" in Newmark scheme. Jacobian is the mass matrix.
7257 //=======================================================================
7259 DenseMatrix<double>& jacobian)
7260 {
7261#ifdef PARANOID
7262 // Check that we're computing the real residuals, not the
7263 // ones corresponding to the assignement of a prescribed displacement
7265 {
7266 std::ostringstream error_stream;
7267 error_stream << "Can only call fill_in_jacobian_for_newmark_accel()\n"
7268 << "With Solve_for_consistent_newmark_accel_flag:"
7270 error_stream << "Solid_ic_pt: " << Solid_ic_pt << std::endl;
7271
7272 throw OomphLibError(
7274 }
7275#endif
7276
7277 // Find the number of nodes
7278 const unsigned n_node = nnode();
7279 const unsigned n_position_type = nnodal_position_type();
7280 const unsigned nodal_dim = nodal_dimension();
7281
7282 // Set the number of Lagrangian coordinates
7283 const unsigned n_lagrangian = dim();
7284
7285 // Integer storage for local equation and unknown
7286 int local_eqn = 0, local_unknown = 0;
7287
7288 // Set up memory for the shape functions:
7289 // # of nodes, # of positional dofs
7291
7292 // # of nodes, # of positional dofs, # of lagrangian coords (for deriv)
7293 // Not needed but they come for free when compute the Jacobian.
7295
7296 // Set # of integration points
7297 const unsigned n_intpt = integral_pt()->nweight();
7298
7299 // Set the Vector to hold local coordinates
7301
7302 // Get positional timestepper from first nodal point
7304
7305#ifdef PARANOID
7306 // Of course all this only works if we're actually using a
7307 // Newmark timestepper!
7308 if (timestepper_pt->type() != "Newmark")
7309 {
7310 std::ostringstream error_stream;
7312 << "Assignment of Newmark accelerations obviously only works\n"
7313 << "for Newmark timesteppers. You've called me with "
7314 << timestepper_pt->type() << std::endl;
7315
7316 throw OomphLibError(
7318 }
7319#endif
7320
7321 // "Acceleration" is the last history value:
7322 const unsigned ntstorage = timestepper_pt->ntstorage();
7323 const double accel_weight = timestepper_pt->weight(2, ntstorage - 1);
7324
7325 // Loop over the integration points
7326 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
7327 {
7328 // Assign values of s
7329 for (unsigned i = 0; i < n_lagrangian; i++)
7330 {
7331 s[i] = integral_pt()->knot(ipt, i);
7332 }
7333
7334 // Get the integral weight
7335 double w = integral_pt()->weight(ipt);
7336
7337 // Jacobian of mapping between local and Lagrangian coords and shape
7338 // functions
7339 double J = dshape_lagrangian(s, psi, dpsidxi);
7340
7341 // Get Lagrangian coordinate
7343 interpolated_xi(s, xi);
7344
7345 // Get multiplier for inertia terms
7346 double factor = multiplier(xi);
7347
7348 // Premultiply the weights and the Jacobian
7349 double W = w * J;
7350
7351 // Loop over the nodes
7352 for (unsigned l0 = 0; l0 < n_node; l0++)
7353 {
7354 // Loop over the positional dofs: 'Type': 0: displacement; 1: deriv
7355 for (unsigned k0 = 0; k0 < n_position_type; k0++)
7356 {
7357 // Loop over Eulerian directions
7358 for (unsigned i0 = 0; i0 < nodal_dim; i0++)
7359 {
7361 // If it's a degree of freedom, add contribution
7362 if (local_eqn >= 0)
7363 {
7364 // Loop over the nodes
7365 for (unsigned l1 = 0; l1 < n_node; l1++)
7366 {
7367 // Loop over the positional dofs: 'Type': 0: displacement;
7368 // 1: deriv
7369 for (unsigned k1 = 0; k1 < n_position_type; k1++)
7370 {
7371 // Loop over Eulerian directions
7372 unsigned i1 = i0;
7373 {
7375 // If it's a degree of freedom, add contribution
7376 if (local_unknown >= 0)
7377 {
7378 // Add contribution: Mass matrix, multiplied by
7379 // weight for "acceleration" in Newmark scheme
7380 // and general multiplier
7381 jacobian(local_eqn, local_unknown) +=
7382 factor * accel_weight * psi(l0, k0) * psi(l1, k1) * W;
7383 }
7384 }
7385 }
7386 }
7387 }
7388 }
7389 }
7390 }
7391
7392 } // End of loop over the integration points
7393 }
7394
7395
7396 //=======================================================================
7397 /// Helper function to fill in the residuals and (if flag==1) the Jacobian
7398 /// for the setup of an initial condition. The global equations are:
7399 /// \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]
7400 /// where \f$ N \f$ is the number of nodes in the mesh and \f$ K \f$
7401 /// the number of generalised nodal coordinates. The initial shape
7402 /// of the solid body, \f$ {\bf R}^{(IC)},\f$ and its time-derivatives
7403 /// are specified via the \c GeomObject that is stored in the
7404 /// \c SolidFiniteElement::SolidInitialCondition object. The latter also
7405 /// stores the order of the time-derivative \f$ D \f$ to be assigned.
7406 //=======================================================================
7409 DenseMatrix<double>& jacobian,
7410 const unsigned& flag)
7411 {
7412 // Find the number of nodes and position types
7413 const unsigned n_node = nnode();
7414 const unsigned n_position_type = nnodal_position_type();
7415
7416 // Set the dimension of the global coordinates
7417 const unsigned nodal_dim = nodal_dimension();
7418
7419 // Find the number of lagragian types from the first node
7420 const unsigned n_lagrangian_type = nnodal_lagrangian_type();
7421
7422 // Set the number of lagrangian coordinates
7423 const unsigned n_lagrangian = dim();
7424
7425 // Integer storage for local equation number
7426 int local_eqn = 0;
7427 int local_unknown = 0;
7428
7429 // # of nodes, # of positional dofs
7431
7432 // # of nodes, # of positional dofs, # of lagrangian coords (for deriv)
7433 // not needed but they come for free when we compute the Jacobian
7434 // DShape dpsidxi(n_node,n_position_type,n_lagrangian);
7435
7436 // Set # of integration points
7437 const unsigned n_intpt = integral_pt()->nweight();
7438
7439 // Set the Vector to hold local coordinates
7441
7442 // Loop over the integration points
7443 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
7444 {
7445 // Assign values of s
7446 for (unsigned i = 0; i < n_lagrangian; i++)
7447 {
7448 s[i] = integral_pt()->knot(ipt, i);
7449 }
7450
7451 // Get the integral weight
7452 double w = integral_pt()->weight(ipt);
7453
7454 // Shape fcts
7455 shape(s, psi);
7456
7457 // Get Lagrangian coordinate
7459
7460 // Loop over the number of lagrangian coordinates
7461 for (unsigned i = 0; i < n_lagrangian; i++)
7462 {
7463 // Loop over the local nodes
7464 for (unsigned l = 0; l < n_node; l++)
7465 {
7466 // Loop over the number of dofs
7467 for (unsigned k = 0; k < n_lagrangian_type; k++)
7468 {
7469 xi[i] += lagrangian_position_gen(l, k, i) * psi(l, k);
7470 }
7471 }
7472 }
7473
7474 // Get initial condition
7478
7479 // Weak form of assignment of initial guess
7480
7481 // Loop over the number of node
7482 for (unsigned l = 0; l < n_node; l++)
7483 {
7484 // Loop over the type of degree of freedom
7485 for (unsigned k = 0; k < n_position_type; k++)
7486 {
7487 // Loop over the coordinate directions
7488 for (unsigned i = 0; i < nodal_dim; i++)
7489 {
7491
7492 // If it's not a boundary condition
7493 if (local_eqn >= 0)
7494 {
7495 // Note we're ignoring the mapping between local and
7496 // global Lagrangian coords -- doesn't matter here;
7497 // we're just enforcing a slightly different
7498 // weak form.
7500 (interpolated_x(s, i) - drdt_ic[i]) * psi(l, k) * w;
7501
7502
7503 // Do Jacobian too?
7504 if (flag == 1)
7505 {
7506 // Loop over the number of node
7507 for (unsigned ll = 0; ll < n_node; ll++)
7508 {
7509 // Loop over the type of degree of freedom
7510 for (unsigned kk = 0; kk < n_position_type; kk++)
7511 {
7512 // Only diagonal term
7513 unsigned ii = i;
7514
7516
7517 // If it's not a boundary condition
7518 if (local_unknown >= 0)
7519 {
7520 // Note we're ignoring the mapping between local and
7521 // global Lagrangian coords -- doesn't matter here;
7522 // we're just enforcing a slightly different
7523 // weak form.
7524 jacobian(local_eqn, local_unknown) +=
7525 psi(ll, kk) * psi(l, k) * w;
7526 }
7527 else
7528 {
7530 << "WARNING: You should really free all Data"
7531 << std::endl
7532 << " before setup of initial guess" << std::endl
7533 << "ll, kk, ii " << ll << " " << kk << " " << ii
7534 << std::endl;
7535 }
7536 }
7537 }
7538 }
7539 }
7540 else
7541 {
7542 oomph_info << "WARNING: You should really free all Data"
7543 << std::endl
7544 << " before setup of initial guess"
7545 << std::endl
7546 << "l, k, i " << l << " " << k << " " << i
7547 << std::endl;
7548 }
7549 }
7550 }
7551 }
7552
7553 } // End of loop over the integration points
7554 }
7555
7556
7557 //===============================================================
7558 /// Return the geometric shape function at the local coordinate s
7559 //===============================================================
7561 {
7562 // Single shape function always has value 1
7563 psi[0] = 1.0;
7564 }
7565
7566 //=======================================================================
7567 /// Assign the static Default_integration_scheme
7568 //=======================================================================
7570
7571
7572} // 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
A Class for the derivatives of shape functions The class design is essentially the same as Shape,...
Definition shape.h:278
A class that represents a collection of data; each Data object may contain many different individual ...
Definition nodes.h:86
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
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
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
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
Class of matrices containing doubles, and stored as a DenseMatrix<double>, but with solving functiona...
Definition matrices.h:1271
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 ...
FaceElements are elements that coincide with the faces of higher-dimensional "bulk" elements....
Definition elements.h:4342
int & normal_sign()
Sign of outer unit normal (relative to cross-products of tangent vectors in the corresponding "bulk" ...
Definition elements.h:4616
int & face_index()
Index of the face (a number that uniquely identifies the face in the element)
Definition elements.h:4630
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:5543
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
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 outer_unit_normal(const Vector< double > &s, Vector< double > &unit_normal) const
Compute outer unit normal at the specified local coordinate.
Definition elements.cc:6037
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:6384
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
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
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
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
FiniteElement *& bulk_element_pt()
Pointer to higher-dimensional "bulk" element.
Definition elements.h:4739
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:5273
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:5359
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:5441
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
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:6440
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:6415
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
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
void output_zeta(std::ostream &outfile, const unsigned &nplot)
Output boundary coordinate zeta.
Definition elements.cc:5231
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
Integral *const & integral_pt() const
Return the pointer to the integration scheme (const version)
Definition elements.h:1967
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
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
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
MacroElement * macro_elem_pt()
Access function to pointer to macro element.
Definition elements.h:1882
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
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition elements.h:2179
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
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:5163
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 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:5127
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:816
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:1150
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:1185
static bool Suppress_warning_about_repeated_external_data
Static boolean to suppress warnings about repeated external data. Defaults to true.
Definition elements.h:687
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
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
Data *& external_data_pt(const unsigned &i)
Return a pointer to i-th external data object.
Definition elements.h:642
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:744
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:822
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:691
int ** Data_local_eqn
Pointer to array storage for local equation numbers associated with internal and external data....
Definition elements.h:101
Data *& internal_data_pt(const unsigned &i)
Return a pointer to i-th internal data object.
Definition elements.h:605
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:713
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
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:967
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:679
virtual ~GeneralisedElement()
Virtual destructor to clean up any memory allocated by the object.
Definition elements.cc:281
unsigned ninternal_data() const
Return the number of internal data objects.
Definition elements.h:810
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 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
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 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 set_halo(const unsigned &non_halo_proc_ID)
Label the element as halo and specify processor that holds non-halo counterpart.
Definition elements.h:1138
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:683
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
A geometric object is an object that provides a parametrised description of its shape via the functio...
unsigned ndim() const
Access function to # of Eulerian coordinates.
virtual void dposition_dt(const Vector< double > &zeta, const unsigned &j, Vector< double > &drdt)
j-th time-derivative on object at current time: .
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.
A class to specify when the error is caused by an inverted element.
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
TimeStepper *& position_time_stepper_pt()
Return a pointer to the position timestepper.
Definition nodes.h:1022
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
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition nodes.h:1060
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
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
An OomphLibError object which should be thrown when an run-time error is encountered....
===================================================================== A class for handling oomph-lib ...
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:7560
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
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:6905
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:6617
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:6929
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:6741
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:6863
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:7407
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:6674
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:6559
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:7135
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:7211
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:6768
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:7016
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:6812
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:6473
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:6545
virtual ~SolidFiniteElement()
Destructor to clean up any allocated memory.
Definition elements.cc:6660
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:7258
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
unsigned & ic_time_deriv()
Which time derivative are we currently assigning?
Definition elements.h:3521
GeomObject *& geom_object_pt()
(Reference to) pointer to geom object that specifies the initial condition
Definition elements.h:3515
A Class for nodes that deform elastically (i.e. position is an unknown in the problem)....
Definition nodes.h:1686
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
unsigned required_nvalue(const unsigned &n) const
Access function for Nvalue: # of ‘values’ (pinned or dofs) at node n (always returns the same value a...
Base class for time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
A slight extension to the standard template vector class so that we can include "graceful" array rang...
Definition Vector.h:58
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
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...