The two examples of this section will demonstrate each input method. Figure 14 shows the volume that will be meshed. It has six bounding surfaces that are topologically quadrilaterals, a prerequist for volume mapping.
Figure 14: Volume mapper input
In the first method, the surface description (points and quadrilaterals) is read from a file as shown in the code below.
// read input file int *quads = NULL; // array allocated in read_surf_file double *points = NULL; // array allocated in read_surf_file int num_points = 0; int num_quads = 0; if (!read_surf_file(num_points, points, num_quads, quads)) { exit(EXIT_FAILURE); }
The call to read_surf_file(...) returns an array of points that is three times num_points in length. The coordinates are stored in sequential order: x1, y1, z1, x2, y2, z2, ... xn, yn, zn.
The quadrilaterals are also returned with each quadrilateral described by the four vertices or nodes, i.e., the index in the points array of the corresponding corner of the quadrilateral. Internally, the arrays are zero-based so the reader decrements all of the node numbers by 1. The quads array is four times the num_quads in length.
The next section of code shows how the surface description is passed to the volume mesher.
// mesh the volume int *hexes = NULL; int new_points = 0; int num_hexes = 0; CMLVolMapper v_mapper; ret_value = v_mapper.set_boundary_mesh(num_points, points, num_quads, quads); if (!ret_value) { printf("Failed setting boundary mesh\n"); } // delete memory allocated in read_surf_file delete [] points; delete [] quads; points = NULL;
A volume mapper, v_mapper, is instantiated. The call to v_mapper.set_boundary_mesh(...) passes the points and quads to the volume mapper. Now the memory for the arrays that was allocated in read_surf_file(...) is no longer needed and is deleted. The points pointer is set to NULL since it will be used again.
// generate the mesh if (ret_value) { ret_value = v_mapper.generate_mesh(new_points, num_hexes); if (!ret_value) { printf("Failed generating mesh\n"); } } // allocate memory to accept hex mesh and retrieve it if (ret_value) { hexes = new int [num_hexes * 8]; points = new double [new_points * 3]; ret_value = v_mapper.get_mesh(new_points, points, num_hexes, hexes); if (!ret_value) { printf("Failed reading hex mesh\n"); } }
A call to v_mapper.generate_mesh(...) causes the meshing algorithm to sort through the quadrilaterals to find the logical corners of the volume and prepare an ordered list of the points. With the ordered points list, it gnerates the hexhedral mesh and returns the number of total points in new_points and the number of hexahedra in num_hexes.
The sample code then allocates memory to retrieve the points and hexes from the mesher. In this example, the mesh is retrieved in one step. In the second example, the mesh will be retrieved one buffer at a time to save copying the entire mesh and therefore, limiting the size of mesh one may generate.
// write output file if (ret_value) { ret_value = write_hex_file(new_points, points, num_hexes, hexes); }
In the code above, a call to write_hex_file(...) writes the mesh to a file with new_points and num_hexes on the first line followed by points (point_id x y z) repeated num_points times and hexes (hex_id, n1, n2, n3, n4, n5, n6, n7, n8) repeated num_hexes times. In the input and output files, numbering is one-based so internal zero-based arrays indices are incremented by one. The node ordering for a hexahedron is the four nodes of the front face in counter-clockwise order followed by the four nodes of the back face such that the first node of the front and back faces are connected by an edge as are the second node of the front and back faces, etc.
Figure 15 show three slices of hexahedra through the volume after generating the volume mapped mesh.
Figure 15: Three slices of generated hexahedra
The second example differs from the first in two ways:
// read input file double *points = NULL; // array allocated in read_surf_file int num_points = 0; int i_ints, j_ints, k_ints; if (!read_points_file(num_points, points, i_ints, j_ints, k_ints)) { printf("Failed read input\n"); exit(EXIT_FAILURE); }
The code above reads num_points from a file. It also returns the number of intervals in the three parametric directions (i_ints, j_ints, k_ints.) The number of intervals is one less than the number of points on the curve. Think number of elements and not number of points.
// mesh the volume int *hexes = NULL; int new_points = 0; int num_hexes = 0; CMLVolMapper v_mapper; ret_value = v_mapper.set_boundary_points(num_points, points, k_ints, j_ints, i_ints); if (!ret_value) { printf("Failed setting boundary mesh\n"); } // delete memory allocated in read_points_file delete [] points; // generate the mesh if (ret_value) { ret_value = v_mapper.generate_mesh(new_points, num_hexes); if (!ret_value) { printf("Failed generating mesh\n"); } }
A volume mesh object, v_mapper, is instantiated and calls v_mapper.set_boundary_points(...) to pass the information read from the file to the volume mesher. The points are ordered by their i, j, k coordinates, where i varies most rapidly, followed by j and then k. Only the exterior points are included in the array.
In Figure 14, assume that the origin is at node 71, the i-direction extends from node 71 to node 111, the j-direction from node 71 to node 67, and the k-direction from node 71 to node 1. Then the ordered points array would begin with nodes 71, 155, 156, 157, 111 (bottom row of front-face), followed by the rest of the nodes on the bottom. Next the nodes on the second k-slice beginning with nodes 83, 170, 169, 168, 167, 112 on the near-face, then 102 and its corresponding node on the opposite side, and across the slice until node 70 and the other nodes on the far-face and this slice. This continues up through each slice in the k-direction until all the nodes on the last k-face are in the list. The last nodes being node 17, 21, 20, 19, 18, 7.
The call to generate the mesh is the same as in example one. The sorting of quadrilaterals and finding logical corners is skipped because the points are already in the desired order.
// write output file if (ret_value) { ret_value = write_hex_file(v_mapper, new_points, num_hexes); if (!ret_value) { printf("Failed writing hex mesh file\n"); } }
When write_hex_file(...) is called in this example, the v_mapper object is passed as a parameter. It will be used to retrieve buffers of points and hexahedra in the code snippet below.
bool write_hex_file(CMLVolMapper& mapper, int npoints, int nhexes) { FILE* file = fopen("volmapper2.out", "w"); if (file == NULL) { printf("Failed to open output file!\n"); return false; } // write number of points and hexes fprintf(file, "%8d %7d\n", npoints, nhexes); // write points (node no., x, y, z) int i; const int BUF_SIZE = 4096; double pbuf[BUF_SIZE]; int npnts = 0; int num = mapper.get_points_buf(BUF_SIZE, pbuf); while (num > 0) { double* p = pbuf; for (i = 0; i < num; i++) { fprintf(file, "%8d %14.7e %14.7e %14.7e\n", i+1, p[0], p[1], p[2]); p += 3; } npnts += num; num = mapper.get_points_buf(BUF_SIZE, pbuf, npnts); }
After opening the output file and writing the number of points and number of hexahedra, the program gets a buffer of points with the call mapper.get_points_buf(...), which returns the number of points in the buffer. As long as this number is greater than zero, the while-loop writes the points to the file, increments the number of points read, and reads another buffer. It is important to pass the number of points read to mapper.get_points_buf(...) on the second and subsequent calls because that is the beginning point of the next buffer. The default value of zero always gets the first points.
// write hexahedra (hex no., n1, n2, n3, n4, n5, n6, n7, n8) // NOTE: increment internal zero-based index by 1 int nhxs = 0; if (ret_val) { int hbuf[BUF_SIZE]; num = mapper.get_hexes_buf(BUF_SIZE, hbuf); while (num > 0) { int* h = hbuf; for (i = 0; i < num; i++) { fprintf(file, "%8d %7d %7d %7d %7d %7d %7d %7d %7d\n", nhxs + i + 1, h[0]+1, h[1]+1, h[2]+1, h[3]+1, h[4]+1, h[5]+1, h[6]+1, h[7]+1); h += 8; } nhxs += num; num = mapper.get_hexes_buf(BUF_SIZE, hbuf, nhxs); } ret_val = nhxs == nhexes; } fclose(file); return ret_val;
Finally, an algorithm similar to the one for points is used to write the hexahedra to the file. The algorithm is read a buffer, while the buffer has data, write it to the file, and read another buffer. The program closes the output files and returns.
CAMAL 5.2-0 documentation created on 1 Jun 2010
Comments to csimsoft.com