Toggle navigation
Documentation
Big picture
The finite element method
The data structure
Not-so-quick guide
Optimisation
Order of action functions
Example codes and tutorials
List of example codes and tutorials
Meshing
Solvers
MPI parallel processing
Post-processing/visualisation
Other
Change log
Creating documentation
Coding conventions
Index
FAQ
About
People
Contact/Get involved
Publications
Acknowledgements
Copyright
Picture show
Go
src
meshes
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
41
#include "generic/sample_point_parameters.h"
42
#include "generic/mesh_as_geometric_object.h"
43
#include "generic/projection.h"
44
45
namespace
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>
52
void
RefineableTetgenMesh<ELEMENT>::update_faceted_surface_using_face_mesh
(
53
TetMeshFacetedSurface
*&
faceted_surface_pt
)
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;
77
this->
template
build_face_mesh<ELEMENT, FaceElementAsGeomObject>
(
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!
87
FaceElementAsGeomObject<ELEMENT>
*
el_pt
=
88
dynamic_cast<
FaceElementAsGeomObject<ELEMENT>
*
>
(
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
100
Vector<unsigned>
new_local_facet
(3);
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
{
125
new_local_facet
[
n
] =
counter
;
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
137
new_local_facet
[
n
] =
counter
;
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
156
unsigned
n_facet_vertex
=
new_vertex_nodes
.size();
157
// Vector<Vector<double> > facet_point(n_facet_vertex);
158
Vector<Node*>
facet_nodes_pt
(
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
{
164
facet_nodes_pt
[
count
] = *
it
;
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();
175
Vector<Vector<unsigned>
>
new_facet
(
n_facet
);
176
Vector<unsigned>
new_facet_boundary_id
(
n_facet
);
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
;
200
faceted_surface_pt
=
new
TetMeshFacetedClosedSurfaceForRemesh
(
201
facet_nodes_pt
,
new_facet
,
new_facet_boundary_id
);
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)
207
Vector<double>
inner_point
(3, 0.0);
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
{
218
inner_point
[
i
] /=
n_facet_vertex
;
219
}
220
221
// Now set the hole if required
222
dynamic_cast<
TetMeshFacetedClosedSurface
*
>
(
faceted_surface_pt
)
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>
230
void
RefineableTetgenMesh<ELEMENT>::surface_remesh_for_inner_hole_boundaries
()
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>
249
void
RefineableTetgenMesh<ELEMENT>::snap_nodes_onto_boundary
(
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;
274
this->
template
build_face_mesh<ELEMENT, FaceElementAsGeomObject>
(
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!
284
FaceElementAsGeomObject<ELEMENT>
*
el_pt
=
285
dynamic_cast<
FaceElementAsGeomObject<ELEMENT>
*
>
(
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
{
295
Mesh*
new_face_mesh_pt
=
new
Mesh;
296
new_mesh_pt
->template
build_face_mesh<ELEMENT, FaceElementAsGeomObject>
(
297
b
,
new_face_mesh_pt
);
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
306
Vector<double>
b_coo
(2);
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
332
MeshAsGeomObject
*
mesh_geom_obj_pt
=
new
MeshAsGeomObject
(
face_mesh_pt
);
333
334
// Now assign the new nodes positions based on the old meshes
335
// potentially curvilinear boundary (its geom object incarnation)
336
Vector<double>
new_x
(3);
337
Vector<double>
b_coord
(2);
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
345
mesh_geom_obj_pt
->position(
b_coord
,
new_x
);
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
364
TElement<3, 2>
dummy_four_node_element
;
365
// Make a dummy quadratic element
366
TElement<3, 3>
dummy_ten_node_element
;
367
Vector<double>
s
(3);
368
Vector<double>
x_new
(3);
369
for
(
unsigned
n
= 0;
n
< 4;
n
++)
370
{
371
dummy_four_node_element
.construct_node(
n
);
372
}
373
for
(
unsigned
n
= 0;
n
< 10;
n
++)
374
{
375
dummy_ten_node_element
.construct_node(
n
);
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
{
393
dummy_four_node_element
.node_pt(
n
)->x(
i
) =
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
403
if
(!
elem_pt
->node_pt(
n
)->is_on_boundary())
404
{
405
elem_pt
->local_coordinate_of_node(
n
,
s
);
406
dummy_four_node_element
.interpolated_x(
s
,
x_new
);
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
{
423
dummy_ten_node_element
.node_pt(
n
)->x(
i
) =
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
433
if
(!
elem_pt
->node_pt(
n
)->is_on_boundary())
434
{
435
elem_pt
->local_coordinate_of_node(
n
,
s
);
436
dummy_ten_node_element
.interpolated_x(
s
,
x_new
);
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>
453
void
RefineableTetgenMesh<ELEMENT>::adapt
(
const
Vector<double>
&
elem_error
)
454
{
455
double
t_start
= 0.0;
456
// ###################################
457
t_start
= TimingHelpers::timer();
458
// ###################################
459
460
// Get refinement targets
461
Vector<double>
target_size
(
elem_error
.size());
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
{
470
if
(
target_size
[
e
] >
max_size
)
max_size
=
target_size
[
e
];
471
if
(
target_size
[
e
] <
min_size
)
min_size
=
target_size
[
e
];
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
482
double
orig_max_size
,
orig_min_size
;
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
// ##################################################################
488
oomph_info
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
// ###################################
511
t_start
= TimingHelpers::timer();
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
//---------------------------------------
521
RefineableTetgenMesh<ELEMENT>
*
tmp_new_mesh_pt
= 0;
522
if
(
solid_mesh_pt
!= 0)
523
{
524
throw
OomphLibError
(
"Solid RefineableTetgenMesh not done yet."
,
525
OOMPH_CURRENT_FUNCTION
,
526
OOMPH_EXCEPTION_LOCATION
);
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
{
536
tmp_new_mesh_pt
=
new
RefineableTetgenMesh<ELEMENT>
(
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
// ##################################################################
546
oomph_info
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
552
tetgenio
*
tmp_new_tetgenio_pt
=
tmp_new_mesh_pt
->tetgenio_pt();
553
RefineableTetgenMesh<ELEMENT>
*
new_mesh_pt
= 0;
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
566
CGALSamplePointContainerParameters
cgal_params
(
this
);
567
if
(
use_eulerian_coords
)
568
{
569
cgal_params
.enable_use_eulerian_coordinates_during_setup();
570
}
571
MeshAsGeomObject
*
mesh_geom_obj_pt
=
new
MeshAsGeomObject
(&
cgal_params
);
572
573
#else
574
575
std::ostringstream
error_message
;
576
error_message
<<
"Non-CGAL-based target size transfer not implemented.\n"
;
577
throw
OomphLibError
(
578
error_message
.str(),
OOMPH_CURRENT_FUNCTION
,
OOMPH_EXCEPTION_LOCATION
);
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(¶ms);
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
622
Vector<double>
new_transferred_target_size
(
nelem
, 0.0);
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);
638
el_pt
->interpolated_x(
s
, x);
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
644
GeomObject
*
geom_obj_pt
= 0;
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(),
657
OOMPH_CURRENT_FUNCTION
,
658
OOMPH_EXCEPTION_LOCATION
);
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(),
673
OOMPH_CURRENT_FUNCTION
,
674
OOMPH_EXCEPTION_LOCATION
);
675
}
676
else
677
{
678
#endif
679
// What's the target size of the element that contains this
680
// point
681
double
tg_size
=
target_size
[
element_number
[
fe_pt
]];
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
{
690
new_transferred_target_size
[
e
] =
691
std::min(
new_transferred_target_size
[
e
],
tg_size
);
692
}
693
else
694
{
695
new_transferred_target_size
[
e
] =
tg_size
;
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(),
709
OOMPH_CURRENT_FUNCTION
,
710
OOMPH_EXCEPTION_LOCATION
);
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
;
759
if
(
output_target_sizes
)
760
{
761
unsigned
length
=
new_transferred_target_size
.size();
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
);
785
if
(
output_target_sizes
)
786
{
787
target_sizes_file
.open(
filename
);
788
}
789
790
const
unsigned
nel_new
=
tmp_new_mesh_pt
->nelement();
791
Vector<double>
new_target_size
(
nel_new
);
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
;
802
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(),
818
OOMPH_CURRENT_FUNCTION
,
819
OOMPH_EXCEPTION_LOCATION
);
820
}
821
else
822
{
823
// Limit target size to the equivalent of uniform refinement
824
// during this stage of the iteration
825
new_target_size
[
e
] =
new_size
;
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
841
if
(
output_target_sizes
)
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
874
n_ele_need_refinement_iter
++;
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
882
if
(
output_target_sizes
)
883
{
884
target_sizes_file
.close();
885
}
886
887
if
(
done
)
888
{
889
oomph_info
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
900
oomph_info
901
<<
"\n\n\n==================================================\n"
902
<<
"==================================================\n"
903
<<
"==================================================\n"
904
<<
"==================================================\n"
905
<<
"\n\n\n"
;
906
907
// ##################################################################
908
oomph_info
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
// ###################################
921
t_start
= TimingHelpers::timer();
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(),
930
OOMPH_CURRENT_FUNCTION
,
931
OOMPH_EXCEPTION_LOCATION
);
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
{
941
new_mesh_pt
=
942
new
RefineableTetgenMesh<ELEMENT>
(
new_target_size
,
943
tmp_new_tetgenio_pt
,
944
this->Outer_boundary_pt,
945
this->Internal_surface_pt,
946
this->
Time_stepper_pt
,
947
this->
Use_attributes
);
948
}
949
950
// ##################################################################
951
oomph_info
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
) !=
976
tmp_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
{
1001
tmp_new_mesh_pt
=
new_mesh_pt
;
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
// ###################################
1011
t_start
= TimingHelpers::timer();
1012
// ###################################
1013
1014
// Project current solution onto new mesh
1015
//---------------------------------------
1016
ProjectionProblem<ELEMENT>
*
project_problem_pt
=
1017
new
ProjectionProblem<ELEMENT>
;
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)
1024
TerminateHelper::setup();
1025
1026
// ##################################################################
1027
oomph_info
1028
<<
"adapt: Time for project soln onto new mesh : "
1029
<< TimingHelpers::timer() -
t_start
<<
" sec "
<< std::endl;
1030
// ##################################################################
1031
}
1032
1033
// ###################################
1034
t_start
= TimingHelpers::timer();
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();
1057
Node_pt
.resize(
nnod
);
1058
nel
=
new_mesh_pt
->nelement();
1059
Element_pt
.resize(
nel
);
1060
for
(
unsigned
j
= 0;
j
<
nnod
;
j
++)
1061
{
1062
Node_pt
[
j
] =
new_mesh_pt
->node_pt(
j
);
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
);
1132
if
(
n_boundary_el_in_region
> 0)
1133
{
1134
// Allocate storage in the map
1135
this->
Boundary_region_element_pt
[
b
][
r
].resize(
1136
n_boundary_el_in_region
);
1137
this->
Face_index_region_at_boundary
[
b
][
r
].resize(
1138
n_boundary_el_in_region
);
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
// ##################################################################
1164
oomph_info
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(),
1179
OOMPH_CURRENT_FUNCTION
,
1180
OOMPH_EXCEPTION_LOCATION
);
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
oomph::RefineableTetgenMesh::surface_remesh_for_inner_hole_boundaries
void surface_remesh_for_inner_hole_boundaries()
Generate a new faceted representation of the inner hole boundaries.
Definition
refineable_tetgen_mesh.template.cc:230
oomph::RefineableTetgenMesh::update_faceted_surface_using_face_mesh
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(...
Definition
refineable_tetgen_mesh.template.cc:52
oomph::RefineableTetgenMesh::snap_nodes_onto_boundary
void snap_nodes_onto_boundary(RefineableTetgenMesh< ELEMENT > *&new_mesh_pt, const unsigned &b)
Snap the boundary nodes onto any curvilinear boundaries.
Definition
refineable_tetgen_mesh.template.cc:249
oomph::RefineableTetgenMesh::adapt
void adapt(const Vector< double > &elem_error)
Adapt mesh, based on elemental error provided.
Definition
refineable_tetgen_mesh.template.cc:453
oomph::RefineableTriangleMesh
Unstructured refineable Triangle Mesh.
Definition
triangle_mesh.h:2225
oomph::TriangleMesh::Time_stepper_pt
TimeStepper * Time_stepper_pt
Timestepper used to build elements.
Definition
triangle_mesh.h:1162
oomph::TriangleMesh::Use_attributes
bool Use_attributes
Boolean flag to indicate whether to use attributes or not (required for multidomain meshes)
Definition
triangle_mesh.h:1166
oomph
Definition
annular_domain.h:35