xda_tet_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#ifndef OOMPH_XDA_TET_MESH_TEMPLATE_HEADER
27#define OOMPH_XDA_TET_MESH_TEMPLATE_HEADER
28
29#ifndef OOMPH_XDA_TET_MESH_HEADER
30#error __FILE__ should only be included from xda_tet_mesh.h.
31#endif // OOMPH_XDA_TET_MESH_HEADER
32
33#include "generic/Telements.h"
34
35namespace oomph
36{
37 //======================================================================
38 /// Constructor: Pass name of xda file. Note that all
39 /// boundary elements get their own ID -- this is required for
40 /// FSI problems. The vector containing the oomph-lib
41 /// boundary IDs of all oomph-lib boundaries that collectively form
42 /// a given boundary as specified in the xda input file can be
43 /// obtained from the access function oomph_lib_boundary_ids(...).
44 /// Timestepper defaults to steady pseudo-timestepper.
45 //======================================================================
46 template<class ELEMENT>
48 TimeStepper* time_stepper_pt)
49 {
50 // Mesh can only be built with 3D Telements.
51 MeshChecker::assert_geometric_element<TElementGeometricBase, ELEMENT>(3);
52
53 // Open and process xda input file
54 std::ifstream infile(xda_file_name.c_str(), std::ios_base::in);
55 unsigned n_node;
56 unsigned n_element;
57 unsigned n_bound_face;
58
59 if (!infile.is_open())
60 {
61 std::ostringstream error_stream;
62 error_stream << "Failed to open " << xda_file_name << "\n";
63 throw OomphLibError(
65 }
66
67 // Dummy storage to jump lines
68 char dummy[101];
69
70 // Ignore file format
71 infile.getline(dummy, 100);
72
73 // Get number of elements
75
76 // Ignore rest of line
77 infile.getline(dummy, 100);
78
79 // Get number of nodes
80 infile >> n_node;
81
82 // Ignore rest of line
83 infile.getline(dummy, 100);
84
85 // Ignore sum of element weights (whatever that is...)
86 infile.getline(dummy, 100);
87
88 // Get number of enumerated boundary faces on which boundary conditions
89 // are applied.
91
92 // Keep reading until "Title String"
93 while (dummy[0] != 'T')
94 {
95 infile.getline(dummy, 100);
96 }
97
98 // Make space for nodes and elements
100 Element_pt.resize(n_element);
101
102 // Read first line with node labels and count them
103 std::string line;
104 std::getline(infile, line);
105 std::istringstream ostr(line);
106 std::istream_iterator<std::string> it(ostr);
107 std::istream_iterator<std::string> end;
108 unsigned nnod_el = 0;
110 while (it != end)
111 {
112 first_node.push_back(atoi((*it).c_str()));
113 it++;
114 nnod_el++;
115 }
116
117 // Check
118 if (nnod_el != 10)
119 {
120 std::ostringstream error_stream;
122 << "XdaTetMesh can currently only be built with quadratic tets "
123 << "with 10 nodes. The specified mesh file has " << nnod_el
124 << "nodes per element.\n";
125 throw OomphLibError(
127 }
128
129 // Storage for the global node numbers listed element-by-element
131
132 // Read in nodes
133 unsigned k = 0;
134
135 // Copy across first nodes
136 for (unsigned j = 0; j < nnod_el; j++)
137 {
139 k++;
140 }
141
142 // Read the other ones
143 for (unsigned i = 1; i < n_element; i++)
144 {
145 for (unsigned j = 0; j < nnod_el; j++)
146 {
147 infile >> global_node[k];
148 k++;
149 }
150 infile.getline(dummy, 100);
151 }
152
153 // Create storage for coordinates
157
158 // Get nodal coordinates
159 for (unsigned i = 0; i < n_node; i++)
160 {
161 infile >> x_node[i];
162 infile >> y_node[i];
163 infile >> z_node[i];
164 }
165
166 // Read in boundaries for faces
167 unsigned element_nmbr;
168 unsigned bound_id;
169 unsigned side_nmbr;
170 unsigned max_bound = 0;
171
172 // Make space for enumeration of sub-boundaries
173 Boundary_id.resize(n_bound_face + 1);
174
175 // Counter for number of separate boundary faces
176 unsigned count = 0;
177 Vector<std::set<unsigned>> boundary_id(n_node);
178 for (unsigned i = 0; i < n_bound_face; i++)
179 {
180 // Number of the element
182
183 // Which side/face on the tet are we dealing with (xda enumeratation)?
184 infile >> side_nmbr;
185
186 // What's the boundary ID?
187 infile >> bound_id;
188
189 // Turn into zero-based oomph-lib mesh boundary id
190 unsigned oomph_lib_bound_id = bound_id - 1;
192 Boundary_id[bound_id].push_back(count);
193
194 // Increment number of separate boundary faces
195 count++;
196
197 // Get ready for allocation of total number of boundaries
199
200 // Identify the "side nodes" (i.e. the nodes on the faces of
201 // the bulk tet) according to the
202 // conventions in '.xda' mesh files so that orientation of the
203 // faces is always the same (required for computation of
204 // outer unit normals
206 switch (side_nmbr)
207 {
208 case 0:
215 break;
216
217 case 1:
224 break;
225
226 case 2:
233 break;
234
235 case 3:
242 break;
243
244 default:
245 throw OomphLibError(
246 "Unexcpected side number in your '.xda' input file\n",
249 }
250
251 // Associate boundaries with nodes
252 for (unsigned j = 0; j < 6; j++)
253 {
254 boundary_id[side_node[j]].insert(oomph_lib_bound_id);
255 }
256 }
257
258 // Set number of boundaries
259 set_nboundary(max_bound + 1);
260
261 // Vector of bools, will tell us if we already visited a node
262 std::vector<bool> done(n_node, false);
263
265 translate[0] = 0;
266 translate[1] = 2;
267 translate[2] = 1;
268 translate[3] = 3;
269 translate[4] = 5;
270 translate[5] = 7;
271 translate[6] = 4;
272 translate[7] = 6;
273 translate[8] = 8;
274 translate[9] = 9;
275
276 // Create the elements
277 unsigned node_count = 0;
278 for (unsigned e = 0; e < n_element; e++)
279 {
280 Element_pt[e] = new ELEMENT;
281
282 // Loop over all nodes
283 for (unsigned j = 0; j < nnod_el; j++)
284 {
285 unsigned j_global = global_node[node_count];
286 if (done[j_global] == false)
287 {
288 if (boundary_id[j_global].size() == 0)
289 {
290 Node_pt[j_global] = finite_element_pt(e)->construct_node(
292 }
293 else
294 {
295 Node_pt[j_global] = finite_element_pt(e)->construct_boundary_node(
297 for (std::set<unsigned>::iterator it =
298 boundary_id[j_global].begin();
299 it != boundary_id[j_global].end();
300 it++)
301 {
302 add_boundary_node(*it, Node_pt[j_global]);
303 }
304 }
305 done[j_global] = true;
309 }
310 else
311 {
312 finite_element_pt(e)->node_pt(translate[j]) = Node_pt[j_global];
313 }
314 node_count++;
315 }
316 }
317
318 // Figure out which elements are next to the boundaries.
319 setup_boundary_element_info();
320
321 // Setup boundary coordinates
322 unsigned nb = nboundary();
323 for (unsigned b = 0; b < nb; b++)
324 {
325 bool switch_normal = false;
326 setup_boundary_coordinates(b, switch_normal);
327 }
328 }
329
330 //======================================================================
331 /// Setup boundary coordinate on boundary b while is
332 /// temporarily flattened to simplex faces. Boundary coordinates are the
333 /// x-y coordinates in the plane of that boundary with the
334 /// x-axis along the line from the (lexicographically)
335 /// "lower left" to the "upper right" node. The y axis
336 /// is obtained by taking the cross-product of the positive
337 /// x direction with the outer unit normal computed by
338 /// the face elements (or its negative if switch_normal is set
339 /// to true). Doc faces in output file.
340 //======================================================================
341 template<class ELEMENT>
343 const unsigned& b, const bool& switch_normal, std::ofstream& outfile)
344 {
345 // Temporary storage for face elements
347
348 // Backup for nodal positions
349 std::map<Node*, Vector<double>> backup_position;
350
351 // Loop over all elements on boundaries
352 unsigned nel = this->nboundary_element(b);
353 if (nel > 0)
354 {
355 // Loop over the bulk elements adjacent to boundary b
356 for (unsigned e = 0; e < nel; e++)
357 {
358 // Get pointer to the bulk element that is adjacent to boundary b
359 FiniteElement* bulk_elem_pt = this->boundary_element_pt(b, e);
360
361 // Find the index of the face of element e along boundary b
362 int face_index = this->face_index_at_boundary(b, e);
363
364 // Create new face element
367 face_el_pt.push_back(el_pt);
368
369 // Backup nodal position
371 for (unsigned j = 3; j < 6; j++)
372 {
373 if (backup_position[el_pt->node_pt(j)].size() == 0)
374 {
377 }
378 }
379
380 // Temporarily flatten the element to a simplex
381 for (unsigned i = 0; i < 3; i++)
382 {
383 // Node 3 is between vertex nodes 0 and 1
384 el_pt->node_pt(3)->x(i) =
385 0.5 * (el_pt->node_pt(0)->x(i) + el_pt->node_pt(1)->x(i));
386
387 // Node 4 is between vertex nodes 1 and 2
388 el_pt->node_pt(4)->x(i) =
389 0.5 * (el_pt->node_pt(1)->x(i) + el_pt->node_pt(2)->x(i));
390
391 // Node 5 is between vertex nodes 2 and 0
392 el_pt->node_pt(5)->x(i) =
393 0.5 * (el_pt->node_pt(2)->x(i) + el_pt->node_pt(0)->x(i));
394 }
395
396 // Output faces?
397 if (outfile.is_open())
398 {
400 }
401 }
402
403 // Loop over all nodes to find the lower left and upper right ones
404 Node* lower_left_node_pt = this->boundary_node_pt(b, 0);
405 Node* upper_right_node_pt = this->boundary_node_pt(b, 0);
406
407 // Loop over all nodes on the boundary
408 unsigned nnod = this->nboundary_node(b);
409 for (unsigned j = 0; j < nnod; j++)
410 {
411 // Get node
412 Node* nod_pt = this->boundary_node_pt(b, j);
413
414 // Primary criterion for lower left: Does it have a lower z-coordinate?
415 if (nod_pt->x(2) < lower_left_node_pt->x(2))
416 {
418 }
419 // ...or is it a draw?
420 else if (nod_pt->x(2) == lower_left_node_pt->x(2))
421 {
422 // If it's a draw: Does it have a lower y-coordinate?
423 if (nod_pt->x(1) < lower_left_node_pt->x(1))
424 {
426 }
427 // ...or is it a draw?
428 else if (nod_pt->x(1) == lower_left_node_pt->x(1))
429 {
430 // If it's a draw: Does it have a lower x-coordinate?
431 if (nod_pt->x(0) < lower_left_node_pt->x(0))
432 {
434 }
435 }
436 }
437
438 // Primary criterion for upper right: Does it have a higher
439 // z-coordinate?
440 if (nod_pt->x(2) > upper_right_node_pt->x(2))
441 {
443 }
444 // ...or is it a draw?
445 else if (nod_pt->x(2) == upper_right_node_pt->x(2))
446 {
447 // If it's a draw: Does it have a higher y-coordinate?
448 if (nod_pt->x(1) > upper_right_node_pt->x(1))
449 {
451 }
452 // ...or is it a draw?
453 else if (nod_pt->x(1) == upper_right_node_pt->x(1))
454 {
455 // If it's a draw: Does it have a higher x-coordinate?
456 if (nod_pt->x(0) > upper_right_node_pt->x(0))
457 {
459 }
460 }
461 }
462 }
463
464 // Prepare storage for boundary coordinates
466
467 // Unit vector connecting lower left and upper right nodes
469 b0[0] = upper_right_node_pt->x(0) - lower_left_node_pt->x(0);
470 b0[1] = upper_right_node_pt->x(1) - lower_left_node_pt->x(1);
471 b0[2] = upper_right_node_pt->x(2) - lower_left_node_pt->x(2);
472
473 // Normalise
474 double inv_length =
475 1.0 / sqrt(b0[0] * b0[0] + b0[1] * b0[1] + b0[2] * b0[2]);
476 b0[0] *= inv_length;
477 b0[1] *= inv_length;
478 b0[2] *= inv_length;
479
480 // Get (outer) unit normal to first face element
482 Vector<double> s(2, 0.0);
483 dynamic_cast<DummyFaceElement<ELEMENT>*>(face_el_pt[0])
484 ->outer_unit_normal(s, normal);
485
486 if (switch_normal)
487 {
488 normal[0] = -normal[0];
489 normal[1] = -normal[1];
490 normal[2] = -normal[2];
491 }
492
493#ifdef PARANOID
494
495 // Check that all elements have the same normal
496 for (unsigned e = 0; e < nel; e++)
497 {
498 // Get (outer) unit normal to face element
500 dynamic_cast<DummyFaceElement<ELEMENT>*>(face_el_pt[0])
501 ->outer_unit_normal(s, my_normal);
502
503 // Dot product should be one!
504 double error = normal[0] * my_normal[0] + normal[1] * my_normal[1] +
505 normal[2] * my_normal[2];
506
507 error -= 1.0;
508 if (switch_normal) error += 1.0;
509
510 if (error > Tolerance_for_boundary_finding)
511 {
512 std::ostringstream error_message;
513 error_message
514 << "Error in alingment of normals (dot product-1) " << error
515 << " for element " << e << std::endl
516 << "exceeds tolerance specified by the static member data\n"
517 << "TetMeshBase::Tolerance_for_boundary_finding = "
518 << Tolerance_for_boundary_finding << std::endl
519 << "This usually means that the boundary is not planar.\n\n"
520 << "You can suppress this error message by recompiling \n"
521 << "recompiling without PARANOID or by changing the tolerance.\n";
522 throw OomphLibError(error_message.str(),
525 }
526 }
527#endif
528
529 // Cross-product to get second in-plane vector, normal to b0
531 b1[0] = b0[1] * normal[2] - b0[2] * normal[1];
532 b1[1] = b0[2] * normal[0] - b0[0] * normal[2];
533 b1[2] = b0[0] * normal[1] - b0[1] * normal[0];
534
535 // Assign boundary coordinates: projection onto the axes
536 for (unsigned j = 0; j < nnod; j++)
537 {
538 // Get node
539 Node* nod_pt = this->boundary_node_pt(b, j);
540
541 // Difference vector to lower left corner
543 delta[0] = nod_pt->x(0) - lower_left_node_pt->x(0);
544 delta[1] = nod_pt->x(1) - lower_left_node_pt->x(1);
545 delta[2] = nod_pt->x(2) - lower_left_node_pt->x(2);
546
547 // Project
548 zeta[0] = delta[0] * b0[0] + delta[1] * b0[1] + delta[2] * b0[2];
549 zeta[1] = delta[0] * b1[0] + delta[1] * b1[1] + delta[2] * b1[2];
550
551#ifdef PARANOID
552
553 // Check:
554 double error = pow(lower_left_node_pt->x(0) + zeta[0] * b0[0] +
555 zeta[1] * b1[0] - nod_pt->x(0),
556 2) +
557 pow(lower_left_node_pt->x(1) + zeta[0] * b0[1] +
558 zeta[1] * b1[1] - nod_pt->x(1),
559 2) +
560 pow(lower_left_node_pt->x(2) + zeta[0] * b0[2] +
561 zeta[1] * b1[2] - nod_pt->x(2),
562 2);
563
564 if (sqrt(error) > Tolerance_for_boundary_finding)
565 {
566 std::ostringstream error_message;
567 error_message
568 << "Error in setup of boundary coordinate " << sqrt(error)
569 << std::endl
570 << "exceeds tolerance specified by the static member data\n"
571 << "TetMeshBase::Tolerance_for_boundary_finding = "
572 << Tolerance_for_boundary_finding << std::endl
573 << "This usually means that the boundary is not planar.\n\n"
574 << "You can suppress this error message by recompiling \n"
575 << "recompiling without PARANOID or by changing the tolerance.\n";
576 throw OomphLibError(error_message.str(),
579 }
580#endif
581
582 // Set boundary coordinate
583 nod_pt->set_coordinates_on_boundary(b, zeta);
584 }
585 }
586
587 // Indicate that boundary coordinate has been set up
588 set_boundary_coordinate_exists(b);
589
590 // Cleanup
591 unsigned n = face_el_pt.size();
592 for (unsigned e = 0; e < n; e++)
593 {
594 delete face_el_pt[e];
595 }
596
597 // Reset nodal position
598 for (std::map<Node*, Vector<double>>::iterator it = backup_position.begin();
599 it != backup_position.end();
600 it++)
601 {
602 Node* nod_pt = (*it).first;
603 Vector<double> pos((*it).second);
604 for (unsigned i = 0; i < 3; i++)
605 {
606 nod_pt->x(i) = pos[i];
607 }
608 }
609 }
610
611} // namespace oomph
612
613#endif
e
Definition cfortran.h:571
static char t char * s
Definition cfortran.h:568
cstr elem_len * i
Definition cfortran.h:603
A general Finite Element class.
Definition elements.h:1317
double size() const
Calculate the size of the element (length, area, volume,...) in Eulerian computational coordinates....
Definition elements.cc:4320
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
TimeStepper *& time_stepper_pt()
Access function for pointer to time stepper: Null if object is not time-dependent.
Nodes are derived from Data, but, in addition, have a definite (Eulerian) position in a space of a gi...
Definition nodes.h:906
void position(Vector< double > &pos) const
Compute Vector of nodal positions either directly or via hanging node representation.
Definition nodes.cc:2499
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....
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 time-stepping schemes. Timestepper provides an approximation of the temporal derivativ...
void setup_boundary_coordinates(const unsigned &b, const bool &switch_normal)
Setup boundary coordinate on boundary b while is temporarily flattened to simplex faces....
XdaTetMesh(const std::string xda_file_name, TimeStepper *time_stepper_pt=&Mesh::Default_TimeStepper)
Constructor: Pass name of xda file. Note that all boundary elements get their own ID – this is requir...
DRAIG: Change all instances of (SPATIAL_DIM) to (DIM-1).