refineable_tetgen_mesh.template.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#ifndef OOMPH_REFINEABLE_TETGEN_MESH_TEMPLATE_HEADER
28#define OOMPH_REFINEABLE_TETGEN_MESH_TEMPLATE_HEADER
29
30#ifndef OOMPH_REFINEABLE_TETGEN_MESH_HEADER
31#error __FILE__ should only be included from refineable_tetgen_mesh.h.
32#endif // OOMPH_REFINEABLE_TETGEN_MESH_HEADER
33
34// Config header
35#ifdef HAVE_CONFIG_H
36#include <oomph-lib-config.h>
37#endif
38
39// OOMPH-LIB Headers
40
43#include "generic/projection.h"
44
45namespace oomph
46{
47 /// Helper function that updates the input faceted surface
48 /// by using the flattened elements from FaceMesh(es) that are
49 /// constructed for the boundaries associated with the segments of the
50 /// polygon.
51 template<class ELEMENT>
54 {
55 // The easiest thing to do is simply to update the
56 // positions of the key control nodes, leaving the connectivity alone,
57 // but that doesn't allow for any surface remeshing
58
59 /// List of vertex nodes
60 std::list<Node*> new_vertex_nodes;
61 // List of facets and boundary ids
62 std::list<std::pair<Vector<unsigned>, unsigned>> new_facet_list;
63
64 // Loop over the number of old facets
65 unsigned n_old_facet = faceted_surface_pt->nfacet();
66 for (unsigned f = 0; f < n_old_facet; f++)
67 {
68 // Get the boundary id of the facet. Need to subtract one,
69 // which is confusing now I think about it.
70 // ALH: Should fix this.
71 unsigned bound = faceted_surface_pt->one_based_facet_boundary_id(f) - 1;
72
73 // Create a face mesh adjacent to the fluid mesh's bound-th boundary.
74 // The face mesh consists of FaceElements that may also be
75 // interpreted as GeomObjects
76 Mesh* face_mesh_pt = new Mesh;
78 bound, face_mesh_pt);
79
80 // Loop over these new face elements and tell them the boundary number
81 // from the bulk fluid mesh -- this is required to they can
82 // get access to the boundary coordinates!
83 unsigned n_face_element = face_mesh_pt->nelement();
84 for (unsigned e = 0; e < n_face_element; e++)
85 {
86 // Cast the element pointer to the correct thing!
89 face_mesh_pt->element_pt(e));
90
91 // Set bulk boundary number
92 el_pt->set_boundary_number_in_bulk_mesh(bound);
93 }
94
95 // In order to set up the new faceted representation
96 // Need to know the positions of the corner nodes
97 // and the connectivity
98
99 // Storage for the connectivity information
101
102 // Now we have the face mesh loop over the face elements and
103 // print out the end points
104 for (unsigned e = 0; e < n_face_element; ++e)
105 {
106 // Cache pointer to the element
107 FiniteElement const* elem_pt = face_mesh_pt->finite_element_pt(e);
108
109 // Just use the three primary (corner) nodes to define the facet
110 unsigned n_vertex_node = 3;
111 for (unsigned n = 0; n < n_vertex_node; n++)
112 {
113 // Cache the pointer to the node
114 Node* const nod_pt = elem_pt->node_pt(n);
115 // If the vertex node is in the list return the number
116 unsigned counter = 0;
117 bool found_it = false;
118 for (std::list<Node*>::iterator it = new_vertex_nodes.begin();
119 it != new_vertex_nodes.end();
120 ++it, ++counter)
121 {
122 // If we have found the node then store it
123 if (*it == nod_pt)
124 {
126 found_it = true;
127 break;
128 }
129 }
130
131 // If we haven't found it
132 // then add the node to the list and fill in the counter
133 if (!found_it)
134 {
135 new_vertex_nodes.push_back(nod_pt);
136 // We know where it is
138 }
139 }
140
141 // Add the new facet connectivity to the list
142 new_facet_list.push_back(std::make_pair(new_local_facet, bound + 1));
143 }
144 } // End of loop over old facets
145
146
147 // Probably want the surface mesh in a better data structure so
148 // that we can perform refinement or coarsening on it
149 // i.e. want neighbours, edge flips all that fun stuff
150 // That will go here!
151
152 // Now we need to put the information into the appropriate data structures
153 // for the Surface
154
155 // Now sort out the facet nodes
157 // Vector<Vector<double> > facet_point(n_facet_vertex);
159 unsigned count = 0;
160 for (std::list<Node*>::iterator it = new_vertex_nodes.begin();
161 it != new_vertex_nodes.end();
162 ++it)
163 {
165 // facet_point[count].resize(3);
166 // for(unsigned i=0;i<3;i++)
167 // {
168 // facet_point[count][i] = (*it)->x(i);
169 // }
170 ++count;
171 }
172
173 // And also the facets
174 unsigned n_facet = new_facet_list.size();
177 count = 0;
178 for (std::list<std::pair<Vector<unsigned>, unsigned>>::iterator it =
179 new_facet_list.begin();
180 it != new_facet_list.end();
181 ++it)
182 {
183 new_facet[count] = (*it).first;
184 new_facet_boundary_id[count] = (*it).second;
185 ++count;
186 }
187
188 for (unsigned f = 0; f < n_facet; f++)
189 {
190 for (unsigned i = 0; i < 3; i++)
191 {
192 oomph_info << new_facet[f][i] << " ";
193 }
194 oomph_info << " : ";
195 oomph_info << new_facet_boundary_id[f] << "\n";
196 }
197
198 // Now just create the new boundary
199 delete faceted_surface_pt;
202
203 // Also need to setup the reverse look-up scheme again
204 this->setup_reverse_lookup_schemes_for_faceted_surface(faceted_surface_pt);
205
206 // Take average to get a new hole position (Won't always work)
208 for (unsigned n = 0; n < n_facet_vertex; n++)
209 {
210 for (unsigned i = 0; i < 3; i++)
211 {
212 inner_point[i] += facet_nodes_pt[n]->x(i);
213 }
214 }
215
216 for (unsigned i = 0; i < 3; i++)
217 {
219 }
220
221 // Now set the hole if required
223 ->set_hole_for_tetgen(inner_point);
224 }
225
226
227 /// Generate a new faceted representation of the inner hole
228 /// boundaries
229 template<class ELEMENT>
231 {
232 // Loop over the number of internal boundarys
233 unsigned n_hole = this->Internal_surface_pt.size();
234 for (unsigned ihole = 0; ihole < n_hole; ihole++)
235 {
236 // Now can the surface update its own representation goes in here
237
238 // If not we have to generate it from the new mesh
239 {
240 // Update the faceted surface associated with the ihole-th hole
241 this->update_faceted_surface_using_face_mesh(
242 this->Internal_surface_pt[ihole]);
243 }
244 }
245 }
246
247 /// Snap the boundary nodes onto any curvilinear boundaries
248 template<class ELEMENT>
250 RefineableTetgenMesh<ELEMENT>*& new_mesh_pt, const unsigned& b)
251 {
252 // Quick return
253 if (!boundary_coordinate_exists(b))
254 {
255 oomph_info << "Not snapping nodes on boundary " << b
256 << " because no boundary coordinate has been set up.\n";
257 return;
258 }
259
260 // Firstly we set the boundary coordinates of the new nodes
261 // In case the mapping between the geometric object's intrinsic coordiante
262 // and the arc-length coordinate is nonlinear.
263 // This is only an approximation,
264 // but it will ensure that the nodes that were input to triangle will
265 // retain exactly the same boundary coordinates and
266 // then linear interpolation
267 // is used between those values for any newly created nodes.
268
269
270 // Create a face mesh adjacent to the fluid mesh's b-th boundary.
271 // The face mesh consists of FaceElements that may also be
272 // interpreted as GeomObjects
273 Mesh* face_mesh_pt = new Mesh;
275 b, face_mesh_pt);
276
277 // Loop over these new face elements and tell them the boundary number
278 // from the bulk fluid mesh -- this is required to they can
279 // get access to the boundary coordinates!
280 unsigned n_face_element = face_mesh_pt->nelement();
281 for (unsigned e = 0; e < n_face_element; e++)
282 {
283 // Cast the element pointer to the correct thing!
286 face_mesh_pt->element_pt(e));
287
288 // Set bulk boundary number
289 el_pt->set_boundary_number_in_bulk_mesh(b);
290 }
291
292
293 // Compare face meshes
294 {
298
299 std::ostringstream filestring;
300 filestring << "old_mesh_boundary" << b << ".dat";
301 face_mesh_pt->output(filestring.str().c_str());
302 filestring.clear();
303 filestring << "new_mesh_boundary" << b << ".dat";
304 new_face_mesh_pt->output(filestring.str().c_str());
305
307 std::cout << "OLD---\n";
308 // Now let's look at the boundary coordinates
309 unsigned n_tmp_node = this->nboundary_node(b);
310 for (unsigned n = 0; n < n_tmp_node; ++n)
311 {
312 Node* const nod_pt = this->boundary_node_pt(b, n);
313 nod_pt->get_coordinates_on_boundary(b, b_coo);
314 std::cout << nod_pt->x(0) << " " << nod_pt->x(1) << " " << nod_pt->x(2)
315 << " " << b_coo[0] << " " << b_coo[1] << "\n";
316 }
317
318 std::cout << "NEW-----------\n";
319 // Now let's look at the boundary coordinates
320 n_tmp_node = new_mesh_pt->nboundary_node(b);
321 for (unsigned n = 0; n < n_tmp_node; ++n)
322 {
323 Node* const nod_pt = new_mesh_pt->boundary_node_pt(b, n);
324 nod_pt->get_coordinates_on_boundary(b, b_coo);
325 std::cout << nod_pt->x(0) << " " << nod_pt->x(1) << " " << nod_pt->x(2)
326 << " " << b_coo[0] << " " << b_coo[1] << "\n";
327 }
328 }
329
330
331 // Now make the mesh as geometric object
333
334 // Now assign the new nodes positions based on the old meshes
335 // potentially curvilinear boundary (its geom object incarnation)
338 unsigned n_new_boundary_node = new_mesh_pt->nboundary_node(b);
339 for (unsigned n = 0; n < n_new_boundary_node; n++)
340 {
341 // Get the boundary coordinate of all new nodes
342 Node* const nod_pt = new_mesh_pt->boundary_node_pt(b, n);
343 nod_pt->get_coordinates_on_boundary(b, b_coord);
344 // Let's find boundary coordinates of the new node
346 // Now snap to the boundary
347 for (unsigned i = 0; i < 3; i++)
348 {
349 nod_pt->x(i) = new_x[i];
350 }
351 }
352
353
354 // Delete the allocated memory for the geometric object and face mesh
355 delete mesh_geom_obj_pt;
356 face_mesh_pt->flush_element_and_node_storage();
357 delete face_mesh_pt;
358
359 // Loop over the elements adjacent to the boundary
360 unsigned n_element = new_mesh_pt->nboundary_element(b);
361 if (n_element > 0)
362 {
363 // Make a dummy simplex element
365 // Make a dummy quadratic element
367 Vector<double> s(3);
369 for (unsigned n = 0; n < 4; n++)
370 {
372 }
373 for (unsigned n = 0; n < 10; n++)
374 {
376 }
377 for (unsigned e = 0; e < n_element; e++)
378 {
379 // Cache the element pointer
380 ELEMENT* elem_pt =
381 dynamic_cast<ELEMENT*>(new_mesh_pt->boundary_element_pt(b, e));
382
383 // Find the number of nodes
384 const unsigned n_node = elem_pt->nnode();
385 // Only do something if not simplex element
386 if (n_node > 4)
387 {
388 // Copy the nodes into the dummy
389 for (unsigned n = 0; n < 4; n++)
390 {
391 for (unsigned i = 0; i < 3; i++)
392 {
394 elem_pt->node_pt(n)->x(i);
395 }
396 }
397
398 // Now sort out the mid-side nodes
399 for (unsigned n = 4; n < 10; n++)
400 {
401 // If it's not on a boundary then reset to be interpolated
402 // from the simplex
404 {
407 for (unsigned i = 0; i < 3; i++)
408 {
409 elem_pt->node_pt(n)->x(i) = x_new[i];
410 }
411 }
412 }
413 }
414
415 // If we have more than 10 nodes interpolate from the quadratic shape
416 if (n_node > 10)
417 {
418 // Copy the nodes into the dummy
419 for (unsigned n = 0; n < 10; n++)
420 {
421 for (unsigned i = 0; i < 3; i++)
422 {
424 elem_pt->node_pt(n)->x(i);
425 }
426 }
427
428 // Now sort out the mid-face and central nodes
429 for (unsigned n = 10; n < n_node; n++)
430 {
431 // If it's not on a boundary then reset to be interpolated
432 // from the simplex
434 {
437 for (unsigned i = 0; i < 3; i++)
438 {
439 elem_pt->node_pt(n)->x(i) = x_new[i];
440 }
441 }
442 }
443 }
444 }
445 } // End of fix up of elements
446 }
447
448
449 //======================================================================
450 /// Adapt problem based on specified elemental error estimates
451 //======================================================================
452 template<class ELEMENT>
454 {
455 double t_start = 0.0;
456 // ###################################
458 // ###################################
459
460 // Get refinement targets
462 double max_edge_ratio =
463 this->compute_volume_target(elem_error, target_size);
464 // Get maximum target volume
465 unsigned n = target_size.size();
466 double max_size = 0.0;
467 double min_size = DBL_MAX;
468 for (unsigned e = 0; e < n; e++)
469 {
472 }
473
474 oomph_info << "Maximum target size: " << max_size << std::endl;
475 oomph_info << "Minimum target size: " << min_size << std::endl;
476 oomph_info << "Number of elements to be refined " << this->Nrefined
477 << std::endl;
478 oomph_info << "Number of elements to be unrefined " << this->Nunrefined
479 << std::endl;
480 oomph_info << "Max edge ratio " << max_edge_ratio << std::endl;
481
483 this->max_and_min_element_size(orig_max_size, orig_min_size);
484 oomph_info << "Max/min element size in original mesh: " << orig_max_size
485 << " " << orig_min_size << std::endl;
486
487 // ##################################################################
489 << "adapt: Time for getting volume targets : "
490 << TimingHelpers::timer() - t_start << " sec " << std::endl;
491 // ##################################################################
492
493 //===============================================================
494 // END: Compute target volumes
495 //===============================================================
496
497 // Check if boundaries need to be updated (regardless of
498 // requirements of bulk error estimator) but don't do anything!
499 bool inner_boundary_update_necessary = false; // true;
500
501 // Should we bother to adapt?
502 if ((Nrefined > 0) || (Nunrefined > this->max_keep_unrefined()) ||
503 (max_edge_ratio > this->max_permitted_edge_ratio()))
504 {
505 if (!((Nrefined > 0) || (Nunrefined > max_keep_unrefined())))
506 {
507 oomph_info << "Mesh regeneration triggered by edge ratio criterion\n";
508 }
509
510 // ###################################
512 // ###################################
513
514 // Are we dealing with a solid mesh?
515 SolidMesh* solid_mesh_pt = dynamic_cast<SolidMesh*>(this);
516
517 // Build temporary uniform background mesh
518 //----------------------------------------
519 // with volume set by maximum required volume
520 //---------------------------------------
522 if (solid_mesh_pt != 0)
523 {
524 throw OomphLibError("Solid RefineableTetgenMesh not done yet.",
527 /* tmp_new_mesh_pt=new RefineableSolidTetgenMesh<ELEMENT> */
528 /* (closed_curve_pt, */
529 /* hole_pt, */
530 /* max_size, */
531 /* this->Time_stepper_pt, */
532 /* this->Use_attributes); */
533 }
534 else
535 {
537 this->Outer_boundary_pt,
538 this->Internal_surface_pt,
539 max_size,
540 this->Time_stepper_pt,
541 this->Use_attributes,
542 this->Corner_elements_must_be_split);
543 }
544
545 // ##################################################################
547 << "adapt: Time for building temp mesh : "
548 << TimingHelpers::timer() - t_start << " sec " << std::endl;
549 // ##################################################################
550
551 // Get the tetgenio object associated with that mesh
554
555 // If the mesh is a solid mesh then do the mapping based on the
556 // Eulerian coordinates
557 bool use_eulerian_coords = false;
558 if (solid_mesh_pt != 0)
559 {
560 use_eulerian_coords = true;
561 }
562
563#ifdef OOMPH_HAS_CGAL
564
565 // Make cgal-based bin
568 {
569 cgal_params.enable_use_eulerian_coordinates_during_setup();
570 }
572
573#else
574
575 std::ostringstream error_message;
576 error_message << "Non-CGAL-based target size transfer not implemented.\n";
577 throw OomphLibError(
579
580 // Here's the relevant construction from the triangle mesh. Update...
581
582 // // Make nonrefineable bin
583 // NonRefineableBinArrayParameters params(this);
584 // if (use_eulerian_coords)
585 // {
586 // params.enable_use_eulerian_coordinates_during_setup();
587 // }
588 // Vector<unsigned> bin_dim(2);
589 // bin_dim[0]=Nbin_x_for_area_transfer;
590 // bin_dim[1]=Nbin_y_for_area_transfer;
591 // params.dimensions_of_bin_array()=bin_dim;
592 // MeshAsGeomObject* mesh_geom_obj_pt=new MeshAsGeomObject(&params);
593
594#endif
595
596 // Set up a map from pointer to element to its number
597 // in the mesh
598 std::map<GeneralisedElement*, unsigned> element_number;
599 unsigned nelem = this->nelement();
600 for (unsigned e = 0; e < nelem; e++)
601 {
602 element_number[this->element_pt(e)] = e;
603 }
604
605 // Now start iterating to refine mesh recursively
606 //-----------------------------------------------
607 bool done = false;
608 unsigned iter = 0;
609 while (!done)
610 {
611 // Accept by default but overwrite if things go wrong below
612 done = true;
613
614 // Loop over elements in new (tmp) mesh and visit all
615 // its integration points. Check where it's located in the bin
616 // structure of the current mesh and pass the target size
617 // to the new element
618 unsigned nelem = tmp_new_mesh_pt->nelement();
619
620 // Store the target sizes for elements in the temporary
621 // mesh
623 for (unsigned e = 0; e < nelem; e++)
624 {
625 ELEMENT* el_pt =
626 dynamic_cast<ELEMENT*>(tmp_new_mesh_pt->element_pt(e));
627 unsigned nint = el_pt->integral_pt()->nweight();
628 for (unsigned ipt = 0; ipt < nint; ipt++)
629 {
630 // Get the coordinate of current point
631 Vector<double> s(3);
632 for (unsigned i = 0; i < 3; i++)
633 {
634 s[i] = el_pt->integral_pt()->knot(ipt, i);
635 }
636
637 Vector<double> x(3);
639
640#if OOMPH_HAS_CGAL
641
642 // Try the five nearest sample points for Newton search
643 // then just settle on the nearest one
645 unsigned max_sample_points = 5;
646 dynamic_cast<CGALSamplePointContainer*>(
647 mesh_geom_obj_pt->sample_point_container_pt())
648 ->limited_locate_zeta(x, max_sample_points, geom_obj_pt, s);
649#ifdef PARANOID
650 if (geom_obj_pt == 0)
651 {
652 std::stringstream error_message;
653 error_message << "Limited locate zeta failed for zeta = [ "
654 << x[0] << " " << x[1] << " " << x[2]
655 << " ]. Makes no sense!\n";
656 throw OomphLibError(error_message.str(),
659 }
660 else
661 {
662#endif
663 FiniteElement* fe_pt = dynamic_cast<FiniteElement*>(geom_obj_pt);
664#ifdef PARANOID
665 if (fe_pt == 0)
666 {
667 std::stringstream error_message;
668 error_message << "Cast to FE for GeomObject returned by "
669 "limited locate zeta failed for zeta = [ "
670 << x[0] << " " << x[1] << " " << x[2]
671 << " ]. Makes no sense!\n";
672 throw OomphLibError(error_message.str(),
675 }
676 else
677 {
678#endif
679 // What's the target size of the element that contains this
680 // point
682
683 // Go for smallest target size over all integration
684 // points in new element
685 // to force "one level" of refinement (the one-level-ness
686 // is enforced below by limiting the actual reduction in
687 // size
688 if (new_transferred_target_size[e] != 0.0)
689 {
692 }
693 else
694 {
696 }
697#ifdef PARANOID
698 }
699 }
700#endif
701
702// Non-CGAL
703#else
704
705 std::ostringstream error_message;
706 error_message
707 << "Non-CGAL-based target size transfer not implemented.\n";
708 throw OomphLibError(error_message.str(),
711
712 // Here's the relevant construction from the triangle mesh.
713 // Update...
714
715 // // Find the bin that contains that point and its contents
716 // int bin_number=0;
717 // bin_array_pt->get_bin(x,bin_number);
718
719 // // Did we find it?
720 // if (bin_number<0)
721 // {
722 // // Not even within bin boundaries... odd
723 // std::stringstream error_message;
724 // error_message
725 // << "Very odd -- we're looking for a point[ "
726 // << x[0] << " " << x[1] << " " << x[2] << " ] that's not even
727 // \n"
728 // << "located within the bin boundaries.\n";
729 // throw OomphLibError(error_message.str(),
730 // OOMPH_CURRENT_FUNCTION,
731 // OOMPH_EXCEPTION_LOCATION);
732 // } // if (bin_number<0)
733 // else
734 // {
735 // // Go for smallest target size of any element in this bin
736 // // to force "one level" of refinement (the one-level-ness
737 // // is enforced below by limiting the actual reduction in
738 // // size
739 // if (new_transferred_target_size[e]!=0)
740 // {
741 // std::min(new_transferred_target_size[e],
742 // bin_min_target_size[bin_number]);
743 // }
744 // else
745 // {
746 // new_transferred_target_size[e]=bin_min_target_size[bin_number];
747 // }
748
749 // }
750
751#endif
752
753 } // for (ipt<nint)
754
755 } // for (e<nelem)
756
757 // do some output (keep it alive!)
758 const bool output_target_sizes = false;
760 {
762 for (unsigned u = 0; u < length; u++)
763 {
764 oomph_info << "Element" << u
765 << ",target size: " << new_transferred_target_size[u]
766 << std::endl;
767 }
768 }
769
770 // Now copy into target size for temporary mesh but limit to
771 // the equivalent of one sub-division per iteration
772#ifdef OOMPH_HAS_MPI
773 unsigned n_ele_need_refinement_iter = 0;
774#endif
775
776 // Don't delete! Keep these around for debugging
777 // ofstream tmp_mesh_file;
778 // tmp_mesh_file.open("tmp_mesh_file.dat");
779 // tmp_new_mesh_pt->output(tmp_mesh_file);
780 // tmp_mesh_file.close();
781
782 std::ofstream target_sizes_file;
783 char filename[100];
784 snprintf(filename, sizeof(filename), "target_sizes%i.dat", iter);
786 {
788 }
789
790 const unsigned nel_new = tmp_new_mesh_pt->nelement();
792 for (unsigned e = 0; e < nel_new; e++)
793 {
794 // The finite element
795 FiniteElement* f_ele_pt = tmp_new_mesh_pt->finite_element_pt(e);
796
797 // Transferred target size
798 const double new_size = new_transferred_target_size[e];
799 if (new_size <= 0.0)
800 {
801 std::ostringstream error_stream;
803 << "This shouldn't happen! Element whose centroid is at "
804 << (f_ele_pt->node_pt(0)->x(0) + f_ele_pt->node_pt(1)->x(0) +
805 f_ele_pt->node_pt(2)->x(0) + f_ele_pt->node_pt(3)->x(0)) /
806 4.0
807 << " "
808 << (f_ele_pt->node_pt(0)->x(1) + f_ele_pt->node_pt(1)->x(1) +
809 f_ele_pt->node_pt(2)->x(1) + f_ele_pt->node_pt(3)->x(1)) /
810 4.0
811 << " "
812 << (f_ele_pt->node_pt(0)->x(2) + f_ele_pt->node_pt(1)->x(2) +
813 f_ele_pt->node_pt(2)->x(2) + f_ele_pt->node_pt(3)->x(2)) /
814 4.0
815 << " "
816 << " has no target size assigned\n";
817 throw OomphLibError(error_stream.str(),
820 }
821 else
822 {
823 // Limit target size to the equivalent of uniform refinement
824 // during this stage of the iteration
826 if (new_target_size[e] < f_ele_pt->size() / 4.0)
827 {
828 new_target_size[e] = f_ele_pt->size() / 4.0;
829
830 // ALH: It seems that tetgen "enlarges" the volume constraint
831 // so this criterion can never be met unless dividing by 1.2
832 // as well. MH: Is this the reason why Andrew's version of
833 // adaptation never converges? Took it out.
834 // new_target_size[e] /= 1.2;
835
836 // We'll need to give it another go later
837 done = false;
838 }
839
840 // Don't delete! Keep around for debugging
842 {
843 target_sizes_file << "ZONE N=4, E=1, F=FEPOINT, ET=TETRAHEDRON\n";
844 for (unsigned j = 0; j < 4; j++)
845 {
846 target_sizes_file << f_ele_pt->node_pt(j)->x(0) << " "
847 << f_ele_pt->node_pt(j)->x(1) << " "
848 << f_ele_pt->node_pt(j)->x(2) << " "
849 << new_size << " " << new_target_size[e]
850 << std::endl;
851 }
852 target_sizes_file << "1 2 3 4\n"; // connectivity
853
854 // Keep around; just doc at centroid
855 /* target_sizes_file */
856 /* << (f_ele_pt->node_pt(0)->x(0)+ */
857 /* f_ele_pt->node_pt(1)->x(0)+ */
858 /* f_ele_pt->node_pt(2)->x(0)+ */
859 /* f_ele_pt->node_pt(3)->x(0))/4.0 << " " */
860 /* << (f_ele_pt->node_pt(0)->x(1)+ */
861 /* f_ele_pt->node_pt(1)->x(1)+ */
862 /* f_ele_pt->node_pt(2)->x(1)+ */
863 /* f_ele_pt->node_pt(3)->x(1))/4.0 << " " */
864 /* << (f_ele_pt->node_pt(0)->x(2)+ */
865 /* f_ele_pt->node_pt(1)->x(2)+ */
866 /* f_ele_pt->node_pt(2)->x(2)+ */
867 /* f_ele_pt->node_pt(3)->x(2))/4.0 << " " */
868 /* << new_size << " " */
869 /* << new_target_size[e] << std::endl; */
870 }
871
872#ifdef OOMPH_HAS_MPI
873 // Keep track of the elements that require (un)refinement
875#endif
876
877 } // else if (new_size <= 0.0)
878
879 } // for (e < nel_new)
880
881 // Don't delete! Keep around for debugging
883 {
884 target_sizes_file.close();
885 }
886
887 if (done)
888 {
890 << "All size adjustments accommodated by max. permitted size"
891 << " reduction during iter " << iter << "\n";
892 }
893 else
894 {
895 oomph_info << "NOT all size adjustments accommodated by max. "
896 << "permitted size reduction during iter " << iter
897 << "\n";
898 }
899
901 << "\n\n\n==================================================\n"
902 << "==================================================\n"
903 << "==================================================\n"
904 << "==================================================\n"
905 << "\n\n\n";
906
907 // ##################################################################
909 << "adapt: Time for new_target_size[.] : "
910 << TimingHelpers::timer() - t_start << " sec " << std::endl;
911 // ##################################################################
912
913 // Now create the new mesh from TriangulateIO structure
914 //-----------------------------------------------------
915 // associated with uniform background mesh and the
916 //------------------------------------------------
917 // associated target element sizes.
918 //---------------------------------
919
920 // ###################################
922 // ###################################
923
924 // Solid mesh?
925 if (solid_mesh_pt != 0)
926 {
927 std::ostringstream error_message;
928 error_message << "RefineableSolidTetgenMesh not implemented yet.\n";
929 throw OomphLibError(error_message.str(),
932 /* new_mesh_pt=new RefineableSolidTetgenMesh<ELEMENT> */
933 /* (new_target_area, */
934 /* tmp_new_triangulateio, */
935 /* this->Time_stepper_pt, */
936 /* this->Use_attributes); */
937 }
938 // No solid mesh
939 else
940 {
944 this->Outer_boundary_pt,
945 this->Internal_surface_pt,
946 this->Time_stepper_pt,
947 this->Use_attributes);
948 }
949
950 // ##################################################################
952 << "adapt: Time for new_mesh_pt : "
953 << TimingHelpers::timer() - t_start << " sec " << std::endl;
954 // ##################################################################
955
956 // Not done: get ready for another iteration
957 iter++;
958
959 // Check if the new mesh actually differs from the old one
960 // If not, we're done.
961 if (!done)
962 {
963 unsigned nnod = tmp_new_mesh_pt->nnode();
964 if (nnod == new_mesh_pt->nnode())
965 {
966 unsigned nel = tmp_new_mesh_pt->nelement();
967 if (nel == new_mesh_pt->nelement())
968 {
969 bool nodes_identical = true;
970 for (unsigned j = 0; j < nnod; j++)
971 {
972 bool coords_identical = true;
973 for (unsigned i = 0; i < 3; i++)
974 {
975 if (new_mesh_pt->node_pt(j)->x(i) !=
977 {
978 coords_identical = false;
979 }
980 }
981 if (!coords_identical)
982 {
983 nodes_identical = false;
984 break;
985 }
986 }
987 if (nodes_identical)
988 {
989 done = true;
990 }
991 }
992 }
993 }
994
995 // Delete the temporary mesh
996 delete tmp_new_mesh_pt;
997
998 // Now transfer over the pointers
999 if (!done)
1000 {
1002 tmp_new_tetgenio_pt = new_mesh_pt->tetgenio_pt();
1003 }
1004
1005 } // end of iteration
1006
1007 // Check that the projection step is not disabled
1008 if (!Projection_is_disabled)
1009 {
1010 // ###################################
1012 // ###################################
1013
1014 // Project current solution onto new mesh
1015 //---------------------------------------
1018 project_problem_pt->mesh_pt() = new_mesh_pt;
1019 project_problem_pt->project(this);
1020 delete project_problem_pt;
1021
1022 // Re-setup terminate helper (which was reset by the
1023 // deconstructor of project_problem_pt)
1025
1026 // ##################################################################
1028 << "adapt: Time for project soln onto new mesh : "
1029 << TimingHelpers::timer() - t_start << " sec " << std::endl;
1030 // ##################################################################
1031 }
1032
1033 // ###################################
1035 // ###################################
1036
1037 // this->output("pre_proj",5);
1038 // new_mesh_pt->output("post_proj.dat",5);
1039
1040 // Flush the old mesh
1041 unsigned nnod = nnode();
1042 for (unsigned j = nnod; j > 0; j--)
1043 {
1044 delete Node_pt[j - 1];
1045 Node_pt[j - 1] = 0;
1046 }
1047 unsigned nel = nelement();
1048 for (unsigned e = nel; e > 0; e--)
1049 {
1050 delete Element_pt[e - 1];
1051 Element_pt[e - 1] = 0;
1052 }
1053
1054 // Now copy back to current mesh
1055 //------------------------------
1056 nnod = new_mesh_pt->nnode();
1058 nel = new_mesh_pt->nelement();
1059 Element_pt.resize(nel);
1060 for (unsigned j = 0; j < nnod; j++)
1061 {
1063 }
1064 for (unsigned e = 0; e < nel; e++)
1065 {
1066 Element_pt[e] = new_mesh_pt->element_pt(e);
1067 }
1068
1069 // Copy the boundary schemes
1070 unsigned nbound = new_mesh_pt->nboundary();
1071 Boundary_element_pt.resize(nbound);
1072 Face_index_at_boundary.resize(nbound);
1073 Boundary_node_pt.resize(nbound);
1074 for (unsigned b = 0; b < nbound; b++)
1075 {
1076 unsigned nel = new_mesh_pt->nboundary_element(b);
1077 Boundary_element_pt[b].resize(nel);
1078 Face_index_at_boundary[b].resize(nel);
1079 for (unsigned e = 0; e < nel; e++)
1080 {
1081 Boundary_element_pt[b][e] = new_mesh_pt->boundary_element_pt(b, e);
1082 Face_index_at_boundary[b][e] =
1083 new_mesh_pt->face_index_at_boundary(b, e);
1084 }
1085 unsigned nnod = new_mesh_pt->nboundary_node(b);
1086 Boundary_node_pt[b].resize(nnod);
1087 for (unsigned j = 0; j < nnod; j++)
1088 {
1089 Boundary_node_pt[b][j] = new_mesh_pt->boundary_node_pt(b, j);
1090 }
1091 }
1092
1093 // Also copy over the new boundary and region information
1094 unsigned n_region = new_mesh_pt->nregion();
1095
1096 // Only bother if we have regions
1097 if (n_region > 1)
1098 {
1099 // Deal with the region information first
1100 this->Region_element_pt.resize(n_region);
1101 this->Region_attribute.resize(n_region);
1102 for (unsigned i = 0; i < n_region; i++)
1103 {
1104 // Copy across region attributes (region ids!)
1105 this->Region_attribute[i] = new_mesh_pt->region_attribute(i);
1106
1107 // Find the number of elements in the region
1108 unsigned r = this->Region_attribute[i];
1109 unsigned n_region_element = new_mesh_pt->nregion_element(r);
1110 this->Region_element_pt[i].resize(n_region_element);
1111 for (unsigned e = 0; e < n_region_element; e++)
1112 {
1113 this->Region_element_pt[i][e] =
1114 new_mesh_pt->region_element_pt(r, e);
1115 }
1116 }
1117
1118 // Now the boundary region information
1119 this->Boundary_region_element_pt.resize(nbound);
1120 this->Face_index_region_at_boundary.resize(nbound);
1121
1122 // Now loop over the boundaries
1123 for (unsigned b = 0; b < nbound; ++b)
1124 {
1125 // Loop over the regions
1126 for (unsigned i = 0; i < n_region; ++i)
1127 {
1128 unsigned r = this->Region_attribute[i];
1129
1130 unsigned n_boundary_el_in_region =
1131 new_mesh_pt->nboundary_element_in_region(b, r);
1133 {
1134 // Allocate storage in the map
1135 this->Boundary_region_element_pt[b][r].resize(
1137 this->Face_index_region_at_boundary[b][r].resize(
1139
1140 // Copy over the information
1141 for (unsigned e = 0; e < n_boundary_el_in_region; ++e)
1142 {
1143 this->Boundary_region_element_pt[b][r][e] =
1144 new_mesh_pt->boundary_element_in_region_pt(b, r, e);
1145 this->Face_index_region_at_boundary[b][r][e] =
1146 new_mesh_pt->face_index_at_boundary_in_region(b, r, e);
1147 }
1148 }
1149 }
1150 } // End of loop over boundaries
1151
1152 } // End of case when more than one region
1153
1154 // Copy TriangulateIO representation
1155 this->set_deep_copy_tetgenio_pt(new_mesh_pt->tetgenio_pt());
1156
1157 // Flush the mesh
1158 new_mesh_pt->flush_element_and_node_storage();
1159
1160 // Delete the mesh and the problem
1161 delete new_mesh_pt;
1162
1163 // ##################################################################
1165 << "adapt: Time for moving nodes etc. to actual mesh : "
1166 << TimingHelpers::timer() - t_start << " sec " << std::endl;
1167 // ##################################################################
1168
1169 // Solid mesh?
1170 if (solid_mesh_pt != 0)
1171 {
1172 // Warning
1173 std::stringstream error_message;
1174 error_message
1175 << "Lagrangian coordinates are currently not projected but are\n"
1176 << "are re-set during adaptation. This is not appropriate for\n"
1177 << "real solid mechanics problems!\n";
1178 OomphLibWarning(error_message.str(),
1181
1182 // Reset Lagrangian coordinates
1183 dynamic_cast<SolidMesh*>(this)->set_lagrangian_nodal_coordinates();
1184 }
1185
1186 double max_area;
1187 double min_area;
1188 this->max_and_min_element_size(max_area, min_area);
1189 oomph_info << "Max/min element size in adapted mesh: " << max_area << " "
1190 << min_area << std::endl;
1191 }
1192 else
1193 {
1194 oomph_info << "Not enough benefit in adaptation.\n";
1195 Nrefined = 0;
1196 Nunrefined = 0;
1197 }
1198 }
1199
1200} // namespace oomph
1201
1202#endif
e
Definition cfortran.h:571
static char t char * s
Definition cfortran.h:568
cstr elem_len * i
Definition cfortran.h:603
CGAL-based SamplePointContainer.
Helper object for dealing with the parameters used for the CGALSamplePointContainer objects.
A general Finite Element class.
Definition elements.h:1317
Integral *const & integral_pt() const
Return the pointer to the integration scheme (const version)
Definition elements.h:1967
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
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
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
virtual Node * construct_node(const unsigned &n)
Construct the local node n and return a pointer to the newly created node object.
Definition elements.h:2513
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
unsigned nnode() const
Return the number of nodes.
Definition elements.h:2214
Node ** Node_pt
Storage for pointers to the nodes in the element.
Definition elements.h:1323
Node *& node_pt(const unsigned &n)
Return a pointer to the local node n.
Definition elements.h:2179
A geometric object is an object that provides a parametrised description of its shape via the functio...
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.
This class provides a GeomObject representation of a given finite element mesh. The Lagrangian coordi...
A general mesh class.
Definition mesh.h:67
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
virtual bool is_on_boundary() const
Test whether the Node lies on a boundary. The "bulk" Node cannot lie on a boundary,...
Definition nodes.h:1373
double & x(const unsigned &i)
Return the i-th nodal coordinate.
Definition nodes.h:1060
void resize(const unsigned &n_value)
Resize the number of equations.
Definition nodes.cc:2167
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 surface_remesh_for_inner_hole_boundaries()
Generate a new faceted representation of the inner hole boundaries.
void update_faceted_surface_using_face_mesh(TetMeshFacetedSurface *&faceted_surface_pt)
Helper function that updates the input faceted surface by using the flattened elements from FaceMesh(...
void snap_nodes_onto_boundary(RefineableTetgenMesh< ELEMENT > *&new_mesh_pt, const unsigned &b)
Snap the boundary nodes onto any curvilinear boundaries.
void adapt(const Vector< double > &elem_error)
Adapt mesh, based on elemental error provided.
General SolidMesh class.
Definition mesh.h:2570
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.
TAdvectionDiffusionReactionElement()
Constructor: Call constructors for TElement and AdvectionDiffusionReaction equations.
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
void setup()
Setup terminate helper.
double timer()
returns the time in seconds after some point in past
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...