tet_mesh.cc
Go to the documentation of this file.
1// LIC// ====================================================================
2// LIC// This file forms part of oomph-lib, the object-oriented,
3// LIC// multi-physics finite-element library, available
4// LIC// at http://www.oomph-lib.org.
5// LIC//
6// LIC// Copyright (C) 2006-2025 Matthias Heil and Andrew Hazel
7// LIC//
8// LIC// This library is free software; you can redistribute it and/or
9// LIC// modify it under the terms of the GNU Lesser General Public
10// LIC// License as published by the Free Software Foundation; either
11// LIC// version 2.1 of the License, or (at your option) any later version.
12// LIC//
13// LIC// This library is distributed in the hope that it will be useful,
14// LIC// but WITHOUT ANY WARRANTY; without even the implied warranty of
15// LIC// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16// LIC// Lesser General Public License for more details.
17// LIC//
18// LIC// You should have received a copy of the GNU Lesser General Public
19// LIC// License along with this library; if not, write to the Free Software
20// LIC// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
21// LIC// 02110-1301 USA.
22// LIC//
23// LIC// The authors may be contacted at oomph-lib@maths.man.ac.uk.
24// LIC//
25// LIC//====================================================================
26
27#include <algorithm>
28
29#include "map_matrix.h"
30#include "tet_mesh.h"
31#include "Telements.h"
32
33
34namespace oomph
35{
36 //=======================================================================
37 /// Constructor for a FacetedSurface created from a list of nodes
38 /// and connectivity information. This is used in remeshing
39 //=======================================================================
41 Vector<Node*> const& vertex_node_pt,
45 {
46 // Create the vertices
47 unsigned n_vertex = vertex_node_pt.size();
48 Vertex_pt.resize(n_vertex);
49 for (unsigned v = 0; v < n_vertex; ++v)
50 {
51 Vertex_pt[v] = new TetMeshVertex(vertex_node_pt[v]);
52 }
53
54 // Create the facets
55 unsigned n_facet = facet_connectivity.size();
56 Facet_pt.resize(n_facet);
57 for (unsigned f = 0; f < n_facet; ++f)
58 {
61 for (unsigned i = 0; i < n_vertex_on_facet; ++i)
62 {
63 Facet_pt[f]->set_vertex_pt(i, Vertex_pt[facet_connectivity[f][i]]);
64 }
65 // Add in the boundary id
66 Facet_pt[f]->set_one_based_boundary_id(facet_boundary_id[f]);
67 }
68 }
69
70 //=================================================================
71 /// Destructor. Delete allocated memory
72 //================================================================
74 {
75 // Delete the facets and the vertices
76 unsigned n_facet = this->nfacet();
77 for (unsigned f = 0; f < n_facet; f++)
78 {
79 delete Facet_pt[f];
80 }
81 unsigned n_vertex = this->nvertex();
82 for (unsigned v = 0; v < n_vertex; v++)
83 {
84 delete Vertex_pt[v];
85 }
86 }
87
88
89 //================================================================
90 /// Global static data that specifies the permitted
91 /// error in the setup of the boundary coordinates
92 //================================================================
94
95
96 //================================================================
97 /// Setup lookup schemes which establish which elements are located
98 /// next to which boundaries (Doc to outfile if it's open).
99 //================================================================
101 {
102 // Should we document the output here
103 bool doc = false;
104
105 if (outfile) doc = true;
106
107 // Number of boundaries
108 unsigned nbound = nboundary();
109
110 // Wipe/allocate storage for arrays
111 Boundary_element_pt.clear();
116
117 // Temporary vector of vectors of pointers to elements on the boundaries:
118 // This is a vector to ensure UNIQUE ordering in all processors
121
122 // Matrix map for working out the fixed face for elements on boundary
124
125 // Loop over elements
126 //-------------------
127 unsigned nel = nelement();
128
129
130 // Get pointer to vector of boundaries that the
131 // node lives on
133
134 for (unsigned e = 0; e < nel; e++)
135 {
136 // Get pointer to element
138
139 if (doc) outfile << "Element: " << e << " " << fe_pt << std::endl;
140
141 // Only include 3D elements! Some meshes contain interface elements too.
142 if (fe_pt->dim() == 3)
143 {
144 // Loop over the element's nodes and find out which boundaries they're
145 // on
146 // ----------------------------------------------------------------------
147 // We need only loop over the corner nodes
148 for (unsigned i = 0; i < 4; i++)
149 {
151 }
152
153 // Find the common boundaries of each face
155
156 // NOTE: Face indices defined in Telements.h
157
158 // Face 3 connnects points 0, 1 and 2
159 if (boundaries_pt[0] && boundaries_pt[1] && boundaries_pt[2])
160 {
161 std::set<unsigned> aux;
162
163 std::set_intersection(
164 boundaries_pt[0]->begin(),
165 boundaries_pt[0]->end(),
166 boundaries_pt[1]->begin(),
167 boundaries_pt[1]->end(),
168 std::insert_iterator<std::set<unsigned>>(aux, aux.begin()));
169
170 std::set_intersection(
171 aux.begin(),
172 aux.end(),
173 boundaries_pt[2]->begin(),
174 boundaries_pt[2]->end(),
175 std::insert_iterator<std::set<unsigned>>(face[3], face[3].begin()));
176 }
177
178 // Face 2 connects points 0, 1 and 3
179 if (boundaries_pt[0] && boundaries_pt[1] && boundaries_pt[3])
180 {
181 std::set<unsigned> aux;
182
183 std::set_intersection(
184 boundaries_pt[0]->begin(),
185 boundaries_pt[0]->end(),
186 boundaries_pt[1]->begin(),
187 boundaries_pt[1]->end(),
188 std::insert_iterator<std::set<unsigned>>(aux, aux.begin()));
189
190 std::set_intersection(
191 aux.begin(),
192 aux.end(),
193 boundaries_pt[3]->begin(),
194 boundaries_pt[3]->end(),
195 std::insert_iterator<std::set<unsigned>>(face[2], face[2].begin()));
196 }
197
198 // Face 1 connects points 0, 2 and 3
199 if (boundaries_pt[0] && boundaries_pt[2] && boundaries_pt[3])
200 {
201 std::set<unsigned> aux;
202
203 std::set_intersection(
204 boundaries_pt[0]->begin(),
205 boundaries_pt[0]->end(),
206 boundaries_pt[2]->begin(),
207 boundaries_pt[2]->end(),
208 std::insert_iterator<std::set<unsigned>>(aux, aux.begin()));
209
210 std::set_intersection(
211 aux.begin(),
212 aux.end(),
213 boundaries_pt[3]->begin(),
214 boundaries_pt[3]->end(),
215 std::insert_iterator<std::set<unsigned>>(face[1], face[1].begin()));
216 }
217
218 // Face 0 connects points 1, 2 and 3
219 if (boundaries_pt[1] && boundaries_pt[2] && boundaries_pt[3])
220 {
221 std::set<unsigned> aux;
222
223 std::set_intersection(
224 boundaries_pt[1]->begin(),
225 boundaries_pt[1]->end(),
226 boundaries_pt[2]->begin(),
227 boundaries_pt[2]->end(),
228 std::insert_iterator<std::set<unsigned>>(aux, aux.begin()));
229
230 std::set_intersection(
231 aux.begin(),
232 aux.end(),
233 boundaries_pt[3]->begin(),
234 boundaries_pt[3]->end(),
235 std::insert_iterator<std::set<unsigned>>(face[0], face[0].begin()));
236 }
237
238
239 // We now know whether any faces lay on the boundaries
240 for (unsigned i = 0; i < 4; i++)
241 {
242 // How many boundaries are there
243 unsigned count = 0;
244
245 // The number of the boundary
246 int boundary = -1;
247
248 // Loop over all the members of the set and add to the count
249 // and set the boundary
250 for (std::set<unsigned>::iterator it = face[i].begin();
251 it != face[i].end();
252 ++it)
253 {
254 ++count;
255 boundary = *it;
256 }
257
258 // If we're on more than one boundary, this is weird, so die
259 if (count > 1)
260 {
261 std::ostringstream error_stream;
263 error_stream << "Face " << i << " is on " << count
264 << " boundaries.\n";
265 error_stream << "This is rather strange.\n";
266 error_stream << "Your mesh may be too coarse or your tet mesh\n";
267 error_stream << "may be screwed up. I'm skipping the automated\n";
268 error_stream << "setup of the elements next to the boundaries\n";
269 error_stream << "lookup schemes.\n";
273 }
274
275 // If we have a boundary then add this to the appropriate set
276 if (boundary >= 0)
277 {
278 // Does the pointer already exits in the vector
280 vector_of_boundary_element_pt[static_cast<unsigned>(boundary)]
281 .begin(),
282 vector_of_boundary_element_pt[static_cast<unsigned>(boundary)]
283 .end(),
284 fe_pt);
285
286 // Only insert if we have not found it (i.e. got to the end)
287 if (b_el_it ==
288 vector_of_boundary_element_pt[static_cast<unsigned>(boundary)]
289 .end())
290 {
291 vector_of_boundary_element_pt[static_cast<unsigned>(boundary)]
293 }
294
295 // Also set the fixed face
296 face_identifier(static_cast<unsigned>(boundary), fe_pt) = i;
297 }
298 }
299
300 // Now we set the pointers to the boundary sets to zero
301 for (unsigned i = 0; i < 4; i++)
302 {
303 boundaries_pt[i] = 0;
304 }
305 }
306 }
307
308 // Now copy everything across into permanent arrays
309 //-------------------------------------------------
310
311 // Loop over boundaries
312 //---------------------
313 for (unsigned i = 0; i < nbound; i++)
314 {
315 // Number of elements on this boundary (currently stored in a set)
317
318 // Allocate storage for the coordinate identifiers
320
321 unsigned e_count = 0;
325 it++)
326 {
327 // Recover pointer to element
329
330 // Add to permanent storage
331 Boundary_element_pt[i].push_back(fe_pt);
332
334
335 // Increment counter
336 e_count++;
337 }
338 }
339
340
341 // Doc?
342 //-----
343 if (doc)
344 {
345 // Loop over boundaries
346 for (unsigned i = 0; i < nbound; i++)
347 {
348 unsigned nel = Boundary_element_pt[i].size();
349 outfile << "Boundary: " << i << " is adjacent to " << nel << " elements"
350 << std::endl;
351
352 // Loop over elements on given boundary
353 for (unsigned e = 0; e < nel; e++)
354 {
356 outfile << "Boundary element:" << fe_pt
357 << " Face index of boundary is "
358 << Face_index_at_boundary[i][e] << std::endl;
359 }
360 }
361 }
362
363
364 // Lookup scheme has now been setup yet
366 }
367
368 //========================================================================
369 /// Clear and regenerate the lookup schemes for bulk elements and their
370 /// corresponding face indices which are adjacent to mesh boundaries
371 //========================================================================
373 {
376
377 // cache number of boundaries and regions
378 const unsigned nbound = nboundary();
379 const unsigned nregion = Region_attribute.size();
380
383
384 for (unsigned b = 0; b < nbound; b++)
385 {
386 // Loop over elements next to that boundary
387 unsigned nel = this->nboundary_element(b);
388 for (unsigned e = 0; e < nel; e++)
389 {
391
392 // now search for it in each region
393 for (unsigned r_index = 0; r_index < nregion; r_index++)
394 {
395 unsigned region_id = static_cast<unsigned>(Region_attribute[r_index]);
396
398 std::find(Region_element_pt[r_index].begin(),
400 el_pt);
401
402 // if we find this element in the current region, then update our
403 // lookups
405 {
407
408 unsigned face_index = face_index_at_boundary(b, e);
409 Face_index_region_at_boundary[b][region_id].push_back(face_index);
410 }
411 }
412 }
413 }
414 }
415
416 //========================================================================
417 /// Add an element to a particular region; this helper checks if the
418 /// specified element and region ID already exist, so can be used to move
419 /// an existing element to an existing region, to add an existing element
420 /// to a new region, or to add a new element to a new region
421 //========================================================================
423 const unsigned& region_id)
424 {
425 // number of regions
426 const unsigned nregion = Region_element_pt.size();
427
428 // the index of the requested region in our vector of region IDs;
429 // this is either the index of an existing region, or the index of
430 // a new region which is to be added
431 unsigned region_index = 0;
432
433 // look for this region in our current list
435 std::find(Region_attribute.begin(),
436 Region_attribute.end(),
437 static_cast<double>(region_id));
438
439 // did we find it?
440 if (region_it != Region_attribute.end())
441 {
442 // if so get the vector index
444 }
445 else
446 {
447 // otherwise we're making a new one; the index is the
448 // current size of the vector, and then we'll add the ID to the end
450 Region_attribute.push_back(static_cast<double>(region_id));
451 }
452
453 // is this an element already in the mesh?
454 if (std::find(this->Element_pt.begin(), this->Element_pt.end(), elem_pt) ==
455 this->Element_pt.end())
456 {
457 // if it isn't, then we'll first add it
458 this->add_element_pt(elem_pt);
459 }
460 else
461 {
462 // if this element already exists then we'll search for it
463 // in the region element list - regions can't overlap, so we
464 // must remove this element from its original region in addition to
465 // adding it to the newly requested region
466
467 unsigned original_region_index = 0;
468
469 // has this element already been assigned to a region?
470 bool found = false;
471
473
474 // loop over the regions and look for this original corner element to
475 // find out which region it used to be in, so we can add the new elements
476 // to the same region.
477 for (unsigned r = 0; r < nregion; r++)
478 {
479 // for each region, search the vector of elements in this region for
480 // the original corner element
481 region_element_it = std::find(
483
484 // if the iterator hasn't reached the end then we've found it
486 {
487 // save the region index we're at
489
490 // update the paranoid flag
491 found = true;
492
493 // regions can't overlap, so once we've found one we're done
494 break;
495 }
496 }
497
498 // if it was previously assigned to a region, then remove it
499 if (found)
500 {
501 // Now update the basic region lookup. The iterator will still
502 // point to the original corner element in the lookup, so we can
503 // delete this easily
505 }
506 }
507
508 // if we're adding a new region then the new index will be
509 // equal to the length of the current vector of region elements
510 if (region_index == nregion)
511 {
512 // add the new element as a new entry
515 }
516 else
517 {
518 // otherwise just add the element to an existing vector
520 }
521 }
522
523
524 //======================================================================
525 /// Assess mesh quality: Ratio of max. edge length to min. height,
526 /// so if it's very large it's BAAAAAD.
527 //======================================================================
529 {
531 for (unsigned e = 0; e < 6; e++)
532 {
533 edge[e].resize(3);
534 }
535 unsigned nel = this->nelement();
536 for (unsigned e = 0; e < nel; e++)
537 {
539 for (unsigned i = 0; i < 3; i++)
540 {
541 edge[0][i] = fe_pt->node_pt(2)->x(i) - fe_pt->node_pt(1)->x(i);
542 edge[1][i] = fe_pt->node_pt(0)->x(i) - fe_pt->node_pt(2)->x(i);
543 edge[2][i] = fe_pt->node_pt(1)->x(i) - fe_pt->node_pt(0)->x(i);
544 edge[3][i] = fe_pt->node_pt(3)->x(i) - fe_pt->node_pt(0)->x(i);
545 edge[4][i] = fe_pt->node_pt(3)->x(i) - fe_pt->node_pt(1)->x(i);
546 edge[5][i] = fe_pt->node_pt(3)->x(i) - fe_pt->node_pt(2)->x(i);
547 }
548
549 double max_length = 0.0;
550 for (unsigned j = 0; j < 6; j++)
551 {
552 double length = 0.0;
553 for (unsigned i = 0; i < 3; i++)
554 {
555 length += edge[j][i] * edge[j][i];
556 }
557 length = sqrt(length);
559 }
560
561
562 double min_height = DBL_MAX;
563 for (unsigned j = 0; j < 4; j++)
564 {
566 unsigned e0 = 0;
567 unsigned e1 = 0;
568 unsigned e2 = 0;
569 switch (j)
570 {
571 case 0:
572 e0 = 4;
573 e1 = 5;
574 e2 = 1;
575 break;
576
577 case 1:
578 e0 = 1;
579 e1 = 3;
580 e2 = 2;
581 break;
582
583 case 2:
584 e0 = 3;
585 e1 = 4;
586 e2 = 1;
587 break;
588
589 case 3:
590 e0 = 1;
591 e1 = 2;
592 e2 = 3;
593 break;
594
595 default:
596
597 oomph_info << "never get here\n";
598 abort();
599 }
600
601 normal[0] = edge[e0][1] * edge[e1][2] - edge[e0][2] * edge[e1][1];
602 normal[1] = edge[e0][2] * edge[e1][0] - edge[e0][0] * edge[e1][2];
603 normal[2] = edge[e0][0] * edge[e1][1] - edge[e0][1] * edge[e1][0];
604 double norm =
605 normal[0] * normal[0] + normal[1] * normal[1] + normal[2] * normal[2];
606 double inv_norm = 1.0 / sqrt(norm);
607 normal[0] *= inv_norm;
608 normal[1] *= inv_norm;
609 normal[2] *= inv_norm;
610
611 double height = fabs(edge[e2][0] * normal[0] + edge[e2][1] * normal[1] +
612 edge[e2][2] * normal[2]);
613
614 if (height < min_height) min_height = height;
615 }
616
618
619 some_file << "ZONE N=4, E=1, F=FEPOINT, ET=TETRAHEDRON\n";
620 for (unsigned j = 0; j < 4; j++)
621 {
622 for (unsigned i = 0; i < 3; i++)
623 {
624 some_file << fe_pt->node_pt(j)->x(i) << " ";
625 }
626 some_file << aspect_ratio << std::endl;
627 }
628 some_file << "1 2 3 4" << std::endl;
629 }
630 }
631
632
633 //======================================================================
634 /// Move the nodes on boundaries with associated Geometric Objects (if any)
635 /// so that they exactly coincide with the geometric object. This requires
636 /// that the boundary coordinates are set up consistently
637 //======================================================================
639 {
640 // Backup in case elements get inverted
641 std::map<Node*, Vector<double>> old_nodal_posn;
642 std::map<Node*, Vector<double>> new_nodal_posn;
643 unsigned nnod = nnode();
644 for (unsigned j = 0; j < nnod; j++)
645 {
646 Node* nod_pt = node_pt(j);
647 Vector<double> x(3);
648 nod_pt->position(x);
650 }
651
652 // Loop over all boundaries
653 unsigned n_bound = this->nboundary();
654 for (unsigned b = 0; b < n_bound; b++)
655 {
656 bool do_it = true;
657
658 // Accumulate reason for not snapping
659 std::stringstream reason;
660 reason << "Can't snap nodes on boundary " << b
661 << " onto geom object because: \n";
662
664 std::map<unsigned, TetMeshFacetedSurface*>::iterator it =
666 if (it != Tet_mesh_faceted_surface_pt.end())
667 {
668 faceted_surface_pt = (*it).second;
669 }
670
671 // Facet associated with this boundary?
672 if (faceted_surface_pt == 0)
673 {
674 reason << "-- no facets asssociated with boundary\n";
675 do_it = false;
676 }
677
678 // Get geom object associated with facet
680 if (do_it)
681 {
682 geom_obj_pt = faceted_surface_pt->geom_object_with_boundaries_pt();
683 if (geom_obj_pt == 0)
684 {
685 reason << "-- no geom object associated with boundary\n";
686 do_it = false;
687 }
688 }
689
690 // Triangular facet?
692 {
693 reason << "-- facet has to be triangular and vertex coordinates have\n"
694 << " to have been set up\n";
695 do_it = false;
696 }
697
698 // We need boundary coordinates!
700 {
701 reason << "-- no boundary coordinates were set up\n";
702 do_it = false;
703 }
704
705
706 // Which facet is associated with this boundary?
707 unsigned facet_id_of_boundary = 0;
708 TetMeshFacet* f_pt = 0;
709 if (do_it)
710 {
711 unsigned nf = faceted_surface_pt->nfacet();
712 for (unsigned f = 0; f < nf; f++)
713 {
714 if ((faceted_surface_pt->one_based_facet_boundary_id(f) - 1) == b)
715 {
717 break;
718 }
719 }
721
722
723 // Three vertices?
724 unsigned nv = f_pt->nvertex();
725 if (nv != 3)
726 {
727 reason << "-- number of facet vertices is " << nv
728 << " rather than 3\n";
729 do_it = false;
730 }
731
732 // Have we set up zeta coordinates in geometric object?
733 if ((f_pt->vertex_pt(0)->zeta_in_geom_object().size() != 2) ||
734 (f_pt->vertex_pt(1)->zeta_in_geom_object().size() != 2) ||
735 (f_pt->vertex_pt(2)->zeta_in_geom_object().size() != 2))
736 {
737 reason << "-- no boundary coordinates were set up\n";
738 do_it = false;
739 }
740 }
741
742
743 // Are we ready to go?
744 if (!do_it)
745 {
746 const bool tell_us_why = false;
747 if (tell_us_why)
748 {
749 oomph_info << reason.str() << std::endl;
750 }
751 }
752 else
753 {
754 // Setup area coordinantes in triangular facet
757
760
763
764 double detT = (y2 - y3) * (x1 - x3) + (x3 - x2) * (y1 - y3);
765
766
767 // Boundary coordinate (cartesian coordinates inside facet)
769
770 // Loop over all nodes on that boundary
771 const unsigned n_boundary_node = this->nboundary_node(b);
772 for (unsigned n = 0; n < n_boundary_node; ++n)
773 {
774 // Get the boundary node and coordinates
775 Node* const nod_pt = this->boundary_node_pt(b, n);
776 nod_pt->get_coordinates_on_boundary(b, zeta);
777
778 // Now we have zeta, the cartesian boundary coordinates
779 // in the (assumed to be triangular) boundary facet; let's
780 // work out the area coordinates
781 // Notation as in
782 // https://en.wikipedia.org/wiki/Barycentric_coordinate_system
783 double s0 =
784 ((y2 - y3) * (zeta[0] - x3) + (x3 - x2) * (zeta[1] - y3)) / detT;
785 double s1 =
786 ((y3 - y1) * (zeta[0] - x3) + (x1 - x3) * (zeta[1] - y3)) / detT;
787 double s2 = 1.0 - s0 - s1;
788
791
792 // Vertex zeta coordinates
794 zeta_0 = f_pt->vertex_pt(0)->zeta_in_geom_object();
795
797 zeta_1 = f_pt->vertex_pt(1)->zeta_in_geom_object();
798
800 zeta_2 = f_pt->vertex_pt(2)->zeta_in_geom_object();
801
802
803#ifdef PARANOID
804
805 // Compute zeta values of the vertices from parametrisation of
806 // boundaries
807 double tol = 1.0e-12;
810 for (unsigned v = 0; v < 3; v++)
811 {
812 zeta_vertex = f_pt->vertex_pt(v)->zeta_in_geom_object();
813 for (unsigned alt = 0; alt < 2; alt++)
814 {
815 switch (v)
816 {
817 case 0:
818
819 if (alt == 0)
820 {
821 faceted_surface_pt->boundary_zeta01(
823 }
824 else
825 {
826 faceted_surface_pt->boundary_zeta20(
828 }
829 break;
830
831 case 1:
832
833 if (alt == 0)
834 {
835 faceted_surface_pt->boundary_zeta01(
837 }
838 else
839 {
840 faceted_surface_pt->boundary_zeta12(
842 }
843 break;
844
845 case 2:
846
847 if (alt == 0)
848 {
849 faceted_surface_pt->boundary_zeta12(
851 }
852 else
853 {
854 faceted_surface_pt->boundary_zeta20(
856 }
857 break;
858 }
859
860 double error =
861 sqrt(pow((zeta_vertex[0] - zeta_from_boundary[0]), 2) +
862 pow((zeta_vertex[1] - zeta_from_boundary[1]), 2));
863 if (error > tol)
864 {
865 std::ostringstream error_message;
866 error_message
867 << "Error in parametrisation of boundary coordinates \n"
868 << "for vertex " << v << " [alt=" << alt << "] in facet "
869 << facet_id_of_boundary << " : \n"
870 << "zeta_vertex = [ " << zeta_vertex[0] << " "
871 << zeta_vertex[1] << " ] \n"
872 << "zeta_from_boundary = [ " << zeta_from_boundary[0]
873 << " " << zeta_from_boundary[1] << " ] \n"
874 << std::endl;
875 throw OomphLibError(error_message.str(),
878 }
879 }
880 }
881
882#endif
883
884 // Compute zeta values of the interpolation parameters
885 Vector<double> zeta_a(3, 0.0);
886 Vector<double> zeta_b(3, 0.0);
887 Vector<double> zeta_c(3, 0.0);
888 Vector<double> zeta_d(3, 0.0);
889 Vector<double> zeta_e(3, 0.0);
890 Vector<double> zeta_f(3, 0.0);
892 faceted_surface_pt->boundary_zeta01(
894
896 faceted_surface_pt->boundary_zeta12(
898
899 faceted_surface_pt->boundary_zeta20(
902
903 // Transfinite mapping
904 zeta_in_geom_obj[0] = s0 * (zeta_a[0] + zeta_b[0] - zeta_0[0]) +
905 s1 * (zeta_c[0] + zeta_d[0] - zeta_1[0]) +
906 s2 * (zeta_e[0] + zeta_f[0] - zeta_2[0]);
907 zeta_in_geom_obj[1] = s0 * (zeta_a[1] + zeta_b[1] - zeta_0[1]) +
908 s1 * (zeta_c[1] + zeta_d[1] - zeta_1[1]) +
909 s2 * (zeta_e[1] + zeta_f[1] - zeta_2[1]);
910
911 unsigned n_tvalues =
912 1 + nod_pt->position_time_stepper_pt()->nprev_values();
913 for (unsigned t = 0; t < n_tvalues; ++t)
914 {
915 // Get the position according to the underlying geometric object
917
918 // Move the node
919 for (unsigned i = 0; i < 3; i++)
920 {
922 }
923 }
924 }
925 }
926 }
927
928 // Check if any element is inverted
929 bool some_element_is_inverted = false;
930 unsigned count = 0;
931 unsigned nel = nelement();
932 for (unsigned e = 0; e < nel; e++)
933 {
935 bool passed = true;
937 if (!passed)
938 {
940 char filename[100];
941 std::ofstream some_file;
942 snprintf(
943 filename, sizeof(filename), "overly_distorted_element%i.dat", count);
944 some_file.open(filename);
945 unsigned nnod_1d = el_pt->nnode_1d();
947 some_file.close();
948
949 // Reset to old nodal position
950 unsigned nnod = el_pt->nnode();
951 for (unsigned j = 0; j < nnod; j++)
952 {
958 for (unsigned i = 0; i < 3; i++)
959 {
960 nod_pt->x(i) = old_x[i];
961 }
962 }
963
964 // Plot
966 sizeof(filename),
967 "orig_overly_distorted_element%i.dat",
968 count);
969 some_file.open(filename);
971 some_file.close();
972
973 // Reset
974 for (unsigned j = 0; j < nnod; j++)
975 {
977 for (unsigned i = 0; i < 3; i++)
978 {
980 }
981 }
982
983 // Bump
984 count++;
985 }
986 }
988 {
989 std::ostringstream error_message;
990 error_message
991 << "A number of elements, namely: " << count
992 << " are inverted after snapping. Their shapes are in "
993 << " overly_distorted_element*.dat and "
994 "orig_overly_distorted_element*.dat"
995 << "Next person to get this error: Please implement a straightforward\n"
996 << "variant of one of the functors in src/mesh_smoothing to switch\n"
997 << "to harmonic mapping\n"
998 << std::endl;
999 throw OomphLibError(
1000 error_message.str(), OOMPH_CURRENT_FUNCTION, OOMPH_EXCEPTION_LOCATION);
1001 }
1002 else
1003 {
1004 oomph_info << "No elements are inverted after snapping. Yay!"
1005 << std::endl;
1006 }
1007 }
1008
1009
1010 /////////////////////////////////////////////////////////////////////
1011 /////////////////////////////////////////////////////////////////////
1012 /////////////////////////////////////////////////////////////////////
1013
1014
1015} // namespace oomph
e
Definition cfortran.h:571
cstr elem_len * i
Definition cfortran.h:603
char t
Definition cfortran.h:568
A general Finite Element class.
Definition elements.h:1317
void position(const Vector< double > &zeta, Vector< double > &r) const
Return the parametrised position of the FiniteElement in its incarnation as a GeomObject,...
Definition elements.h:2680
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
unsigned dim() const
Return the spatial dimension of the element, i.e. the number of local coordinates required to paramet...
Definition elements.h:2615
unsigned nnode() const
Return the number of nodes.
Definition elements.h:2214
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition elements.h:2179
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 unsigned nnode_1d() const
Return the number of nodes along one edge of the element Default is to return zero — must be overload...
Definition elements.h:2222
A geometric object is an object that provides a parametrised description of its shape via the functio...
unsigned long nboundary_node(const unsigned &ibound) const
Return number of nodes on a particular boundary.
Definition mesh.h:841
Vector< Vector< FiniteElement * > > Boundary_element_pt
Vector of Vector of pointers to elements on the boundaries: Boundary_element_pt(b,...
Definition mesh.h:91
bool Lookup_for_elements_next_boundary_is_setup
Flag to indicate that the lookup schemes for elements that are adjacent to the boundaries has been se...
Definition mesh.h:87
FiniteElement * finite_element_pt(const unsigned &e) const
Upcast (downcast?) to FiniteElement (needed to access FiniteElement member functions).
Definition mesh.h:477
int face_index_at_boundary(const unsigned &b, const unsigned &e) const
For the e-th finite element on boundary b, return int to indicate the face_index of the face adjacent...
Definition mesh.h:904
Vector< Vector< int > > Face_index_at_boundary
For the e-th finite element on boundary b, this is the index of the face that lies along that boundar...
Definition mesh.h:95
unsigned nboundary_element(const unsigned &b) const
Return number of finite elements that are adjacent to boundary b.
Definition mesh.h:886
Node *& node_pt(const unsigned long &n)
Return pointer to global node n.
Definition mesh.h:440
FiniteElement * boundary_element_pt(const unsigned &b, const unsigned &e) const
Return pointer to e-th finite element on boundary b.
Definition mesh.h:848
unsigned nboundary() const
Return number of boundaries.
Definition mesh.h:835
unsigned long nnode() const
Return number of nodes in the mesh.
Definition mesh.h:604
void add_element_pt(GeneralisedElement *const &element_pt)
Add a (pointer to) an element to the mesh.
Definition mesh.h:625
Vector< GeneralisedElement * > Element_pt
Vector of pointers to generalised elements.
Definition mesh.h:186
bool boundary_coordinate_exists(const unsigned &i) const
Indicate whether the i-th boundary has an intrinsic coordinate.
Definition mesh.h:571
unsigned long nelement() const
Return number of elements in the mesh.
Definition mesh.h:598
Node *& boundary_node_pt(const unsigned &b, const unsigned &n)
Return pointer to node n on boundary b.
Definition mesh.h:497
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
virtual void get_boundaries_pt(std::set< unsigned > *&boundaries_pt)
Return a pointer to set of mesh boundaries that this node occupies; this will be overloaded by Bounda...
Definition nodes.h:1365
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition nodes.h:1060
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....
TAdvectionDiffusionReactionElement<NREAGENT,DIM,NNODE_1D> elements are isoparametric triangular DIM-d...
void output(std::ostream &outfile)
Output function: x,y,u or x,y,z,u.
void snap_nodes_onto_geometric_objects()
Move the nodes on boundaries with associated GeomObjects so that they exactly coincide with the geome...
Definition tet_mesh.cc:638
Vector< double > Region_attribute
Vector of attributes associated with the elements in each region NOTE: double is enforced on us by te...
Definition tet_mesh.h:1224
unsigned nregion()
Return the number of regions specified by attributes.
Definition tet_mesh.h:1057
std::map< unsigned, TetMeshFacetedSurface * > Tet_mesh_faceted_surface_pt
Reverse lookup scheme: Pointer to faceted surface (if any!) associated with boundary b.
Definition tet_mesh.h:1242
Vector< std::map< unsigned, Vector< FiniteElement * > > > Boundary_region_element_pt
Storage for elements adjacent to a boundary in a particular region.
Definition tet_mesh.h:1228
static double Tolerance_for_boundary_finding
Global static data that specifies the permitted error in the setup of the boundary coordinates.
Definition tet_mesh.h:863
void assess_mesh_quality(std::ofstream &some_file)
Assess mesh quality: Ratio of max. edge length to min. height, so if it's very large it's BAAAAAD.
Definition tet_mesh.cc:528
void add_element_in_region_pt(FiniteElement *const &elem_pt, const unsigned &region_id)
Add an element to a particular region; this helper checks if the specified element and region ID alre...
Definition tet_mesh.cc:422
void setup_boundary_element_info()
Setup lookup schemes which establish which elements are located next to mesh's boundaries (wrapper to...
Definition tet_mesh.h:1204
Vector< std::map< unsigned, Vector< int > > > Face_index_region_at_boundary
Storage for the face index adjacent to a boundary in a particular region.
Definition tet_mesh.h:1232
std::map< unsigned, Vector< Vector< double > > > Triangular_facet_vertex_boundary_coordinate
Boundary coordinates of vertices in triangular facets associated with given boundary....
Definition tet_mesh.h:1251
void regenerate_region_boundary_lookups()
Clear and regenerate the lookup schemes for bulk elements and their corresponding face indices which ...
Definition tet_mesh.cc:372
Vector< Vector< FiniteElement * > > Region_element_pt
Vectors of vectors of elements in each region (note: this just stores them; the region IDs are contai...
Definition tet_mesh.h:1219
Facet for Tet mesh generation. Can lie on boundary (identified via one-based enumeration!...
Definition tet_mesh.h:168
virtual ~TetMeshFacetedClosedSurfaceForRemesh()
Destructor. Delete allocated memory.
Definition tet_mesh.cc:73
TetMeshFacetedClosedSurfaceForRemesh(Vector< Node * > const &vertex_node_pt, Vector< Vector< unsigned > > const &facet_connectivity, Vector< unsigned > const &facet_boundary_id)
Constructor for a FacetedSurface created from a list of nodes and connectivity information....
Definition tet_mesh.cc:40
Base class for closed tet mesh boundary bounded by polygonal planar facets.
Definition tet_mesh.h:724
Base class for tet mesh boundary defined by polygonal planar facets.
Definition tet_mesh.h:306
Vector< TetMeshVertex * > Vertex_pt
Vector pointers to vertices.
Definition tet_mesh.h:669
unsigned nfacet() const
Number of facets.
Definition tet_mesh.h:325
Vector< TetMeshFacet * > Facet_pt
Vector of pointers to facets.
Definition tet_mesh.h:672
unsigned nvertex() const
Number of vertices.
Definition tet_mesh.h:319
Vertex for Tet mesh generation. Can lie on multiple boundaries (identified via one-based enumeration!...
Definition tet_mesh.h:50
A slight extension to the standard template vector class so that we can include "graceful" array rang...
Definition Vector.h:58
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).
OomphInfo oomph_info
Single (global) instantiation of the OomphInfo object – this is used throughout the library as a "rep...