elements.cc
Go to the documentation of this file.
1// LIC// ====================================================================
2// LIC// This file forms part of oomph-lib, the object-oriented,
3// LIC// multi-physics finite-element library, available
4// LIC// at http://www.oomph-lib.org.
5// LIC//
6// LIC// Copyright (C) 2006-2024 Matthias Heil and Andrew Hazel
7// LIC//
8// LIC// This library is free software; you can redistribute it and/or
9// LIC// modify it under the terms of the GNU Lesser General Public
10// LIC// License as published by the Free Software Foundation; either
11// LIC// version 2.1 of the License, or (at your option) any later version.
12// LIC//
13// LIC// This library is distributed in the hope that it will be useful,
14// LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// LIC// Lesser General Public License for more details.
17// LIC//
18// LIC// You should have received a copy of the GNU Lesser General Public
19// LIC// License along with this library; if not, write to the Free Software
20// LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21// LIC// 02110-1301 USA.
22// LIC//
23// LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24// LIC//
25// LIC//====================================================================
26// Non-inline member functions for generic elements
27
28#include <float.h>
29
30// oomph-lib includes
31#include "elements.h"
32#include "timesteppers.h"
33#include "integral.h"
34#include "shape.h"
35#include "oomph_definitions.h"
37
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 //==========================================================================
281 GeneralisedElement::~GeneralisedElement()
282 {
283 // Delete each of the objects stored as internal data
284 for (unsigned i = Ninternal_data; i > 0; i--)
285 {
286 // The objects are stored at the beginning of the Data_pt array
287 delete Data_pt[i - 1];
288 Data_pt[i - 1] = 0;
289 }
290
291 // Now delete the storage for internal and external data
292 delete[] Data_pt;
293
294 // Now if we have allocated storage for the local equation for
295 // the internal and external data, delete it.
296 if (Data_local_eqn)
297 {
298 delete[] Data_local_eqn[0];
299 delete[] Data_local_eqn;
300 }
301
302 // Delete the storage for the global equation numbers
303 delete[] Eqn_number;
304 }
305
306
307 //=======================================================================
308 /// Add a (pointer to an) external data object to the element and
309 /// return the index required to obtain it from the access
310 /// function \c external_data_pt()
311 //=======================================================================
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 (a) the pointer to a Data object
5118 /// and (b) the index of the value in that Data object.
5119 /// The generic implementation (should be overloaded in more specific
5120 /// applications) is to include all nodal and internal Data stored in
5121 /// the FiniteElement. Note that the geometric data,
5122 /// which includes the positions
5123 /// of SolidNodes, is treated separately by the function
5124 /// \c identify_geometric_data()
5125 //======================================================================
5127 std::set<std::pair<Data*, unsigned>>& paired_field_data)
5128 {
5129 // Loop over all internal data
5130 const unsigned n_internal = this->ninternal_data();
5131 for (unsigned n = 0; n < n_internal; n++)
5132 {
5133 // Cache the data pointer
5134 Data* const dat_pt = this->internal_data_pt(n);
5135 // Find the number of data values stored in the data object
5136 const unsigned n_value = dat_pt->nvalue();
5137 // Add the index of each data value and the pointer to the set
5138 // of pairs
5139 for (unsigned i = 0; i < n_value; i++)
5140 {
5141 paired_field_data.insert(std::make_pair(dat_pt, i));
5142 }
5143 }
5144
5145 // Loop over all the nodes
5146 const unsigned n_node = this->nnode();
5147 for (unsigned n = 0; n < n_node; n++)
5148 {
5149 // Cache the node pointer
5150 Node* const nod_pt = this->node_pt(n);
5151 // Find the number of values stored at the node
5152 const unsigned n_value = nod_pt->nvalue();
5153 // Add the index of each data value and the pointer to the set
5154 // of pairs
5155 for (unsigned i = 0; i < n_value; i++)
5156 {
5157 paired_field_data.insert(std::make_pair(nod_pt, i));
5158 }
5159 }
5160 }
5161
5162 void FiniteElement::build_face_element(const int& face_index,
5163 FaceElement* face_element_pt)
5164 {
5165 // Set the nodal dimension
5166 face_element_pt->set_nodal_dimension(nodal_dimension());
5167
5168 // Set the pointer to the orginal "bulk" element
5169 face_element_pt->bulk_element_pt() = this;
5170
5171#ifdef OOMPH_HAS_MPI
5172 // Pass on non-halo proc ID
5173 face_element_pt->set_halo(Non_halo_proc_ID);
5174#endif
5175
5176 // Set the face index
5177 face_element_pt->face_index() = face_index;
5178
5179 // Get number of bulk nodes on a face of this element
5180 const unsigned nnode_face = nnode_on_face();
5181
5182 // Set the function pointer for face coordinate to bulk coordinate
5183 // mapping
5184 face_element_pt->face_to_bulk_coordinate_fct_pt() =
5186
5187 // Set the function pointer for the derivative of the face coordinate to
5188 // bulk coordinate mapping
5189 face_element_pt->bulk_coordinate_derivatives_fct_pt() =
5191
5192 // Resize storage for the number of values originally stored each of the
5193 // face element's nodes.
5194 face_element_pt->nbulk_value_resize(nnode_face);
5195
5196 // Resize storage for the bulk node numbers corresponding to the face
5197 // element's nodes.
5198 face_element_pt->bulk_node_number_resize(nnode_face);
5199
5200 // Copy bulk_node_numbers and nbulk_values
5201 for (unsigned i = 0; i < nnode_face; i++)
5202 {
5203 // Find the corresponding bulk node's number
5204 unsigned bulk_number = get_bulk_node_number(face_index, i);
5205
5206 // Assign the pointer and number into the face element
5207 face_element_pt->node_pt(i) = node_pt(bulk_number);
5208 face_element_pt->bulk_node_number(i) = bulk_number;
5209
5210 // Set the number of values originally stored at this node
5211 face_element_pt->nbulk_value(i) = required_nvalue(bulk_number);
5212 }
5213
5214 // Set the outer unit normal sign
5215 face_element_pt->normal_sign() = face_outer_unit_normal_sign(face_index);
5216 }
5217
5218 /// ///////////////////////////////////////////////////////////////////////
5219 /// ///////////////////////////////////////////////////////////////////////
5220 /// ///////////////////////////////////////////////////////////////////////
5221
5222 // Initialise the static variable.
5223 // Do not ignore warning for discontinuous tangent vectors.
5225
5226
5227 //========================================================================
5228 /// Output boundary coordinate zeta
5229 //========================================================================
5230 void FaceElement::output_zeta(std::ostream& outfile, const unsigned& nplot)
5231 {
5232 // Vector of local coordinates
5233 unsigned n_dim = dim();
5235
5236 // Tecplot header info
5238
5239 // Loop over plot points
5241 for (unsigned iplot = 0; iplot < num_plot_points; iplot++)
5242 {
5243 // Get local coordinates of plot point
5245
5246 // Spatial coordinates are one higher
5247 for (unsigned i = 0; i < n_dim + 1; i++)
5248 {
5249 outfile << interpolated_x(s, i) << " ";
5250 }
5251
5252 // Boundary coordinate
5255 for (unsigned i = 0; i < n_dim; i++)
5256 {
5257 outfile << zeta[i] << " ";
5258 }
5259 outfile << std::endl;
5260 }
5261
5262 // Write tecplot footer (e.g. FE connectivity lists)
5264 }
5265
5266
5267 //========================================================================
5268 /// Calculate the determinant of the
5269 /// Jacobian of the mapping between local and global
5270 /// coordinates at the position s. Overloaded from FiniteElement.
5271 //========================================================================
5273 {
5274 // Find out the sptial dimension of the element
5275 unsigned n_dim_el = this->dim();
5276
5277 // Bail out if we're in a point element -- not sure what
5278 // J_eulerian actually is, but this is harmless
5279 if (n_dim_el == 0) return 1.0;
5280
5281 // Find out how many nodes there are
5282 unsigned n_node = nnode();
5283
5284 // Find out how many positional dofs there are
5285 unsigned n_position_type = this->nnodal_position_type();
5286
5287 // Find out the dimension of the node
5288 unsigned n_dim = this->nodal_dimension();
5289
5290 // Set up memory for the shape functions
5293
5294 // Only need to call the local derivatives
5296
5297 // Also calculate the surface Vectors (derivatives wrt local coordinates)
5299
5300 // Calculate positions and derivatives
5301 for (unsigned l = 0; l < n_node; l++)
5302 {
5303 // Loop over positional dofs
5304 for (unsigned k = 0; k < n_position_type; k++)
5305 {
5306 // Loop over coordinates
5307 for (unsigned i = 0; i < n_dim; i++)
5308 {
5309 // Loop over LOCAL derivative directions, to calculate the tangent(s)
5310 for (unsigned j = 0; j < n_dim_el; j++)
5311 {
5312 interpolated_A(j, i) +=
5314 }
5315 }
5316 }
5317 }
5318 // Now find the local deformed metric tensor from the tangent Vectors
5320 for (unsigned i = 0; i < n_dim_el; i++)
5321 {
5322 for (unsigned j = 0; j < n_dim_el; j++)
5323 {
5324 // Take the dot product
5325 for (unsigned k = 0; k < n_dim; k++)
5326 {
5327 A(i, j) += interpolated_A(i, k) * interpolated_A(j, k);
5328 }
5329 }
5330 }
5331
5332 // Find the determinant of the metric tensor
5333 double Adet = 0.0;
5334 switch (n_dim_el)
5335 {
5336 case 1:
5337 Adet = A(0, 0);
5338 break;
5339 case 2:
5340 Adet = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
5341 break;
5342 default:
5343 throw OomphLibError("Wrong dimension in FaceElement",
5344 "FaceElement::J_eulerian()",
5346 }
5347
5348 // Return
5349 return sqrt(Adet);
5350 }
5351
5352
5353 //========================================================================
5354 /// Compute the Jacobian of the mapping between the local and global
5355 /// coordinates at the ipt-th integration point. Overloaded from
5356 /// FiniteElement.
5357 //========================================================================
5358 double FaceElement::J_eulerian_at_knot(const unsigned& ipt) const
5359 {
5360 // Find the number of nodes
5361 const unsigned n_node = nnode();
5362
5363 // Find the number of position types
5364 const unsigned n_position_type = nnodal_position_type();
5365
5366 // Find the dimension of the node and element
5367 const unsigned n_dim = nodal_dimension();
5368 const unsigned n_dim_el = dim();
5369
5370 // Set up dummy memory for the shape functions
5373
5374 // Get the shape functions and local derivatives at the knot
5375 // This call may use the stored versions, which is why this entire
5376 // function doesn't just call J_eulerian(s), after reading out s from
5377 // the knots.
5379
5380 // Also calculate the surface Vectors (derivatives wrt local coordinates)
5382
5383 // Calculate positions and derivatives
5384 for (unsigned l = 0; l < n_node; l++)
5385 {
5386 // Loop over positional dofs
5387 for (unsigned k = 0; k < n_position_type; k++)
5388 {
5389 // Loop over coordinates
5390 for (unsigned i = 0; i < n_dim; i++)
5391 {
5392 // Loop over LOCAL derivative directions, to calculate the tangent(s)
5393 for (unsigned j = 0; j < n_dim_el; j++)
5394 {
5395 interpolated_A(j, i) +=
5397 }
5398 }
5399 }
5400 }
5401
5402 // Now find the local deformed metric tensor from the tangent Vectors
5404 for (unsigned i = 0; i < n_dim_el; i++)
5405 {
5406 for (unsigned j = 0; j < n_dim_el; j++)
5407 {
5408 // Take the dot product
5409 for (unsigned k = 0; k < n_dim; k++)
5410 {
5411 A(i, j) += interpolated_A(i, k) * interpolated_A(j, k);
5412 }
5413 }
5414 }
5415
5416 // Find the determinant of the metric tensor
5417 double Adet = 0.0;
5418 switch (n_dim_el)
5419 {
5420 case 1:
5421 Adet = A(0, 0);
5422 break;
5423 case 2:
5424 Adet = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
5425 break;
5426 default:
5427 throw OomphLibError("Wrong dimension in FaceElement",
5428 "FaceElement::J_eulerian_at_knot()",
5430 }
5431
5432 // Return
5433 return sqrt(Adet);
5434 }
5435
5436 //========================================================================
5437 /// Check that Jacobian of mapping between local and Eulerian
5438 /// coordinates at all integration points is positive.
5439 //========================================================================
5441 {
5442 // Let's be optimistic...
5443 passed = true;
5444
5445 // Find the number of nodes
5446 const unsigned n_node = nnode();
5447
5448 // Find the number of position types
5449 const unsigned n_position_type = nnodal_position_type();
5450
5451 // Find the dimension of the node and element
5452 const unsigned n_dim = nodal_dimension();
5453 const unsigned n_dim_el = dim();
5454
5455 // Set up dummy memory for the shape functions
5458
5459
5460 unsigned nintpt = integral_pt()->nweight();
5461 for (unsigned ipt = 0; ipt < nintpt; ipt++)
5462 {
5463 // Get the shape functions and local derivatives at the knot
5464 // This call may use the stored versions, which is why this entire
5465 // function doesn't just call J_eulerian(s), after reading out s from
5466 // the knots.
5468
5469 // Also calculate the surface Vectors (derivatives wrt local coordinates)
5471
5472 // Calculate positions and derivatives
5473 for (unsigned l = 0; l < n_node; l++)
5474 {
5475 // Loop over positional dofs
5476 for (unsigned k = 0; k < n_position_type; k++)
5477 {
5478 // Loop over coordinates
5479 for (unsigned i = 0; i < n_dim; i++)
5480 {
5481 // Loop over LOCAL derivative directions, to calculate the
5482 // tangent(s)
5483 for (unsigned j = 0; j < n_dim_el; j++)
5484 {
5485 interpolated_A(j, i) +=
5487 dpsids(l, k, j);
5488 }
5489 }
5490 }
5491 }
5492
5493 // Now find the local deformed metric tensor from the tangent Vectors
5495 for (unsigned i = 0; i < n_dim_el; i++)
5496 {
5497 for (unsigned j = 0; j < n_dim_el; j++)
5498 {
5499 // Take the dot product
5500 for (unsigned k = 0; k < n_dim; k++)
5501 {
5502 A(i, j) += interpolated_A(i, k) * interpolated_A(j, k);
5503 }
5504 }
5505 }
5506
5507 // Find the determinant of the metric tensor
5508 double Adet = 0.0;
5509 switch (n_dim_el)
5510 {
5511 case 1:
5512 Adet = A(0, 0);
5513 break;
5514 case 2:
5515 Adet = A(0, 0) * A(1, 1) - A(0, 1) * A(1, 0);
5516 break;
5517 default:
5518 throw OomphLibError("Wrong dimension in FaceElement",
5519 "FaceElement::J_eulerian_at_knot()",
5521 }
5522
5523
5524 // Are we dead yet?
5525 if (Adet <= 0.0)
5526 {
5527 passed = false;
5528 return;
5529 }
5530 }
5531 }
5532
5533 //=======================================================================
5534 /// Compute the tangent vector(s) and the outer unit normal
5535 /// vector at the specified local coordinate.
5536 /// In two spatial dimensions, a "tangent direction" is not required.
5537 /// In three spatial dimensions, a tangent direction is required
5538 /// (set via set_tangent_direction(...)), and we project the tanent direction
5539 /// on to the surface. The second tangent vector is taken to be the cross
5540 /// product of the projection and the unit normal.
5541 //=======================================================================
5543 const Vector<double>& s,
5546 {
5547 // Find the spatial dimension of the FaceElement
5548 const unsigned element_dim = dim();
5549
5550 // Find the overall dimension of the problem
5551 //(assume that it's the same for all nodes)
5552 const unsigned spatial_dim = nodal_dimension();
5553
5554#ifdef PARANOID
5555 // Check the number of local coordinates passed
5556 if (s.size() != element_dim)
5557 {
5558 std::ostringstream error_stream;
5560 << "Local coordinate s passed to outer_unit_normal() has dimension "
5561 << s.size() << std::endl
5562 << "but element dimension is " << element_dim << std::endl;
5563
5564 throw OomphLibError(
5566 }
5567
5568 // Check that if the Tangent_direction_pt is set, then
5569 // it is the same length as the spatial dimension.
5570 if (Tangent_direction_pt != 0 &&
5572 {
5573 std::ostringstream error_stream;
5574 error_stream << "Tangent direction vector has dimension "
5575 << Tangent_direction_pt->size() << std::endl
5576 << "but spatial dimension is " << spatial_dim << std::endl;
5577
5578 throw OomphLibError(
5580 }
5581
5582 // Check the dimension of the normal vector
5583 if (unit_normal.size() != spatial_dim)
5584 {
5585 std::ostringstream error_stream;
5586 error_stream << "Unit normal passed to outer_unit_normal() has dimension "
5587 << unit_normal.size() << std::endl
5588 << "but spatial dimension is " << spatial_dim << std::endl;
5589
5590 throw OomphLibError(
5592 }
5593
5594
5595 // The number of tangent vectors.
5596 unsigned ntangent_vec = tang_vec.size();
5597
5598 // For the tangent vector,
5599 // if element_dim = 2, tang_vec is a 2D Vector of length 3.
5600 // if element_dim = 1, tang_vec is a 1D Vector of length 2.
5601 // if element_dim = 0, tang_vec is a 1D Vector of length 1.
5602 switch (element_dim)
5603 {
5604 // Point element, derived from a 1D element.
5605 case 0:
5606 {
5607 // Check that tang_vec is a 1D vector.
5608 if (ntangent_vec != 1)
5609 {
5610 std::ostringstream error_stream;
5612 << "This is a 0D FaceElement, we need one tangent vector.\n"
5613 << "You have given me " << tang_vec.size() << " vector(s).\n";
5614 throw OomphLibError(error_stream.str(),
5617 }
5618 }
5619 break;
5620
5621 // Line element, derived from a 2D element.
5622 case 1:
5623 {
5624 // Check that tang_vec is a 1D vector.
5625 if (ntangent_vec != 1)
5626 {
5627 std::ostringstream error_stream;
5629 << "This is a 1D FaceElement, we need one tangent vector.\n"
5630 << "You have given me " << tang_vec.size() << " vector(s).\n";
5631 throw OomphLibError(error_stream.str(),
5634 }
5635 }
5636 break;
5637
5638 // Plane element, derived from 3D element.
5639 case 2:
5640 {
5641 // Check that tang_vec is a 2D vector.
5642 if (ntangent_vec != 2)
5643 {
5644 std::ostringstream error_stream;
5646 << "This is a 2D FaceElement, we need two tangent vectors.\n"
5647 << "You have given me " << tang_vec.size() << " vector(s).\n";
5648 throw OomphLibError(error_stream.str(),
5651 }
5652 }
5653 break;
5654 // There are no other elements!
5655 default:
5656
5657 throw OomphLibError(
5658 "Cannot have a FaceElement with dimension higher than 2",
5661 break;
5662 }
5663
5664 // Check the lengths of each sub vectors.
5665 for (unsigned vec_i = 0; vec_i < ntangent_vec; vec_i++)
5666 {
5667 if (tang_vec[vec_i].size() != spatial_dim)
5668 {
5669 std::ostringstream error_stream;
5670 error_stream << "This problem has " << spatial_dim
5671 << " spatial dimensions.\n"
5672 << "But the " << vec_i << " tangent vector has size "
5673 << tang_vec[vec_i].size() << std::endl;
5674 throw OomphLibError(
5676 }
5677 }
5678
5679#endif
5680
5681
5682 // Now let's consider the different element dimensions
5683 switch (element_dim)
5684 {
5685 // Point element, derived from a 1D element.
5686 case 0:
5687 {
5688 std::ostringstream error_stream;
5690 << "It is unclear how to calculate a tangent and normal vectors for "
5691 << "a point element.\n";
5692 throw OomphLibError(
5694 }
5695 break;
5696
5697 // Line element, derived from a 2D element, in this case
5698 // the normal is a mess of cross products
5699 // We need an interior direction, so we must find the local
5700 // derivatives in the BULK element
5701 case 1:
5702 {
5703 // Find the number of nodes in the bulk element
5704 const unsigned n_node_bulk = Bulk_element_pt->nnode();
5705 // Find the number of position types in the bulk element
5706 const unsigned n_position_type_bulk =
5708
5709 // Construct the local coordinate in the bulk element
5711 // Get the local coordinates in the bulk element
5713
5714 // Allocate storage for the shape functions and their derivatives wrt
5715 // local coordinates
5718 // Get the value of the shape functions at the given local coordinate
5720
5721 // Calculate all derivatives of the spatial coordinates
5722 // wrt local coordinates
5724
5725 // Loop over all parent nodes
5726 for (unsigned l = 0; l < n_node_bulk; l++)
5727 {
5728 // Loop over all position types in the bulk
5729 for (unsigned k = 0; k < n_position_type_bulk; k++)
5730 {
5731 // Loop over derivative direction
5732 for (unsigned j = 0; j < 2; j++)
5733 {
5734 // Loop over coordinate directions
5735 for (unsigned i = 0; i < spatial_dim; i++)
5736 {
5737 // Compute the spatial derivative
5738 interpolated_dxds(j, i) +=
5740 dpsids(l, k, j);
5741 }
5742 }
5743 }
5744 }
5745
5746 // Initialise the tangent, interior tangent and normal vectors to zero
5747 // The idea is that even if the element is in a two-dimensional space,
5748 // the normal cannot be calculated without embedding the element in
5749 // three dimensions, in which case, the tangent and interior tangent
5750 // will have zero z-components.
5751 Vector<double> tangent(3, 0.0), interior_tangent(3, 0.0),
5752 normal(3, 0.0);
5753
5754 // We must get the relationship between the coordinate along the face
5755 // and the local coordinates in the bulk element
5756 // We must also find an interior direction
5758 unsigned interior_direction = 0;
5760 // Load in the values for the tangents
5761 for (unsigned i = 0; i < spatial_dim; i++)
5762 {
5763 // Tangent to the face is the derivative wrt to the face coordinate
5764 // which is calculated using dsbulk_dsface and the chain rule
5765 tangent[i] = interpolated_dxds(0, i) * dsbulk_dsface(0, 0) +
5766 interpolated_dxds(1, i) * dsbulk_dsface(1, 0);
5767 // Interior tangent to the face is the derivative in the interior
5768 // direction
5770 }
5771
5772 // Now the (3D) normal to the element is the interior tangent
5773 // crossed with the tangent
5775
5776 // We find the line normal by crossing the element normal with the
5777 // tangent
5780
5781 // Copy the appropriate entries into the unit normal
5782 // Two or Three depending upon the spatial dimension of the system
5783 for (unsigned i = 0; i < spatial_dim; i++)
5784 {
5786 }
5787
5788 // Finally normalise unit normal and multiply by the Normal_sign
5790 for (unsigned i = 0; i < spatial_dim; i++)
5791 {
5793 }
5794
5795 // Create the tangent vector
5796 tang_vec[0][0] = -unit_normal[1];
5797 tang_vec[0][1] = unit_normal[0];
5798 }
5799 break;
5800
5801 // Plane element, derived from 3D element, in this case the normal
5802 // is just the cross product of the two surface tangents
5803 // We assume, therefore, that we have three spatial coordinates
5804 // and two surface coordinates
5805 // Then we need only to get the derivatives wrt the local coordinates
5806 // in this face element
5807 case 2:
5808 {
5809#ifdef PARANOID
5810 // Check that we actually have three spatial dimensions
5811 if (spatial_dim != 3)
5812 {
5813 std::ostringstream error_stream;
5814 error_stream << "There are only " << spatial_dim
5815 << "coordinates at the nodes of this 2D FaceElement,\n"
5816 << "which must have come from a 3D Bulk element\n";
5817 throw OomphLibError(error_stream.str(),
5820 }
5821#endif
5822
5823 // Find the number of nodes in the element
5824 const unsigned n_node = this->nnode();
5825 // Find the number of position types
5826 const unsigned n_position_type = this->nnodal_position_type();
5827
5828 // Allocate storage for the shape functions and their derivatives wrt
5829 // local coordinates
5832 // Get the value of the shape functions at the given local coordinate
5833 this->dshape_local(s, psi, dpsids);
5834
5835 // Calculate all derivatives of the spatial coordinates
5836 // wrt local coordinates
5838
5839 // Loop over all nodes
5840 for (unsigned l = 0; l < n_node; l++)
5841 {
5842 // Loop over all position types
5843 for (unsigned k = 0; k < n_position_type; k++)
5844 {
5845 // Loop over derivative directions
5846 for (unsigned j = 0; j < 2; j++)
5847 {
5848 // Loop over coordinate directions
5849 for (unsigned i = 0; i < 3; i++)
5850 {
5851 // Compute the spatial derivative
5852 // Remember that we need to translate the position type
5853 // to its location in the bulk node
5854 interpolated_dxds[j][i] +=
5856 dpsids(l, k, j);
5857 }
5858 }
5859 }
5860 }
5861
5862 // We now take the cross product of the two normal vectors
5865
5866 // Finally normalise unit normal
5868
5869 for (unsigned i = 0; i < spatial_dim; i++)
5870 {
5872 }
5873
5874 // Next we create the continuous tangent vectors!
5875 if (Tangent_direction_pt != 0)
5876 // There is a general direction that the first tangent vector should
5877 // follow.
5878 {
5879 // Project the Tangent direction vector onto the surface.
5880 // Project Tangent_direction_pt D onto the plane P defined by
5881 // T1 and T2:
5882 // proj_P(D) = proj_T1(D) + proj_T2(D), where D is
5883 // Tangent_direction_pt, recall that proj_u(v) = (u.v)/(u.u) * u
5884
5885 // Get the direction vector. The vector is NOT copied! :)
5887
5888#ifdef PARANOID
5889 // Check that the angle between the direction vector and the normal
5890 // is not less than 10 degrees
5892 (10.0 * MathematicalConstants::Pi / 180.0))
5893 {
5894 std::ostringstream err_stream;
5895 err_stream << "The angle between the unit normal and the \n"
5896 << "direction vector is less than 10 degrees.";
5897 throw OomphLibError(err_stream.str(),
5900 }
5901#endif
5902
5903 // Calculate the two scalings, (u.v) / (u.u)
5904 double t1_scaling =
5907
5908 double t2_scaling =
5911
5912 // Finish off the projection.
5913 tang_vec[0][0] = t1_scaling * interpolated_dxds[0][0] +
5915 tang_vec[0][1] = t1_scaling * interpolated_dxds[0][1] +
5917 tang_vec[0][2] = t1_scaling * interpolated_dxds[0][2] +
5919
5920 // The second tangent vector is the cross product of
5921 // tang_vec[0] and the normal vector N.
5923
5924 // Normalise the tangent vectors
5925 for (unsigned vec_i = 0; vec_i < 2; vec_i++)
5926 {
5927 // Get the length...
5929
5930 for (unsigned dim_i = 0; dim_i < spatial_dim; dim_i++)
5931 {
5933 }
5934 }
5935 }
5936 else
5937 // A direction for the first tangent vector is not supplied, we do our
5938 // best to keep it continuous. But if the normal vector is aligned with
5939 // the z axis, then the tangent vector may "flip", even within elements.
5940 // This is because we check this by comparing doubles.
5941 // The code is copied from impose_parallel_outflow_element.h,
5942 // NO modification is done except for a few variable name changes.
5943 {
5944 double a, b, c;
5945 a = unit_normal[0];
5946 b = unit_normal[1];
5947 c = unit_normal[2];
5948
5949 if (a != 0.0 || b != 0.0)
5950 {
5951 double a_sq = a * a;
5952 double b_sq = b * b;
5953 double c_sq = c * c;
5954
5955 tang_vec[0][0] = -b / sqrt(a_sq + b_sq);
5956 tang_vec[0][1] = a / sqrt(a_sq + b_sq);
5957 tang_vec[0][2] = 0;
5958
5959 double z = (a_sq + b_sq) / sqrt(a_sq * c_sq + b_sq * c_sq +
5960 (a_sq + b_sq) * (a_sq + b_sq));
5961
5962 tang_vec[1][0] = -(a * c * z) / (a_sq + b_sq);
5963 tang_vec[1][1] = -(b * c * z) / (a_sq + b_sq);
5964 tang_vec[1][2] = z;
5965 // NB : we didn't use the fact that N is normalized,
5966 // that's why we have these unsimplified formulas
5967 }
5968 else if (c != 0.0)
5969 {
5971 {
5972 std::ostringstream warn_stream;
5974 << "I have detected that your normal vector is [0,0,1].\n"
5975 << "Since this function compares against 0.0, you may\n"
5976 << "get discontinuous tangent vectors.";
5980 }
5981
5982 tang_vec[0][0] = 1.0;
5983 tang_vec[0][1] = 0.0;
5984 tang_vec[0][2] = 0.0;
5985
5986 tang_vec[1][0] = 0.0;
5987 tang_vec[1][1] = 1.0;
5988 tang_vec[1][2] = 0.0;
5989 }
5990 else
5991 {
5992 throw OomphLibError("You have a zero normal vector!! ",
5995 }
5996 }
5997 }
5998 break;
5999
6000 default:
6001
6002 throw OomphLibError(
6003 "Cannot have a FaceElement with dimension higher than 2",
6006 break;
6007 }
6008 }
6009
6010 //=======================================================================
6011 /// Compute the tangent vector(s) and the outer unit normal
6012 /// vector at the ipt-th integration point. This is a wrapper around
6013 /// continuous_tangent_and_outer_unit_normal(...) with the integration points
6014 /// converted into local coordinates.
6015 //=======================================================================
6017 const unsigned& ipt,
6020 {
6021 // Find the dimension of the element
6022 const unsigned element_dim = dim();
6023 // Find the local coordinates of the ipt-th integration point
6025 for (unsigned i = 0; i < element_dim; i++)
6026 {
6027 s[i] = integral_pt()->knot(ipt, i);
6028 }
6029 // Call the outer unit normal function
6031 }
6032
6033 //=======================================================================
6034 /// Compute the outer unit normal at the specified local coordinate
6035 //=======================================================================
6038 {
6039 // Find the spatial dimension of the FaceElement
6040 const unsigned element_dim = dim();
6041
6042 // Find the overall dimension of the problem
6043 //(assume that it's the same for all nodes)
6044 const unsigned spatial_dim = nodal_dimension();
6045
6046#ifdef PARANOID
6047 // Check the number of local coordinates passed
6048 if (s.size() != element_dim)
6049 {
6050 std::ostringstream error_stream;
6052 << "Local coordinate s passed to outer_unit_normal() has dimension "
6053 << s.size() << std::endl
6054 << "but element dimension is " << element_dim << std::endl;
6055
6056 throw OomphLibError(
6058 }
6059
6060 // Check the dimension of the normal vector
6061 if (unit_normal.size() != spatial_dim)
6062 {
6063 std::ostringstream error_stream;
6064 error_stream << "Unit normal passed to outer_unit_normal() has dimension "
6065 << unit_normal.size() << std::endl
6066 << "but spatial dimension is " << spatial_dim << std::endl;
6067
6068 throw OomphLibError(
6070 }
6071#endif
6072
6073 /* //The spatial dimension of the bulk element will be element_dim+1
6074 const unsigned bulk_dim = element_dim + 1;
6075
6076 //Find the number of nodes in the bulk element
6077 const unsigned n_node_bulk = Bulk_element_pt->nnode();
6078 //Find the number of position types in the bulk element
6079 const unsigned n_position_type_bulk =
6080 Bulk_element_pt->nnodal_position_type();
6081
6082 //Construct the local coordinate in the bulk element
6083 Vector<double> s_bulk(bulk_dim);
6084 //Set the value of the bulk coordinate that is fixed on the face
6085 //s_bulk[s_fixed_index()] = s_fixed_value();
6086
6087 //Set the other bulk coordinates
6088 //for(unsigned i=0;i<element_dim;i++) {s_bulk[bulk_s_index(i)] = s[i];}
6089
6090 get_local_coordinate_in_bulk(s,s_bulk);
6091
6092 //Allocate storage for the shape functions and their derivatives wrt
6093 //local coordinates
6094 Shape psi(n_node_bulk,n_position_type_bulk);
6095 DShape dpsids(n_node_bulk,n_position_type_bulk,bulk_dim);
6096 //Get the value of the shape functions at the given local coordinate
6097 Bulk_element_pt->dshape_local(s_bulk,psi,dpsids);
6098
6099 //Calculate all derivatives of the spatial coordinates wrt local
6100 coordinates DenseMatrix<double> interpolated_dxds(bulk_dim,spatial_dim);
6101 //Initialise to zero
6102 for(unsigned j=0;j<bulk_dim;j++)
6103 {for(unsigned i=0;i<spatial_dim;i++) {interpolated_dxds(j,i) = 0.0;}}
6104
6105 //Loop over all parent nodes
6106 for(unsigned l=0;l<n_node_bulk;l++)
6107 {
6108 //Loop over all position types in the bulk
6109 for(unsigned k=0;k<n_position_type_bulk;k++)
6110 {
6111 //Loop over derivative direction
6112 for(unsigned j=0;j<bulk_dim;j++)
6113 {
6114 //Loop over coordinate directions
6115 for(unsigned i=0;i<spatial_dim;i++)
6116 {
6117 //Compute the spatial derivative
6118 interpolated_dxds(j,i) +=
6119 Bulk_element_pt->nodal_position_gen(l,k,i)*dpsids(l,k,j);
6120 }
6121 }
6122 }
6123 }*/
6124
6125 // Now let's consider the different element dimensions
6126 switch (element_dim)
6127 {
6128 // Point element, derived from a 1D element, in this case
6129 // the normal is merely the tangent to the bulk element
6130 // and there is only one free coordinate in the bulk element
6131 // Hence we will need to calculate the derivatives wrt the
6132 // local coordinates in the BULK element.
6133 case 0:
6134 {
6135 // Find the number of nodes in the bulk element
6136 const unsigned n_node_bulk = Bulk_element_pt->nnode();
6137 // Find the number of position types in the bulk element
6138 const unsigned n_position_type_bulk =
6140
6141 // Construct the local coordinate in the bulk element
6143
6144 // Get the local coordinates in the bulk element
6146
6147 // Allocate storage for the shape functions and their derivatives wrt
6148 // local coordinates
6151 // Get the value of the shape functions at the given local coordinate
6153
6154 // Calculate all derivatives of the spatial coordinates wrt
6155 // local coordinates
6157
6158 // Loop over all parent nodes
6159 for (unsigned l = 0; l < n_node_bulk; l++)
6160 {
6161 // Loop over all position types in the bulk
6162 for (unsigned k = 0; k < n_position_type_bulk; k++)
6163 {
6164 // Loop over coordinate directions
6165 for (unsigned i = 0; i < spatial_dim; i++)
6166 {
6167 // Compute the spatial derivative
6168 interpolated_dxds(0, i) +=
6170 }
6171 }
6172 }
6173
6174 // Now the unit normal is just the derivative of the position vector
6175 // with respect to the single coordinate
6176 for (unsigned i = 0; i < spatial_dim; i++)
6177 {
6179 }
6180 }
6181 break;
6182
6183 // Line element, derived from a 2D element, in this case
6184 // the normal is a mess of cross products
6185 // We need an interior direction, so we must find the local
6186 // derivatives in the BULK element
6187 case 1:
6188 {
6189 // Find the number of nodes in the bulk element
6190 const unsigned n_node_bulk = Bulk_element_pt->nnode();
6191 // Find the number of position types in the bulk element
6192 const unsigned n_position_type_bulk =
6194
6195 // Construct the local coordinate in the bulk element
6197 // Get the local coordinates in the bulk element
6199
6200 // Allocate storage for the shape functions and their derivatives wrt
6201 // local coordinates
6204 // Get the value of the shape functions at the given local coordinate
6206
6207 // Calculate all derivatives of the spatial coordinates
6208 // wrt local coordinates
6210
6211 // Loop over all parent nodes
6212 for (unsigned l = 0; l < n_node_bulk; l++)
6213 {
6214 // Loop over all position types in the bulk
6215 for (unsigned k = 0; k < n_position_type_bulk; k++)
6216 {
6217 // Loop over derivative direction
6218 for (unsigned j = 0; j < 2; j++)
6219 {
6220 // Loop over coordinate directions
6221 for (unsigned i = 0; i < spatial_dim; i++)
6222 {
6223 // Compute the spatial derivative
6224 interpolated_dxds(j, i) +=
6226 dpsids(l, k, j);
6227 }
6228 }
6229 }
6230 }
6231
6232 // Initialise the tangent, interior tangent and normal vectors to zero
6233 // The idea is that even if the element is in a two-dimensional space,
6234 // the normal cannot be calculated without embedding the element in
6235 // three dimensions, in which case, the tangent and interior tangent
6236 // will have zero z-components.
6237 Vector<double> tangent(3, 0.0), interior_tangent(3, 0.0),
6238 normal(3, 0.0);
6239
6240 // We must get the relationship between the coordinate along the face
6241 // and the local coordinates in the bulk element
6242 // We must also find an interior direction
6244 unsigned interior_direction = 0;
6246 // Load in the values for the tangents
6247 for (unsigned i = 0; i < spatial_dim; i++)
6248 {
6249 // Tangent to the face is the derivative wrt to the face coordinate
6250 // which is calculated using dsbulk_dsface and the chain rule
6251 tangent[i] = interpolated_dxds(0, i) * dsbulk_dsface(0, 0) +
6252 interpolated_dxds(1, i) * dsbulk_dsface(1, 0);
6253 // Interior tangent to the face is the derivative in the interior
6254 // direction
6256 }
6257
6258 // Now the (3D) normal to the element is the interior tangent
6259 // crossed with the tangent
6261
6262 // We find the line normal by crossing the element normal with the
6263 // tangent
6266
6267 // Copy the appropriate entries into the unit normal
6268 // Two or Three depending upon the spatial dimension of the system
6269 for (unsigned i = 0; i < spatial_dim; i++)
6270 {
6272 }
6273 }
6274 break;
6275
6276 // Plane element, derived from 3D element, in this case the normal
6277 // is just the cross product of the two surface tangents
6278 // We assume, therefore, that we have three spatial coordinates
6279 // and two surface coordinates
6280 // Then we need only to get the derivatives wrt the local coordinates
6281 // in this face element
6282 case 2:
6283 {
6284#ifdef PARANOID
6285 // Check that we actually have three spatial dimensions
6286 if (spatial_dim != 3)
6287 {
6288 std::ostringstream error_stream;
6289 error_stream << "There are only " << spatial_dim
6290 << "coordinates at the nodes of this 2D FaceElement,\n"
6291 << "which must have come from a 3D Bulk element\n";
6292 throw OomphLibError(error_stream.str(),
6295 }
6296#endif
6297
6298 // Find the number of nodes in the element
6299 const unsigned n_node = this->nnode();
6300 // Find the number of position types
6301 const unsigned n_position_type = this->nnodal_position_type();
6302
6303 // Allocate storage for the shape functions and their derivatives wrt
6304 // local coordinates
6307 // Get the value of the shape functions at the given local coordinate
6308 this->dshape_local(s, psi, dpsids);
6309
6310 // Calculate all derivatives of the spatial coordinates
6311 // wrt local coordinates
6313
6314 // Loop over all nodes
6315 for (unsigned l = 0; l < n_node; l++)
6316 {
6317 // Loop over all position types
6318 for (unsigned k = 0; k < n_position_type; k++)
6319 {
6320 // Loop over derivative directions
6321 for (unsigned j = 0; j < 2; j++)
6322 {
6323 // Loop over coordinate directions
6324 for (unsigned i = 0; i < 3; i++)
6325 {
6326 // Compute the spatial derivative
6327 // Remember that we need to translate the position type
6328 // to its location in the bulk node
6329 interpolated_dxds[j][i] +=
6331 dpsids(l, k, j);
6332 }
6333 }
6334 }
6335 }
6336
6337 // We now take the cross product of the two normal vectors
6340 }
6341 break;
6342
6343 default:
6344
6345 throw OomphLibError(
6346 "Cannot have a FaceElement with dimension higher than 2",
6349 break;
6350 }
6351
6352 // Finally normalise unit normal
6354 for (unsigned i = 0; i < spatial_dim; i++)
6355 {
6357 }
6358 }
6359
6360 //=======================================================================
6361 /// Compute the outer unit normal at the ipt-th integration point
6362 //=======================================================================
6365 {
6366 // Find the dimension of the element
6367 const unsigned element_dim = dim();
6368 // Find the local coordiantes of the ipt-th integration point
6370 for (unsigned i = 0; i < element_dim; i++)
6371 {
6372 s[i] = integral_pt()->knot(ipt, i);
6373 }
6374 // Call the outer unit normal function
6376 }
6377
6378
6379 //=======================================================================
6380 /// Return vector of local coordinates in bulk element,
6381 /// given the local coordinates in this FaceElement
6382 //=======================================================================
6384 const Vector<double>& s) const
6385 {
6386 // Find the dimension of the bulk element
6387 unsigned dim_bulk = Bulk_element_pt->dim();
6388
6389 // Vector of local coordinates in bulk element
6391
6392 // Use the function pointer if it is set
6394 {
6395 // Call the translation function
6396 (*Face_to_bulk_coordinate_fct_pt)(s, s_bulk);
6397 }
6398 else
6399 {
6400 throw OomphLibError("Face_to_bulk_coordinate mapping not set",
6403 }
6404
6405 // Return it
6406 return s_bulk;
6407 }
6408
6409
6410 //=======================================================================
6411 /// Calculate the vector of local coordinates in bulk element,
6412 /// given the local coordinates in this FaceElement
6413 //=======================================================================
6415 Vector<double>& s_bulk) const
6416 {
6417 // Use the function pointer if it is set
6419 {
6420 // Call the translation function
6421 (*Face_to_bulk_coordinate_fct_pt)(s, s_bulk);
6422 }
6423 // Otherwise use the existing (not general) interface
6424 else
6425 {
6426 throw OomphLibError("Face_to_bulk_coordinate mapping not set",
6429 }
6430 }
6431
6432
6433 //=======================================================================
6434 /// Calculate the derivatives of the local coordinates in the
6435 /// bulk element with respect to the local coordinates in this FaceElement.
6436 /// In addition return the index of a bulk local coordinate that varies away
6437 /// from the face.
6438 //=======================================================================
6441 unsigned& interior_direction) const
6442 {
6443 // Use the function pointer if it is set
6445 {
6446 // Call the translation function
6447 (*Bulk_coordinate_derivatives_fct_pt)(
6449 }
6450 // Otherwise throw an error
6451 else
6452 {
6453 throw OomphLibError(
6454 "No function for derivatives of bulk coords wrt face coords set",
6457 }
6458 }
6459
6460
6461 /// ////////////////////////////////////////////////////////////////////////
6462 /// ////////////////////////////////////////////////////////////////////////
6463 // Functions for elastic general elements
6464 /// ////////////////////////////////////////////////////////////////////////
6465 /// ////////////////////////////////////////////////////////////////////////
6466
6467
6468 //==================================================================
6469 /// Calculate the L2 norm of the displacement u=R-r to overload the
6470 /// compute_norm function in the GeneralisedElement base class
6471 //==================================================================
6473 {
6474 // Initialise el_norm to 0.0
6475 el_norm = 0.0;
6476
6477 unsigned n_dim = dim();
6478
6479 // Vector of local coordinates
6481
6482 // Displacement vector, Lagrangian coordinate vector and Eulerian
6483 // coordinate vector respectively
6485 Vector<double> xi(n_dim, 0.0);
6486 Vector<double> x(n_dim, 0.0);
6487
6488 // Find out how many nodes there are in the element
6489 unsigned n_node = this->nnode();
6490
6491 // Construct a shape function
6492 Shape psi(n_node);
6493
6494 // Get the number of integration points
6495 unsigned n_intpt = this->integral_pt()->nweight();
6496
6497 // Loop over the integration points
6498 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
6499 {
6500 // Assign values of s
6501 for (unsigned i = 0; i < n_dim; i++)
6502 {
6503 s[i] = this->integral_pt()->knot(ipt, i);
6504 }
6505
6506 // Get the integral weight
6507 double w = this->integral_pt()->weight(ipt);
6508
6509 // Get jacobian of mapping
6510 double J = this->J_eulerian(s);
6511
6512 // Premultiply the weights and the Jacobian
6513 double W = w * J;
6514
6515 // Get the Lagrangian and Eulerian coordinate at this point, respectively
6516 this->interpolated_xi(s, xi);
6517 this->interpolated_x(s, x);
6518
6519 // Calculate the displacement vector, u=R-r=x-xi
6520 for (unsigned idim = 0; idim < n_dim; idim++)
6521 {
6522 disp[idim] = x[idim] - xi[idim];
6523 }
6524
6525 // Add to norm
6526 for (unsigned ii = 0; ii < n_dim; ii++)
6527 {
6528 el_norm += (disp[ii] * disp[ii]) * W;
6529 }
6530 }
6531 } // End of compute_norm(...)
6532
6533
6534 //=========================================================================
6535 /// Function to describe the local dofs of the element. The ostream
6536 /// specifies the output stream to which the description
6537 /// is written; the string stores the currently
6538 /// assembled output that is ultimately written to the
6539 /// output stream by Data::describe_dofs(...); it is typically
6540 /// built up incrementally as we descend through the
6541 /// call hierarchy of this function when called from
6542 /// Problem::describe_dofs(...)
6543 //=========================================================================
6545 std::ostream& out, const std::string& current_string) const
6546 {
6547 // Call the standard finite element description.
6550 }
6551
6552
6553 //=========================================================================
6554 /// Internal function that is used to assemble the jacobian of the mapping
6555 /// from local coordinates (s) to the lagrangian coordinates (xi), given the
6556 /// derivatives of the shape functions.
6557 //=========================================================================
6559 const DShape& dpsids, DenseMatrix<double>& jacobian) const
6560 {
6561 // Find the the dimension of the element
6562 const unsigned el_dim = dim();
6563 // Find the number of shape functions and shape functions types
6564 // We shall ASSUME (ENFORCE) that Lagrangian coordinates must
6565 // be interpolated through the nodes
6566 const unsigned n_shape = nnode();
6567 const unsigned n_shape_type = nnodal_lagrangian_type();
6568
6569#ifdef PARANOID
6570 // Check for dimensional compatibility
6572 {
6573 std::ostringstream error_message;
6574 error_message << "Dimension mismatch" << std::endl;
6575 error_message << "The elemental dimension: " << el_dim
6576 << " must equal the nodal Lagrangian dimension: "
6578 << " for the jacobian of the mapping to be well-defined"
6579 << std::endl;
6580 throw OomphLibError(
6581 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
6582 }
6583#endif
6584
6585 // Loop over the rows of the jacobian
6586 for (unsigned i = 0; i < el_dim; i++)
6587 {
6588 // Loop over the columns of the jacobian
6589 for (unsigned j = 0; j < el_dim; j++)
6590 {
6591 // Zero the entry
6592 jacobian(i, j) = 0.0;
6593 // Loop over the shape functions
6594 for (unsigned l = 0; l < n_shape; l++)
6595 {
6596 for (unsigned k = 0; k < n_shape_type; k++)
6597 {
6598 // Jacobian is dx_j/ds_i, which is represented by the sum
6599 // over the dpsi/ds_i of the nodal points X j
6600 // Call the Non-hanging version of positions
6601 // This is overloaded in refineable elements
6602 jacobian(i, j) +=
6604 }
6605 }
6606 }
6607 }
6608 }
6609
6610 //=========================================================================
6611 /// Internal function that is used to assemble the jacobian of second
6612 /// derivatives of the the mapping from local coordinates (s) to the
6613 /// lagrangian coordinates (xi), given the second derivatives of the
6614 /// shape functions.
6615 //=========================================================================
6618 {
6619 // Find the the dimension of the element
6620 const unsigned el_dim = dim();
6621 // Find the number of shape functions and shape functions types
6622 // We ENFORCE that Lagrangian coordinates must be interpolated through
6623 // the nodes
6624 const unsigned n_shape = nnode();
6625 const unsigned n_shape_type = nnodal_lagrangian_type();
6626 // Find the number of second derivatives
6627 const unsigned n_row = N2deriv[el_dim];
6628
6629 // Assemble the "jacobian" (d^2 x_j/ds_i^2) for second derivatives of
6630 // shape functions
6631 // Loop over the rows (number of second derivatives)
6632 for (unsigned i = 0; i < n_row; i++)
6633 {
6634 // Loop over the columns (element dimension
6635 for (unsigned j = 0; j < el_dim; j++)
6636 {
6637 // Zero the entry
6638 jacobian2(i, j) = 0.0;
6639 // Loop over the shape functions
6640 for (unsigned l = 0; l < n_shape; l++)
6641 {
6642 // Loop over the shape function types
6643 for (unsigned k = 0; k < n_shape_type; k++)
6644 {
6645 // Add the terms to the jacobian entry
6646 // Call the Non-hanging version of positions
6647 // This is overloaded in refineable elements
6648 jacobian2(i, j) +=
6650 }
6651 }
6652 }
6653 }
6654 }
6655
6656 //============================================================================
6657 /// Destructor for SolidFiniteElement:
6658 //============================================================================
6660 {
6661 // Delete the storage allocated for the positional local equations
6662 delete[] Position_local_eqn;
6663 }
6664
6665
6666 //==========================================================================
6667 /// Calculate the mapping from local to lagrangian coordinates
6668 /// assuming that the coordinates are aligned in the direction of the local
6669 /// coordinates, i.e. there are no cross terms and the jacobian is diagonal.
6670 /// The local derivatives are passed as dpsids and the jacobian and
6671 /// inverse jacobian are returned.
6672 //==========================================================================
6674 const DShape& dpsids,
6675 DenseMatrix<double>& jacobian,
6677 {
6678 // Find the dimension of the element
6679 const unsigned el_dim = dim();
6680 // Find the number of shape functions and shape functions types
6681 // We shall ASSUME (ENFORCE) that Lagrangian coordinates must
6682 // be interpolated through the nodes
6683 const unsigned n_shape = nnode();
6684 const unsigned n_shape_type = nnodal_lagrangian_type();
6685
6686 // In this case we assume that there are no cross terms, that is
6687 // global coordinate 0 is always in the direction of local coordinate 0
6688
6689 // Loop over the coordinates
6690 for (unsigned i = 0; i < el_dim; i++)
6691 {
6692 // Zero the jacobian and inverse jacobian entries
6693 for (unsigned j = 0; j < el_dim; j++)
6694 {
6695 jacobian(i, j) = 0.0;
6696 inverse_jacobian(i, j) = 0.0;
6697 }
6698
6699 // Loop over the shape functions
6700 for (unsigned l = 0; l < n_shape; l++)
6701 {
6702 // Loop over the types of dof
6703 for (unsigned k = 0; k < n_shape_type; k++)
6704 {
6705 // Derivatives are always dx_{i}/ds_{i}
6706 jacobian(i, i) +=
6708 }
6709 }
6710 }
6711
6712 // Now calculate the determinant of the matrix
6713 double det = 1.0;
6714 for (unsigned i = 0; i < el_dim; i++)
6715 {
6716 det *= jacobian(i, i);
6717 }
6718
6719// Report if Matrix is singular, or negative
6720#ifdef PARANOID
6722#endif
6723
6724 // Calculate the inverse mapping (trivial in this case)
6725 for (unsigned i = 0; i < el_dim; i++)
6726 {
6727 inverse_jacobian(i, i) = 1.0 / jacobian(i, i);
6728 }
6729
6730 // Return the value of the Jacobian
6731 return (det);
6732 }
6733
6734 //========================================================================
6735 /// Calculate shape functions and derivatives w.r.t. Lagrangian
6736 /// coordinates at local coordinate s. Returns the Jacobian of the mapping
6737 /// from Lagrangian to local coordinates.
6738 /// General case, may be overloaded
6739 //========================================================================
6741 Shape& psi,
6742 DShape& dpsi) const
6743 {
6744 // Find the element dimension
6745 const unsigned el_dim = dim();
6746
6747 // Get the values of the shape function and local derivatives
6748 // Temporarily stored in dpsi
6750
6751 // Allocate memory for the inverse jacobian
6753 // Now calculate the inverse jacobian
6755
6756 // Now set the values of the derivatives to be dpsidxi
6758 // Return the determinant of the jacobian
6759 return det;
6760 }
6761
6762 //=========================================================================
6763 /// Compute the geometric shape functions and also first
6764 /// derivatives w.r.t. Lagrangian coordinates at integration point ipt.
6765 /// Most general form of function, but may be over-loaded if desired
6766 //========================================================================
6768 Shape& psi,
6769 DShape& dpsi) const
6770 {
6771 // Find the element dimension
6772 const unsigned el_dim = dim();
6773
6774 // Shape function for the local derivatives
6775 // Again we ASSUME (insist) that the lagrangian coordinates
6776 // are interpolated through the nodes
6777 // Get the values of the shape function and local derivatives
6779
6780 // Allocate memory for the inverse jacobian
6782 // Now calculate the inverse jacobian
6784
6785 // Now set the values of the derivatives
6787 // Return the determinant of the jacobian
6788 return det;
6789 }
6790
6791 //========================================================================
6792 /// Compute the geometric shape functions and also first
6793 /// and second derivatives w.r.t. Lagrangian coordinates at
6794 /// local coordinate s;
6795 /// Returns Jacobian of mapping from Lagrangian to local coordinates.
6796 /// Numbering:
6797 /// \b 1D:
6798 /// d2pidxi(i,0) = \f$ d^2 \psi_j / d \xi^2 \f$
6799 /// \b 2D:
6800 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6801 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6802 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j / \partial \xi_0 \partial \xi_1 \f$
6803 /// \b 3D:
6804 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6805 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6806 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j / \partial \xi_2^2 \f$
6807 /// d2psidxi(i,3) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_1 \f$
6808 /// d2psidxi(i,4) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_2 \f$
6809 /// d2psidxi(i,5) = \f$ \partial^2 \psi_j/\partial \xi_1 \partial \xi_2 \f$
6810 //========================================================================
6812 Shape& psi,
6813 DShape& dpsi,
6814 DShape& d2psi) const
6815 {
6816 // Find the element dimension
6817 const unsigned el_dim = dim();
6818 // Find the number of second derivatives required
6819 const unsigned n_deriv = N2deriv[el_dim];
6820
6821 // Get the values of the shape function and local derivatives
6823
6824 // Allocate memory for the jacobian and inverse jacobian
6826 // Calculate the jacobian and inverse jacobian
6827 const double det =
6829
6830 // Allocate memory for the jacobian of second derivatives
6832 // Assemble the jacobian of second derivatives
6834
6835 // Now set the value of the derivatives
6837 jacobian, inverse_jacobian, jacobian2, dpsi, d2psi);
6838 // Return the determinant of the mapping
6839 return det;
6840 }
6841
6842 //==========================================================================
6843 /// Compute the geometric shape functions and also first
6844 /// and second derivatives w.r.t. Lagrangian coordinates at
6845 /// the ipt-th integration point
6846 /// Returns Jacobian of mapping from Lagrangian to local coordinates.
6847 /// Numbering:
6848 /// \b 1D:
6849 /// d2pidxi(i,0) = \f$ d^2 \psi_j / d \xi^2 \f$
6850 /// \b 2D:
6851 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6852 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6853 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_1 \f$
6854 /// \b 3D:
6855 /// d2psidxi(i,0) = \f$ \partial^2 \psi_j / \partial \xi_0^2 \f$
6856 /// d2psidxi(i,1) = \f$ \partial^2 \psi_j / \partial \xi_1^2 \f$
6857 /// d2psidxi(i,2) = \f$ \partial^2 \psi_j / \partial \xi_2^2 \f$
6858 /// d2psidxi(i,3) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_1 \f$
6859 /// d2psidxi(i,4) = \f$ \partial^2 \psi_j/\partial \xi_0 \partial \xi_2 \f$
6860 /// d2psidxi(i,5) = \f$ \partial^2 \psi_j/\partial \xi_1 \partial \xi_2 \f$
6861 //========================================================================
6863 Shape& psi,
6864 DShape& dpsi,
6865 DShape& d2psi) const
6866 {
6867 // Find the values of the indices of the shape functions
6868 // Find the element dimension
6869 const unsigned el_dim = dim();
6870 // Find the number of second derivatives required
6871 const unsigned n_deriv = N2deriv[el_dim];
6872
6873 // Get the values of the shape function and local derivatives
6875
6876 // Allocate memory for the jacobian and inverse jacobian
6878 // Calculate the jacobian and inverse jacobian
6879 const double det =
6881
6882 // Allocate memory for the jacobian of second derivatives
6884 // Assemble the jacobian of second derivatives
6886
6887 // Now set the value of the derivatives
6889 jacobian, inverse_jacobian, jacobian2, dpsi, d2psi);
6890 // Return the determinant of the mapping
6891 return det;
6892 }
6893
6894 //============================================================================
6895 /// Function to describe the local dofs of the element. The ostream
6896 /// specifies the output stream to which the description
6897 /// is written; the string stores the currently
6898 /// assembled output that is ultimately written to the
6899 /// output stream by Data::describe_dofs(...); it is typically
6900 /// built up incrementally as we descend through the
6901 /// call hierarchy of this function when called from
6902 /// Problem::describe_dofs(...)
6903 //============================================================================
6905 std::ostream& out, const std::string& current_string) const
6906 {
6907 // Find the number of nodes
6908 const unsigned n_node = nnode();
6909 // Loop over the nodes
6910 for (unsigned n = 0; n < n_node; n++)
6911 {
6912 // Cast to a solid node
6913 SolidNode* cast_node_pt = static_cast<SolidNode*>(node_pt(n));
6914 std::stringstream conversion;
6915 conversion << " of Solid Node " << n << current_string;
6916 std::string in(conversion.str());
6918 }
6919 }
6920
6921 //============================================================================
6922 /// Assign local equation numbers for the solid equations in the element.
6923 // This can be done at a high level assuming, as I am, that the equations
6924 // will always be formulated in terms of nodal positions.
6925 /// If the boolean argument is true then pointers to the dofs will be
6926 /// stored in Dof_pt.
6927 //============================================================================
6929 const bool& store_local_dof_pt)
6930 {
6931 // Find the number of nodes
6932 const unsigned n_node = nnode();
6933
6934 // Check there are nodes!
6935 if (n_node > 0)
6936 {
6937 // Find the number of position types and dimensions of the nodes
6938 // Local caching
6939 const unsigned n_position_type = nnodal_position_type();
6940 const unsigned nodal_dim = nodal_dimension();
6941
6942 // Delete the existing storage
6943 delete[] Position_local_eqn;
6944 // Resize the storage for the positional equation numbers
6946
6947 // A local queue to store the global equation numbers
6948 std::deque<unsigned long> global_eqn_number_queue;
6949
6950 // Get the number of dofs so far, this must be outside both loops
6951 // so that both can use it
6952 unsigned local_eqn_number = ndof();
6953
6954 // Loop over the nodes
6955 for (unsigned n = 0; n < n_node; n++)
6956 {
6957 // Cast to a solid node
6958 SolidNode* cast_node_pt = static_cast<SolidNode*>(node_pt(n));
6959
6960 // Loop over the number of position dofs
6961 for (unsigned j = 0; j < n_position_type; j++)
6962 {
6963 // Loop over the dimension of each node
6964 for (unsigned k = 0; k < nodal_dim; k++)
6965 {
6966 // Get equation number
6967 // Note eqn_number is long !
6968 long eqn_number = cast_node_pt->position_eqn_number(j, k);
6969 // If equation_number positive add to array
6970 if (eqn_number >= 0)
6971 {
6972 // Add to global array
6974 // Add pointer to the dof to the queue if required
6976 {
6978 &(cast_node_pt->x_gen(j, k)));
6979 }
6980
6981 // Add to look-up scheme
6984 // Increment the local equation number
6986 }
6987 else
6988 {
6991 }
6992 }
6993 }
6994 } // End of loop over nodes
6995
6996 // Now add our global equations numbers to the internal element storage
6999 // Clear the memory used in the deque
7001 {
7002 std::deque<double*>().swap(GeneralisedElement::Dof_pt_deque);
7003 }
7004
7005 } // End of the case when there are nodes
7006 }
7007
7008
7009 //============================================================================
7010 /// This function calculates the entries of Jacobian matrix, used in
7011 /// the Newton method, associated with the elastic problem in which the
7012 /// nodal position is a variable. It does this using finite differences,
7013 /// rather than an analytical formulation, so can be done in total generality.
7014 //==========================================================================
7017 {
7018 // Flag to indicate if we use first or second order FD
7019 // bool use_first_order_fd=false;
7020
7021 // Find the number of nodes
7022 const unsigned n_node = nnode();
7023
7024 // If there aren't any nodes, then return straight away
7025 if (n_node == 0)
7026 {
7027 return;
7028 }
7029
7030 // Call the update function to ensure that the element is in
7031 // a consistent state before finite differencing starts
7033
7034 // Get the number of position dofs and dimensions at the node
7035 const unsigned n_position_type = nnodal_position_type();
7036 const unsigned nodal_dim = nodal_dimension();
7037
7038 // Find the number of dofs in the element
7039 const unsigned n_dof = this->ndof();
7040
7041 // Create newres vectors
7043 // Vector<double> newres_minus(n_dof);
7044
7045 // Integer storage for local unknown
7046 int local_unknown = 0;
7047
7048 // Should probably give this a more global scope
7049 const double fd_step = Default_fd_jacobian_step;
7050
7051 // Loop over the nodes
7052 for (unsigned n = 0; n < n_node; n++)
7053 {
7054 // Loop over position dofs
7055 for (unsigned k = 0; k < n_position_type; k++)
7056 {
7057 // Loop over dimension
7058 for (unsigned i = 0; i < nodal_dim; i++)
7059 {
7060 // If the variable is free
7062 if (local_unknown >= 0)
7063 {
7064 // Store a pointer to the (generalised) Eulerian nodal position
7065 double* const value_pt = &(node_pt(n)->x_gen(k, i));
7066
7067 // Save the old value of the (generalised) Eulerian nodal position
7068 const double old_var = *value_pt;
7069
7070 // Increment the (generalised) Eulerian nodal position
7071 *value_pt += fd_step;
7072
7073 // Perform any auxialiary node updates
7075
7076 // Update any other dependent variables
7078
7079 // Calculate the new residuals
7081
7082 // if (use_first_order_fd)
7083 {
7084 // Do forward finite differences
7085 for (unsigned m = 0; m < n_dof; m++)
7086 {
7087 // Stick the entry into the Jacobian matrix
7088 jacobian(m, local_unknown) =
7089 (newres[m] - residuals[m]) / fd_step;
7090 }
7091 }
7092 // else
7093 // {
7094 // //Take backwards step for the (generalised) Eulerian
7095 // nodal
7096 // // position
7097 // node_pt(n)->x_gen(k,i) = old_var-fd_step;
7098
7099 // //Calculate the new residuals at backward position
7100 // get_residuals(newres_minus);
7101
7102 // //Do central finite differences
7103 // for(unsigned m=0;m<n_dof;m++)
7104 // {
7105 // //Stick the entry into the Jacobian matrix
7106 // jacobian(m,local_unknown) =
7107 // (newres[m] - newres_minus[m])/(2.0*fd_step);
7108 // }
7109 // }
7110
7111 // Reset the (generalised) Eulerian nodal position
7112 *value_pt = old_var;
7113
7114
7115 // Perform any auxialiary node updates
7117
7118 // Reset any other dependent variables
7120 }
7121 }
7122 }
7123 }
7124
7125 // End of finite difference loop
7126 // Final reset of any dependent data
7128 }
7129
7130 //============================================================================
7131 /// Return i-th FE-interpolated Lagrangian coordinate at
7132 /// local coordinate s.
7133 //============================================================================
7135 const unsigned& i) const
7136 {
7137 // Find the number of nodes
7138 const unsigned n_node = nnode();
7139
7140 // Find the number of lagrangian types from the first node
7141 const unsigned n_lagrangian_type = nnodal_lagrangian_type();
7142
7143 // Assign storage for the local shape function
7145
7146 // Find the values of shape function
7147 shape(s, psi);
7148
7149 // Initialise value of xi
7150 double interpolated_xi = 0.0;
7151
7152 // Loop over the local nodes
7153 for (unsigned l = 0; l < n_node; l++)
7154 {
7155 // Loop over the number of dofs
7156 for (unsigned k = 0; k < n_lagrangian_type; k++)
7157 {
7159 }
7160 }
7161
7162 return (interpolated_xi);
7163 }
7164
7165 //============================================================================
7166 /// Compute FE-interpolated Lagrangian coordinate vector xi[] at
7167 /// local coordinate s.
7168 //============================================================================
7170 Vector<double>& xi) const
7171 {
7172 // Find the number of nodes
7173 const unsigned n_node = nnode();
7174
7175 // Find the number of lagrangian types from the first node
7176 const unsigned n_lagrangian_type = nnodal_lagrangian_type();
7177
7178 // Assign storage for the local shape function
7180
7181 // Find the values of shape function
7182 shape(s, psi);
7183
7184 // Read out the number of lagrangian coordinates from the node
7185 const unsigned n_lagrangian = lagrangian_dimension();
7186
7187 // Loop over the number of lagrangian coordinates
7188 for (unsigned i = 0; i < n_lagrangian; i++)
7189 {
7190 // Initialise component to zero
7191 xi[i] = 0.0;
7192
7193 // Loop over the local nodes
7194 for (unsigned l = 0; l < n_node; l++)
7195 {
7196 // Loop over the number of dofs
7197 for (unsigned k = 0; k < n_lagrangian_type; k++)
7198 {
7199 xi[i] += lagrangian_position_gen(l, k, i) * psi(l, k);
7200 }
7201 }
7202 }
7203 }
7204
7205
7206 //============================================================================
7207 /// Compute derivatives of FE-interpolated Lagrangian coordinates xi
7208 /// with respect to local coordinates: dxids[i][j]=dxi_i/ds_j
7209 //============================================================================
7212 {
7213 // Find the number of nodes
7214 const unsigned n_node = nnode();
7215
7216 // Find the number of lagrangian types from the first node
7217 const unsigned n_lagrangian_type = nnodal_lagrangian_type();
7218
7219 // Dimension of the element = number of local coordinates
7220 unsigned el_dim = dim();
7221
7222 // Assign storage for the local shape function
7225
7226 // Find the values of shape function and its derivs w.r.t. to local coords
7228
7229 // Read out the number of lagrangian coordinates from the node
7230 const unsigned n_lagrangian = lagrangian_dimension();
7231
7232 // Loop over the number of lagrangian and local coordinates
7233 for (unsigned i = 0; i < n_lagrangian; i++)
7234 {
7235 for (unsigned j = 0; j < el_dim; j++)
7236 {
7237 // Initialise component to zero
7238 dxids(i, j) = 0.0;
7239
7240 // Loop over the local nodes
7241 for (unsigned l = 0; l < n_node; l++)
7242 {
7243 // Loop over the number of dofs
7244 for (unsigned k = 0; k < n_lagrangian_type; k++)
7245 {
7246 dxids(i, j) += lagrangian_position_gen(l, k, i) * dpsi(l, k, j);
7247 }
7248 }
7249 }
7250 }
7251 }
7252
7253 //=======================================================================
7254 /// Add jacobian and residuals for consistent assignment of
7255 /// initial "accelerations" in Newmark scheme. Jacobian is the mass matrix.
7256 //=======================================================================
7258 DenseMatrix<double>& jacobian)
7259 {
7260#ifdef PARANOID
7261 // Check that we're computing the real residuals, not the
7262 // ones corresponding to the assignement of a prescribed displacement
7264 {
7265 std::ostringstream error_stream;
7266 error_stream << "Can only call fill_in_jacobian_for_newmark_accel()\n"
7267 << "With Solve_for_consistent_newmark_accel_flag:"
7269 error_stream << "Solid_ic_pt: " << Solid_ic_pt << std::endl;
7270
7271 throw OomphLibError(
7273 }
7274#endif
7275
7276 // Find the number of nodes
7277 const unsigned n_node = nnode();
7278 const unsigned n_position_type = nnodal_position_type();
7279 const unsigned nodal_dim = nodal_dimension();
7280
7281 // Set the number of Lagrangian coordinates
7282 const unsigned n_lagrangian = dim();
7283
7284 // Integer storage for local equation and unknown
7285 int local_eqn = 0, local_unknown = 0;
7286
7287 // Set up memory for the shape functions:
7288 // # of nodes, # of positional dofs
7290
7291 // # of nodes, # of positional dofs, # of lagrangian coords (for deriv)
7292 // Not needed but they come for free when compute the Jacobian.
7294
7295 // Set # of integration points
7296 const unsigned n_intpt = integral_pt()->nweight();
7297
7298 // Set the Vector to hold local coordinates
7300
7301 // Get positional timestepper from first nodal point
7303
7304#ifdef PARANOID
7305 // Of course all this only works if we're actually using a
7306 // Newmark timestepper!
7307 if (timestepper_pt->type() != "Newmark")
7308 {
7309 std::ostringstream error_stream;
7311 << "Assignment of Newmark accelerations obviously only works\n"
7312 << "for Newmark timesteppers. You've called me with "
7313 << timestepper_pt->type() << std::endl;
7314
7315 throw OomphLibError(
7317 }
7318#endif
7319
7320 // "Acceleration" is the last history value:
7321 const unsigned ntstorage = timestepper_pt->ntstorage();
7322 const double accel_weight = timestepper_pt->weight(2, ntstorage - 1);
7323
7324 // Loop over the integration points
7325 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
7326 {
7327 // Assign values of s
7328 for (unsigned i = 0; i < n_lagrangian; i++)
7329 {
7330 s[i] = integral_pt()->knot(ipt, i);
7331 }
7332
7333 // Get the integral weight
7334 double w = integral_pt()->weight(ipt);
7335
7336 // Jacobian of mapping between local and Lagrangian coords and shape
7337 // functions
7338 double J = dshape_lagrangian(s, psi, dpsidxi);
7339
7340 // Get Lagrangian coordinate
7342 interpolated_xi(s, xi);
7343
7344 // Get multiplier for inertia terms
7345 double factor = multiplier(xi);
7346
7347 // Premultiply the weights and the Jacobian
7348 double W = w * J;
7349
7350 // Loop over the nodes
7351 for (unsigned l0 = 0; l0 < n_node; l0++)
7352 {
7353 // Loop over the positional dofs: 'Type': 0: displacement; 1: deriv
7354 for (unsigned k0 = 0; k0 < n_position_type; k0++)
7355 {
7356 // Loop over Eulerian directions
7357 for (unsigned i0 = 0; i0 < nodal_dim; i0++)
7358 {
7360 // If it's a degree of freedom, add contribution
7361 if (local_eqn >= 0)
7362 {
7363 // Loop over the nodes
7364 for (unsigned l1 = 0; l1 < n_node; l1++)
7365 {
7366 // Loop over the positional dofs: 'Type': 0: displacement;
7367 // 1: deriv
7368 for (unsigned k1 = 0; k1 < n_position_type; k1++)
7369 {
7370 // Loop over Eulerian directions
7371 unsigned i1 = i0;
7372 {
7374 // If it's a degree of freedom, add contribution
7375 if (local_unknown >= 0)
7376 {
7377 // Add contribution: Mass matrix, multiplied by
7378 // weight for "acceleration" in Newmark scheme
7379 // and general multiplier
7380 jacobian(local_eqn, local_unknown) +=
7381 factor * accel_weight * psi(l0, k0) * psi(l1, k1) * W;
7382 }
7383 }
7384 }
7385 }
7386 }
7387 }
7388 }
7389 }
7390
7391 } // End of loop over the integration points
7392 }
7393
7394
7395 //=======================================================================
7396 /// Helper function to fill in the residuals and (if flag==1) the Jacobian
7397 /// for the setup of an initial condition. The global equations are:
7398 /// \f[ 0 = \int \left( \sum_{j=1}^N \sum_{k=1}^K X_{ijk} \psi_{jk}(\xi_n) - \frac{\partial^D R^{(IC)}_i(\xi_n)}{\partial t^D} \right) \psi_{lm}(\xi_n) \ dv \mbox{ \ \ \ \ for \ \ \ $l=1,...,N, \ \ m=1,...,K$} \f]
7399 /// where \f$ N \f$ is the number of nodes in the mesh and \f$ K \f$
7400 /// the number of generalised nodal coordinates. The initial shape
7401 /// of the solid body, \f$ {\bf R}^{(IC)},\f$ and its time-derivatives
7402 /// are specified via the \c GeomObject that is stored in the
7403 /// \c SolidFiniteElement::SolidInitialCondition object. The latter also
7404 /// stores the order of the time-derivative \f$ D \f$ to be assigned.
7405 //=======================================================================
7408 DenseMatrix<double>& jacobian,
7409 const unsigned& flag)
7410 {
7411 // Find the number of nodes and position types
7412 const unsigned n_node = nnode();
7413 const unsigned n_position_type = nnodal_position_type();
7414
7415 // Set the dimension of the global coordinates
7416 const unsigned nodal_dim = nodal_dimension();
7417
7418 // Find the number of lagragian types from the first node
7419 const unsigned n_lagrangian_type = nnodal_lagrangian_type();
7420
7421 // Set the number of lagrangian coordinates
7422 const unsigned n_lagrangian = dim();
7423
7424 // Integer storage for local equation number
7425 int local_eqn = 0;
7426 int local_unknown = 0;
7427
7428 // # of nodes, # of positional dofs
7430
7431 // # of nodes, # of positional dofs, # of lagrangian coords (for deriv)
7432 // not needed but they come for free when we compute the Jacobian
7433 // DShape dpsidxi(n_node,n_position_type,n_lagrangian);
7434
7435 // Set # of integration points
7436 const unsigned n_intpt = integral_pt()->nweight();
7437
7438 // Set the Vector to hold local coordinates
7440
7441 // Loop over the integration points
7442 for (unsigned ipt = 0; ipt < n_intpt; ipt++)
7443 {
7444 // Assign values of s
7445 for (unsigned i = 0; i < n_lagrangian; i++)
7446 {
7447 s[i] = integral_pt()->knot(ipt, i);
7448 }
7449
7450 // Get the integral weight
7451 double w = integral_pt()->weight(ipt);
7452
7453 // Shape fcts
7454 shape(s, psi);
7455
7456 // Get Lagrangian coordinate
7458
7459 // Loop over the number of lagrangian coordinates
7460 for (unsigned i = 0; i < n_lagrangian; i++)
7461 {
7462 // Loop over the local nodes
7463 for (unsigned l = 0; l < n_node; l++)
7464 {
7465 // Loop over the number of dofs
7466 for (unsigned k = 0; k < n_lagrangian_type; k++)
7467 {
7468 xi[i] += lagrangian_position_gen(l, k, i) * psi(l, k);
7469 }
7470 }
7471 }
7472
7473 // Get initial condition
7477
7478 // Weak form of assignment of initial guess
7479
7480 // Loop over the number of node
7481 for (unsigned l = 0; l < n_node; l++)
7482 {
7483 // Loop over the type of degree of freedom
7484 for (unsigned k = 0; k < n_position_type; k++)
7485 {
7486 // Loop over the coordinate directions
7487 for (unsigned i = 0; i < nodal_dim; i++)
7488 {
7490
7491 // If it's not a boundary condition
7492 if (local_eqn >= 0)
7493 {
7494 // Note we're ignoring the mapping between local and
7495 // global Lagrangian coords -- doesn't matter here;
7496 // we're just enforcing a slightly different
7497 // weak form.
7499 (interpolated_x(s, i) - drdt_ic[i]) * psi(l, k) * w;
7500
7501
7502 // Do Jacobian too?
7503 if (flag == 1)
7504 {
7505 // Loop over the number of node
7506 for (unsigned ll = 0; ll < n_node; ll++)
7507 {
7508 // Loop over the type of degree of freedom
7509 for (unsigned kk = 0; kk < n_position_type; kk++)
7510 {
7511 // Only diagonal term
7512 unsigned ii = i;
7513
7515
7516 // If it's not a boundary condition
7517 if (local_unknown >= 0)
7518 {
7519 // Note we're ignoring the mapping between local and
7520 // global Lagrangian coords -- doesn't matter here;
7521 // we're just enforcing a slightly different
7522 // weak form.
7523 jacobian(local_eqn, local_unknown) +=
7524 psi(ll, kk) * psi(l, k) * w;
7525 }
7526 else
7527 {
7529 << "WARNING: You should really free all Data"
7530 << std::endl
7531 << " before setup of initial guess" << std::endl
7532 << "ll, kk, ii " << ll << " " << kk << " " << ii
7533 << std::endl;
7534 }
7535 }
7536 }
7537 }
7538 }
7539 else
7540 {
7541 oomph_info << "WARNING: You should really free all Data"
7542 << std::endl
7543 << " before setup of initial guess"
7544 << std::endl
7545 << "l, k, i " << l << " " << k << " " << i
7546 << std::endl;
7547 }
7548 }
7549 }
7550 }
7551
7552 } // End of loop over the integration points
7553 }
7554
7555
7556 //===============================================================
7557 /// Return the geometric shape function at the local coordinate s
7558 //===============================================================
7560 {
7561 // Single shape function always has value 1
7562 psi[0] = 1.0;
7563 }
7564
7565 //=======================================================================
7566 /// Assign the static Default_integration_scheme
7567 //=======================================================================
7569
7570
7571} // namespace oomph
static char t char * s
Definition cfortran.h:568
cstr elem_len * i
Definition cfortran.h:603
char t
Definition cfortran.h:568
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:5542
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:6036
int Normal_sign
Sign of outer unit normal (relative to cross-products of tangent vectors in the corresponding "bulk" ...
Definition elements.h:4378
BulkCoordinateDerivativesFctPt Bulk_coordinate_derivatives_fct_pt
Pointer to a function that returns the derivatives of the local "bulk" coordinates with respect to th...
Definition elements.h:4365
Vector< double > local_coordinate_in_bulk(const Vector< double > &s) const
Return vector of local coordinates in bulk element, given the local coordinates in this FaceElement.
Definition elements.cc:6383
Vector< double > * Tangent_direction_pt
A general direction pointer for the tangent vectors. This is used in the function continuous_tangent_...
Definition elements.h:4424
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:5272
static bool Ignore_discontinuous_tangent_warning
Ignores the warning when the tangent vectors from continuous_tangent_and_outer_unit_normal may not be...
Definition elements.h:4388
double J_eulerian_at_knot(const unsigned &ipt) const
Return the Jacobian of the mapping from local to global coordinates at the ipt-th integration point O...
Definition elements.cc:5358
void check_J_eulerian_at_knots(bool &passed) const
Check that Jacobian of mapping between local and Eulerian coordinates at all integration points is po...
Definition elements.cc:5440
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:6439
void get_local_coordinate_in_bulk(const Vector< double > &s, Vector< double > &s_bulk) const
Calculate the vector of local coordinate in the bulk element given the local coordinates in this Face...
Definition elements.cc:6414
CoordinateMappingFctPt Face_to_bulk_coordinate_fct_pt
Pointer to a function that translates the face coordinate to the coordinate in the bulk element.
Definition elements.h:4361
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:5230
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:5162
virtual void transform_second_derivatives(const DenseMatrix< double > &jacobian, const DenseMatrix< double > &inverse_jacobian, const DenseMatrix< double > &jacobian2, DShape &dbasis, DShape &d2basis) const
Convert derivatives and second derivatives w.r.t. local coordiantes to derivatives and second derivat...
Definition elements.cc:3155
virtual void describe_nodal_local_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the local dofs of the element[s]. The ostream specifies the output stream to whi...
Definition elements.cc:1755
unsigned nodal_dimension() const
Return the required Eulerian dimension of the nodes in this element.
Definition elements.h:2488
int ** Nodal_local_eqn
Storage for the local equation numbers associated with the values stored at the nodes.
Definition elements.h:1327
virtual ~FiniteElement()
The destructor cleans up the static memory allocated for shape function storage. Internal and externa...
Definition elements.cc:3203
virtual void get_x_from_macro_element(const Vector< double > &s, Vector< double > &x) const
Global coordinates as function of local coordinates using macro element representation....
Definition elements.h:1938
double raw_nodal_position(const unsigned &n, const unsigned &i) const
Return the i-th coordinate at local node n. Do not use the hanging node representation....
Definition elements.cc:1714
virtual void node_update()
Update the positions of all nodes in the element using each node update function. The default impleme...
Definition elements.cc:5102
virtual void write_tecplot_zone_footer(std::ostream &outfile, const unsigned &nplot) const
Add tecplot zone "footer" to output stream (when plotting nplot points in each "coordinate direction"...
Definition elements.h:3178
unsigned Nnode
Number of nodes in the element.
Definition elements.h:1330
virtual void set_integration_scheme(Integral *const &integral_pt)
Set the spatial integration scheme.
Definition elements.cc:3240
static const unsigned N2deriv[]
Static array that holds the number of second derivatives as a function of the dimension of the elemen...
Definition elements.h:1487
static bool Accept_negative_jacobian
Boolean that if set to true allows a negative jacobian in the transform between global and local coor...
Definition elements.h:1779
void(* UnsteadyExactSolutionFctPt)(const double &, const Vector< double > &, Vector< double > &)
Function pointer for function that computes Vector-valued time-dependent function as .
Definition elements.h:1769
virtual unsigned nnode_on_face() const
Definition elements.h:3391
void transform_derivatives_diagonal(const DenseMatrix< double > &inverse_jacobian, DShape &dbasis) const
Convert derivative w.r.t local coordinates to derivatives w.r.t the coordinates used to assemble the ...
Definition elements.cc:2907
double raw_nodal_position_gen(const unsigned &n, const unsigned &k, const unsigned &i) const
Return the value of the k-th type of the i-th positional variable at the local node n....
Definition elements.h:2276
virtual void get_dresidual_dnodal_coordinates(RankThreeTensor< double > &dresidual_dnodal_coordinates)
Compute derivatives of elemental residual vector with respect to nodal coordinates....
Definition elements.cc:3774
static bool Suppress_output_while_checking_for_inverted_elements
Static boolean to suppress output while checking for inverted elements.
Definition elements.h:1783
int get_node_number(Node *const &node_pt) const
Return the number of the node *node_pt if this node is in the element, else return -1;.
Definition elements.cc:3844
virtual void shape_at_knot(const unsigned &ipt, Shape &psi) const
Return the geometric shape function at the ipt-th integration point.
Definition elements.cc:3250
virtual double d2shape_eulerian_at_knot(const unsigned &ipt, Shape &psi, DShape &dpsidx, DShape &d2psidx) const
Return the geometric shape functions and also first and second derivatives w.r.t. global coordinates ...
Definition elements.cc:3532
virtual void identify_field_data_for_interactions(std::set< std::pair< Data *, unsigned > > &paired_field_data)
The purpose of this function is to identify all possible Data that can affect the fields interpolated...
Definition elements.cc:5126
virtual void dJ_eulerian_dnodal_coordinates(const DenseMatrix< double > &jacobian, const DShape &dpsids, DenseMatrix< double > &djacobian_dX) const
A template-free interface that calculates the derivative of the jacobian of a mapping with respect to...
Definition elements.cc:2699
virtual CoordinateMappingFctPt face_to_bulk_coordinate_fct_pt(const int &face_index) const
Get a pointer to the function mapping face coordinates to bulk coordinates.
Definition elements.h:3413
static const unsigned Default_Initial_Nvalue
Default return value for required_nvalue(n) which gives the number of "data" values required by the e...
Definition elements.h:1374
virtual unsigned self_test()
Self-test: Check inversion of element & do self-test for GeneralisedElement. Return 0 if OK.
Definition elements.cc:4470
void fill_in_jacobian_from_external_by_fd(Vector< double > &residuals, DenseMatrix< double > &jacobian, const bool &fd_all_data=false)
Calculate the contributions to the jacobian from the external degrees of freedom using finite differe...
Definition elements.cc:1227
unsigned nexternal_data() const
Return the number of external data objects.
Definition elements.h:833
virtual void reset_in_external_fd(const unsigned &i)
Function called within the finite difference loop for external data after the values in the i-th exte...
Definition elements.h:488
virtual void fill_in_contribution_to_mass_matrix(Vector< double > &residuals, DenseMatrix< double > &mass_matrix)
Add the elemental contribution to the mass matrix matrix. and the residuals vector....
Definition elements.cc:1320
virtual void update_in_internal_fd(const unsigned &i)
Function called within the finite difference loop for internal data after a change in any values in t...
Definition elements.h:459
bool is_halo() const
Is this element a halo?
Definition elements.h:1167
virtual void reset_after_external_fd()
Function that is call after the finite differencing of the external data. This may be overloaded to r...
Definition elements.h:478
virtual void update_before_external_fd()
Function that is called before the finite differencing of any external data. This may be overloaded t...
Definition elements.h:473
void assign_internal_eqn_numbers(unsigned long &global_number, Vector< double * > &Dof_pt)
Assign the global equation numbers to the internal Data. The arguments are the current highest global...
Definition elements.cc:534
static double Default_fd_jacobian_step
Double used for the default finite difference step in elemental jacobian calculations.
Definition elements.h:1202
static bool Suppress_warning_about_repeated_external_data
Static boolean to suppress warnings about repeated external data. Defaults to true.
Definition elements.h:704
bool internal_data_fd(const unsigned &i) const
Return the status of the boolean flag indicating whether the internal data is included in the finite ...
Definition elements.h:146
unsigned long * Eqn_number
Storage for the global equation numbers represented by the degrees of freedom in the element.
Definition elements.h:77
void read_internal_eqn_numbers_from_vector(const Vector< long > &vector_of_eqn_numbers, unsigned &index)
Read all equation numbers associated with internal data from the vector starting from index....
Definition elements.cc:675
virtual void fill_in_contribution_to_jacobian_and_mass_matrix(Vector< double > &residuals, DenseMatrix< double > &jacobian, DenseMatrix< double > &mass_matrix)
Add the elemental contribution to the jacobian matrix, mass matrix and the residuals vector....
Definition elements.cc:1350
virtual void fill_in_contribution_to_djacobian_and_dmass_matrix_dparameter(double *const &parameter_pt, Vector< double > &dres_dparam, DenseMatrix< double > &djac_dparam, DenseMatrix< double > &dmass_matrix_dparam)
Add the elemental contribution to the derivative of the jacobian matrix, mass matrix and the residual...
Definition elements.cc:1481
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:659
bool external_data_fd(const unsigned &i) const
Return the status of the boolean flag indicating whether the external data is included in the finite ...
Definition elements.h:761
virtual void assign_all_generic_local_eqn_numbers(const bool &store_local_dof_pt)
Assign all the local equation numbering schemes that can be applied generically for the element....
Definition elements.h:253
unsigned add_internal_data(Data *const &data_pt, const bool &fd=true)
Add a (pointer to an) internal data object to the element and return the index required to obtain it ...
Definition elements.cc:67
unsigned ndof() const
Return the number of equations/dofs in the element.
Definition elements.h:839
unsigned long eqn_number(const unsigned &ieqn_local) const
Return the global equation number corresponding to the ieqn_local-th local equation number.
Definition elements.h:708
int ** Data_local_eqn
Pointer to array storage for local equation numbers associated with internal and external data....
Definition elements.h:101
Data *& internal_data_pt(const unsigned &i)
Return a pointer to i-th internal data object.
Definition elements.h:622
int local_eqn_number(const unsigned long &ieqn_global) const
Return the local equation number corresponding to the ieqn_global-th global equation number....
Definition elements.h:730
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:984
virtual void fill_in_contribution_to_dresiduals_dparameter(double *const &parameter_pt, Vector< double > &dres_dparam)
Add the elemental contribution to the derivatives of the residuals with respect to a parameter....
Definition elements.cc:1386
static bool Suppress_warning_about_any_repeated_data
Static boolean to suppress warnings about repeated data. Defaults to false.
Definition elements.h:696
unsigned ninternal_data() const
Return the number of internal data objects.
Definition elements.h:827
int Non_halo_proc_ID
Non-halo processor ID for Data; -1 if it's not a halo.
Definition elements.h:128
void clear_global_eqn_numbers()
Clear the storage for the global equation numbers and pointers to dofs (if stored)
Definition elements.h:207
unsigned Ninternal_data
Number of internal data.
Definition elements.h:107
unsigned Nexternal_data
Number of external data.
Definition elements.h:110
void add_internal_value_pt_to_map(std::map< unsigned, double * > &map_of_value_pt)
Add pointers to the internal data values to map indexed by the global equation number.
Definition elements.cc:616
void add_global_eqn_numbers(std::deque< unsigned long > const &global_eqn_numbers, std::deque< double * > const &global_dof_pt)
Add the contents of the queue global_eqn_numbers to the local storage for the local-to-global transla...
Definition elements.cc:161
virtual void update_before_internal_fd()
Function that is called before the finite differencing of any internal data. This may be overloaded t...
Definition elements.h:449
virtual void 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:1155
void add_internal_eqn_numbers_to_vector(Vector< long > &vector_of_eqn_numbers)
Add all equation numbers associated with internal data to the vector in the internal storage order.
Definition elements.cc:660
virtual void reset_after_internal_fd()
Function that is call after the finite differencing of the internal data. This may be overloaded to r...
Definition elements.h:454
virtual unsigned self_test()
Self-test: Have all internal values been classified as pinned/unpinned? Return 0 if OK.
Definition elements.cc:1631
virtual void assign_local_eqn_numbers(const bool &store_local_dof_pt)
Setup the arrays of local equation numbers for the element. If the optional boolean argument is true,...
Definition elements.cc:696
Data ** Data_pt
Storage for pointers to internal and external data. The data is of the same type and so can be addres...
Definition elements.h:92
static bool Suppress_warning_about_repeated_internal_data
Static boolean to suppress warnings about repeated internal data. Defaults to false.
Definition elements.h:700
double ** Dof_pt
Storage for array of pointers to degrees of freedom ordered by local equation number....
Definition elements.h:84
int external_local_eqn(const unsigned &i, const unsigned &j)
Return the local equation number corresponding to the j-th value stored at the i-th external data.
Definition elements.h:311
void describe_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the dofs of the element. The ostream specifies the output stream to which the de...
Definition elements.cc:556
std::vector< bool > Data_fd
Storage for boolean flags of size Ninternal_data + Nexternal_data that correspond to the data used in...
Definition elements.h:122
void flush_external_data()
Flush all external data.
Definition elements.cc:392
void read_internal_data_values_from_vector(const Vector< double > &vector_of_values, unsigned &index)
Read all internal data and time history values from the vector starting from index....
Definition elements.cc:647
virtual void update_in_external_fd(const unsigned &i)
Function called within the finite difference loop for external data after a change in any values in t...
Definition elements.h:483
unsigned add_external_data(Data *const &data_pt, const bool &fd=true)
Add a (pointer to an) external data object to the element and return its index (i....
Definition elements.cc:312
/////////////////////////////////////////////////////////////////////
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.
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
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....
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
An OomphLibWarning object which should be created as a temporary object to issue a warning....
void shape(const Vector< double > &s, Shape &psi) const
Broken assignment operator.
Definition elements.cc:7559
static PointIntegral Default_integration_scheme
Return spatial dimension of element (=number of local coordinates needed to parametrise the element)
Definition elements.h:3477
Broken pseudo-integration scheme for points elements: Iit's not clear in general what this integratio...
Definition integral.h:89
A Class for shape functions. In simple cases, the shape functions have only one index that can be tho...
Definition shape.h:76
unsigned lagrangian_dimension() const
Return the number of Lagrangian coordinates that the element requires at all nodes....
Definition elements.h:3778
void describe_solid_local_dofs(std::ostream &out, const std::string &current_string) const
Classifies dofs locally for solid specific aspects.
Definition elements.cc:6904
double lagrangian_position_gen(const unsigned &n, const unsigned &k, const unsigned &i) const
Return Generalised Lagrangian coordinate at local node n. ‘Direction’ i, ‘Type’ k.
Definition elements.h:3916
double raw_lagrangian_position_gen(const unsigned &n, const unsigned &k, const unsigned &i) const
Return Generalised Lagrangian coordinate at local node n. ‘Direction’ i, ‘Type’ k....
Definition elements.h:3901
int * Position_local_eqn
Array to hold the local equation number information for the solid equations, whatever they may be.
Definition elements.h:4285
virtual void assemble_local_to_lagrangian_jacobian2(const DShape &d2psids, DenseMatrix< double > &jacobian2) const
Assemble the the "jacobian" matrix of second derivatives, given the second derivatives of the shape f...
Definition elements.cc:6616
virtual void assign_solid_local_eqn_numbers(const bool &store_local_dof)
Assigns local equation numbers for the generic solid local equation numbering schemes....
Definition elements.cc:6928
double dshape_lagrangian(const Vector< double > &s, Shape &psi, DShape &dpsidxi) const
Calculate shape functions and derivatives w.r.t. Lagrangian coordinates at local coordinate s....
Definition elements.cc:6740
unsigned nnodal_lagrangian_type() const
Return the number of types of (generalised) nodal Lagrangian coordinates required to interpolate the ...
Definition elements.h:3789
unsigned Lagrangian_dimension
The Lagrangian dimension of the nodes stored in the element, / i.e. the number of Lagrangian coordina...
Definition elements.h:4289
virtual void update_before_solid_position_fd()
Function that is called before the finite differencing of any solid position data....
Definition elements.h:4244
bool Solve_for_consistent_newmark_accel_flag
Flag to indicate which system of equations to solve when assigning initial conditions for time-depend...
Definition elements.h:4306
SolidInitialCondition * Solid_ic_pt
Pointer to object that specifies the initial condition.
Definition elements.h:4135
virtual double d2shape_lagrangian_at_knot(const unsigned &ipt, Shape &psi, DShape &dpsidxi, DShape &d2psidxi) const
Return the geometric shape functions and also first and second derivatives w.r.t. Lagrangian coordina...
Definition elements.cc:6862
void fill_in_generic_jacobian_for_solid_ic(Vector< double > &residuals, DenseMatrix< double > &jacobian, const unsigned &flag)
Helper function to fill in the residuals and (if flag==1) the Jacobian for the setup of an initial co...
Definition elements.cc:7406
virtual double local_to_lagrangian_mapping_diagonal(const DShape &dpsids, DenseMatrix< double > &jacobian, DenseMatrix< double > &inverse_jacobian) const
Calculate the mapping from local to Lagrangian coordinates given the derivatives of the shape functio...
Definition elements.cc:6673
virtual void update_in_solid_position_fd(const unsigned &i)
Function called within the finite difference loop for the solid position dat after a change in any va...
Definition elements.h:4254
int position_local_eqn(const unsigned &n, const unsigned &k, const unsigned &j) const
Access function that returns the local equation number that corresponds to the j-th coordinate of the...
Definition elements.h:4141
virtual double local_to_lagrangian_mapping(const DShape &dpsids, DenseMatrix< double > &jacobian, DenseMatrix< double > &inverse_jacobian) const
Calculate the mapping from local to lagrangian coordinates, given the derivatives of the shape functi...
Definition elements.h:4086
virtual void assemble_local_to_lagrangian_jacobian(const DShape &dpsids, DenseMatrix< double > &jacobian) const
Assemble the jacobian matrix for the mapping from local to lagrangian coordinates,...
Definition elements.cc:6558
virtual double interpolated_xi(const Vector< double > &s, const unsigned &i) const
Return i-th FE-interpolated Lagrangian coordinate xi[i] at local coordinate s.
Definition elements.cc:7134
virtual void reset_in_solid_position_fd(const unsigned &i)
Function called within the finite difference loop for solid position data after the values in the i-t...
Definition elements.h:4259
virtual void interpolated_dxids(const Vector< double > &s, DenseMatrix< double > &dxids) const
Compute derivatives of FE-interpolated Lagrangian coordinates xi with respect to local coordinates: d...
Definition elements.cc:7210
virtual double dshape_lagrangian_at_knot(const unsigned &ipt, Shape &psi, DShape &dpsidxi) const
Return the geometric shape functions and also first derivatives w.r.t. Lagrangian coordinates at ipt-...
Definition elements.cc:6767
virtual void fill_in_jacobian_from_solid_position_by_fd(Vector< double > &residuals, DenseMatrix< double > &jacobian)
Use finite differences to calculate the Jacobian entries corresponding to the solid positions....
Definition elements.cc:7015
double multiplier(const Vector< double > &xi)
Access to the "multiplier" for the inertia terms in the consistent determination of the initial condi...
Definition elements.h:4312
double d2shape_lagrangian(const Vector< double > &s, Shape &psi, DShape &dpsidxi, DShape &d2psidxi) const
Compute the geometric shape functions and also first and second derivatives w.r.t....
Definition elements.cc:6811
void compute_norm(double &el_norm)
Calculate the L2 norm of the displacement u=R-r to overload the compute_norm function in the Generali...
Definition elements.cc:6472
void describe_local_dofs(std::ostream &out, const std::string &current_string) const
Function to describe the local dofs of the element. The ostream specifies the output stream to which ...
Definition elements.cc:6544
virtual ~SolidFiniteElement()
Destructor to clean up any allocated memory.
Definition elements.cc:6659
void fill_in_jacobian_for_newmark_accel(DenseMatrix< double > &jacobian)
Fill in the contributions of the Jacobian matrix for the consistent assignment of the initial "accele...
Definition elements.cc:7257
virtual void reset_after_solid_position_fd()
Function that is call after the finite differencing of the solid position data. This may be overloade...
Definition elements.h:4249
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
//////////////////////////////////////////////////////////////////////
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...
////////////////////////////////////////////////////////////////////// //////////////////////////////...
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
//////////////////////////////////////////////////////////////////// ////////////////////////////////...
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...