Skip to content

Commit 2af0065

Browse files
authored
Implement VTK I/O for arbitrary degree tetrahedron cells (FEniCS#2985)
* simplify code * Generalise tets * remove cout * Fix tet vtk perms
1 parent 70ae092 commit 2af0065

File tree

2 files changed

+408
-47
lines changed

2 files changed

+408
-47
lines changed

cpp/dolfinx/io/cells.cpp

+259-47
Original file line numberDiff line numberDiff line change
@@ -33,20 +33,17 @@ int cell_degree(mesh::CellType type, int num_nodes)
3333
return n - 1;
3434
}
3535
case mesh::CellType::tetrahedron:
36-
// n * (n + 1) * (n + 2) = 6*A
37-
// return n - 1;
38-
switch (num_nodes)
36+
{
37+
int n = 0;
38+
while (n * (n + 1) * (n + 2) < 6 * num_nodes)
39+
++n;
40+
if (n * (n + 1) * (n + 2) != 6 * num_nodes)
3941
{
40-
case 4:
41-
return 1;
42-
case 10:
43-
return 2;
44-
case 20:
45-
return 3;
46-
default:
4742
throw std::runtime_error("Unknown tetrahedron layout. Number of nodes: "
4843
+ std::to_string(num_nodes));
4944
}
45+
return n - 1;
46+
}
5047
case mesh::CellType::quadrilateral:
5148
{
5249
const int n = std::sqrt(num_nodes);
@@ -102,10 +99,44 @@ std::uint16_t vec_pop(std::vector<std::uint16_t>& v, int i)
10299
return value;
103100
}
104101
//-----------------------------------------------------------------------------
102+
std::vector<std::uint16_t>
103+
vtk_triangle_remainders(std::vector<std::uint16_t> remainders)
104+
{
105+
std::vector<std::uint16_t> map(remainders.size());
106+
std::size_t j = 0;
107+
int degree;
108+
while (remainders.size() > 0)
109+
{
110+
if (remainders.size() == 1)
111+
{
112+
map[j++] = vec_pop(remainders, 0);
113+
break;
114+
}
115+
116+
degree = cell_degree(mesh::CellType::triangle, remainders.size());
117+
118+
map[j++] = vec_pop(remainders, 0);
119+
map[j++] = vec_pop(remainders, degree - 1);
120+
map[j++] = vec_pop(remainders, -1);
121+
122+
for (int i = 0; i < degree - 1; ++i)
123+
map[j++] = vec_pop(remainders, 0);
124+
125+
for (int i = 1, k = degree * (degree - 1) / 2; i < degree;
126+
k -= degree - i, ++i)
127+
map[j++] = vec_pop(remainders, -k);
128+
129+
for (int i = 1, k = 1; i < degree; k += i, ++i)
130+
map[j++] = vec_pop(remainders, -k);
131+
}
132+
133+
return map;
134+
}
135+
//-----------------------------------------------------------------------------
105136
std::vector<std::uint16_t> vtk_triangle(int num_nodes)
106137
{
107-
// Vertices
108138
std::vector<std::uint16_t> map(num_nodes);
139+
// Vertices
109140
std::iota(map.begin(), map.begin() + 3, 0);
110141

111142
int j = 3;
@@ -123,51 +154,239 @@ std::vector<std::uint16_t> vtk_triangle(int num_nodes)
123154
// Interior VTK is ordered as a lower order triangle, while FEniCS
124155
// orders them lexicographically.
125156
std::vector<std::uint16_t> remainders(num_nodes - j);
126-
std::iota(remainders.begin(), remainders.end(), 0);
127-
const std::uint16_t base = 3 * degree;
157+
std::iota(remainders.begin(), remainders.end(), 3 * degree);
158+
159+
for (std::uint16_t r : vtk_triangle_remainders(remainders))
160+
{
161+
map[j++] = r;
162+
}
163+
return map;
164+
}
165+
//-----------------------------------------------------------------------------
166+
std::vector<std::uint16_t>
167+
vtk_tetrahedron_remainders(std::vector<std::uint16_t> remainders)
168+
{
169+
std::vector<std::uint16_t> map(remainders.size());
170+
std::size_t j = 0;
128171

129172
while (remainders.size() > 0)
130173
{
131174
if (remainders.size() == 1)
132175
{
133-
map[j++] = base + vec_pop(remainders, 0);
176+
map[j++] = vec_pop(remainders, 0);
134177
break;
135178
}
136179

137-
degree = cell_degree(mesh::CellType::triangle, remainders.size());
180+
const int deg
181+
= cell_degree(mesh::CellType::tetrahedron, remainders.size()) + 1;
138182

139-
map[j++] = base + vec_pop(remainders, 0);
140-
map[j++] = base + vec_pop(remainders, degree - 1);
141-
map[j++] = base + vec_pop(remainders, -1);
183+
map[j++] = vec_pop(remainders, 0);
184+
map[j++] = vec_pop(remainders, deg - 2);
185+
map[j++] = vec_pop(remainders, deg * (deg + 1) / 2 - 3);
186+
map[j++] = vec_pop(remainders, -1);
142187

143-
for (int i = 0; i < degree - 1; ++i)
144-
map[j++] = base + vec_pop(remainders, 0);
188+
if (deg > 2)
189+
{
190+
for (int i = 0; i < deg - 2; ++i)
191+
{
192+
map[j++] = vec_pop(remainders, 0);
193+
}
194+
int d = deg - 2;
195+
for (int i = 0; i < deg - 2; ++i)
196+
{
197+
map[j++] = vec_pop(remainders, d);
198+
d += deg - 3 - i;
199+
}
200+
d = (deg - 2) * (deg - 1) / 2 - 1;
201+
for (int i = 0; i < deg - 2; ++i)
202+
{
203+
map[j++] = vec_pop(remainders, d);
204+
d -= 2 + i;
205+
}
206+
d = (deg - 3) * (deg - 2) / 2;
207+
for (int i = 0; i < deg - 2; ++i)
208+
{
209+
map[j++] = vec_pop(remainders, d);
210+
d += (deg - i) * (deg - i - 1) / 2 - 1;
211+
}
212+
d = (deg - 3) * (deg - 2) / 2 + deg - 3;
213+
for (int i = 0; i < deg - 2; ++i)
214+
{
215+
map[j++] = vec_pop(remainders, d);
216+
d += (deg - 2 - i) * (deg - 1 - i) / 2 + deg - 4 - i;
217+
}
218+
d = (deg - 3) * (deg - 2) / 2 + deg - 3 + (deg - 2) * (deg - 1) / 2 - 1;
219+
for (int i = 0; i < deg - 2; ++i)
220+
{
221+
map[j++] = vec_pop(remainders, d);
222+
d += (deg - 3 - i) * (deg - 2 - i) / 2 + deg - i - 5;
223+
}
224+
}
225+
if (deg > 3)
226+
{
227+
std::vector<std::uint16_t> dofs((deg - 3) * (deg - 2) / 2);
228+
int di = 0;
229+
int d = (deg - 3) * (deg - 2) / 2;
230+
for (int i = 0; i < deg - 3; ++i)
231+
{
232+
for (int ii = 0; ii < deg - 3 - i; ++ii)
233+
dofs[di++] = vec_pop(remainders, d);
234+
d += (deg - 2 - i) * (deg - 1 - i) / 2 - 1;
235+
}
236+
for (std::uint16_t r : vtk_triangle_remainders(dofs))
237+
map[j++] = r;
145238

146-
for (int i = 1, k = degree * (degree - 1) / 2; i < degree;
147-
k -= degree - i, ++i)
148-
map[j++] = base + vec_pop(remainders, -k);
239+
di = 0;
240+
int start = deg * deg - 4 * deg + 2;
241+
int sub_i_start = deg - 3;
242+
for (int i = 0; i < deg - 3; ++i)
243+
{
244+
d = start;
245+
int sub_i = sub_i_start;
246+
for (int ii = 0; ii < deg - 3 - i; ++ii)
247+
{
248+
dofs[di++] = vec_pop(remainders, d);
249+
d += sub_i * (sub_i + 1) / 2 - 2 - i;
250+
sub_i -= 1;
251+
}
252+
start -= 2 + i;
253+
}
254+
for (std::uint16_t r : vtk_triangle_remainders(dofs))
255+
map[j++] = r;
149256

150-
for (int i = 1, k = 1; i < degree; k += i, ++i)
151-
map[j++] = base + vec_pop(remainders, -k);
152-
}
257+
di = 0;
258+
start = (deg - 3) * (deg - 2) / 2;
259+
sub_i_start = deg - 3;
260+
for (int i = 0; i < deg - 3; ++i)
261+
{
262+
d = start;
263+
int sub_i = sub_i_start;
264+
for (int ii = 0; ii < deg - 3 - i; ++ii)
265+
{
266+
dofs[di++] = vec_pop(remainders, d);
267+
d += sub_i * (sub_i + 1) / 2 - 1 - 2 * i;
268+
sub_i -= 1;
269+
}
270+
start += deg - 4 - i;
271+
}
272+
for (std::uint16_t r : vtk_triangle_remainders(dofs))
273+
map[j++] = r;
153274

275+
di = 0;
276+
int add_start = deg - 4;
277+
for (int i = 0; i < deg - 3; ++i)
278+
{
279+
d = 0;
280+
int add = add_start;
281+
for (int ii = 0; ii < deg - 3 - i; ++ii)
282+
{
283+
dofs[di++] = vec_pop(remainders, d);
284+
d += add;
285+
add -= 1;
286+
}
287+
add_start -= 1;
288+
}
289+
for (std::uint16_t r : vtk_triangle_remainders(dofs))
290+
map[j++] = r;
291+
}
292+
}
154293
return map;
155294
}
156295
//-----------------------------------------------------------------------------
157296
std::vector<std::uint16_t> vtk_tetrahedron(int num_nodes)
158297
{
159-
switch (num_nodes)
298+
const int degree = cell_degree(mesh::CellType::tetrahedron, num_nodes);
299+
300+
std::vector<std::uint16_t> map(num_nodes);
301+
// Vertices
302+
std::iota(map.begin(), map.begin() + 4, 0);
303+
304+
if (degree < 2)
305+
return map;
306+
307+
int base = 4;
308+
int j = 4;
309+
const int edge_dofs = degree - 1;
310+
for (int edge : std::vector<int>({5, 2, 4, 3, 1, 0}))
160311
{
161-
case 4:
162-
return {0, 1, 2, 3};
163-
case 10:
164-
return {0, 1, 2, 3, 9, 6, 8, 7, 5, 4};
165-
case 20:
166-
return {0, 1, 2, 3, 14, 15, 8, 9, 13, 12,
167-
10, 11, 6, 7, 4, 5, 18, 16, 17, 19};
168-
default:
169-
throw std::runtime_error("Unknown tetrahedron layout");
312+
if (edge == 4)
313+
{
314+
for (int i = 0; i < edge_dofs; ++i)
315+
{
316+
map[j++] = base + edge_dofs * (edge + 1) - 1 - i;
317+
}
318+
}
319+
else
320+
{
321+
for (int i = 0; i < edge_dofs; ++i)
322+
{
323+
map[j++] = base + edge_dofs * edge + i;
324+
}
325+
}
170326
}
327+
base += 6 * edge_dofs;
328+
329+
if (degree < 3)
330+
return map;
331+
332+
const int n_face_dofs = (degree - 1) * (degree - 2) / 2;
333+
334+
for (int face : std::vector<int>({2, 0, 1, 3}))
335+
{
336+
std::vector<std::uint16_t> face_dofs(n_face_dofs);
337+
std::size_t fj = 0;
338+
if (face == 2)
339+
{
340+
for (int i = 0; i < n_face_dofs; ++i)
341+
{
342+
face_dofs[fj++] = base + n_face_dofs * face + i;
343+
}
344+
}
345+
else if (face == 0)
346+
{
347+
for (int i = degree - 3; i >= 0; --i)
348+
{
349+
int d = i;
350+
for (int ii = 0; ii <= i; ++ii)
351+
{
352+
face_dofs[fj++] = base + n_face_dofs * face + d;
353+
d += degree - 3 - ii;
354+
}
355+
}
356+
}
357+
else
358+
{
359+
for (int i = 0; i < degree - 2; ++i)
360+
{
361+
int d = i;
362+
for (int ii = 0; ii < degree - 2 - i; ++ii)
363+
{
364+
face_dofs[fj++] = base + n_face_dofs * face + d;
365+
d += degree - 2 - ii;
366+
}
367+
}
368+
}
369+
for (std::uint16_t r : vtk_triangle_remainders(face_dofs))
370+
{
371+
map[j++] = r;
372+
}
373+
}
374+
375+
base += 4 * n_face_dofs;
376+
377+
if (degree < 4)
378+
return map;
379+
380+
std::vector<std::uint16_t> remainders((degree - 1) * (degree - 2)
381+
* (degree - 3) / 6);
382+
std::iota(remainders.begin(), remainders.end(), base);
383+
384+
for (std::uint16_t r : vtk_tetrahedron_remainders(remainders))
385+
{
386+
map[j++] = r;
387+
}
388+
389+
return map;
171390
}
172391
//-----------------------------------------------------------------------------
173392
std::vector<std::uint16_t> vtk_wedge(int num_nodes)
@@ -198,13 +417,7 @@ std::vector<std::uint16_t> vtk_pyramid(int num_nodes)
198417
//-----------------------------------------------------------------------------
199418
std::vector<std::uint16_t> vtk_quadrilateral(int num_nodes)
200419
{
201-
// Check that num_nodes is a square integer (since quadrilaterals are
202-
// tensor products of intervals, the number of nodes for each interval
203-
// should be an integer)
204-
assert((std::sqrt(num_nodes) - std::floor(std::sqrt(num_nodes))) == 0);
205-
206-
// Number of nodes in each direction
207-
const int n = sqrt(num_nodes);
420+
const int n = cell_degree(mesh::CellType::quadrilateral, num_nodes);
208421
std::vector<std::uint16_t> map(num_nodes);
209422

210423
// Vertices
@@ -215,7 +428,7 @@ std::vector<std::uint16_t> vtk_quadrilateral(int num_nodes)
215428

216429
int j = 4;
217430

218-
const int edge_nodes = n - 2;
431+
const int edge_nodes = n - 1;
219432

220433
// Edges
221434
for (int k = 0; k < edge_nodes; ++k)
@@ -235,8 +448,7 @@ std::vector<std::uint16_t> vtk_quadrilateral(int num_nodes)
235448
//-----------------------------------------------------------------------------
236449
std::vector<std::uint16_t> vtk_hexahedron(int num_nodes)
237450
{
238-
const int n = std::cbrt(num_nodes);
239-
assert(n * n * n == num_nodes);
451+
std::uint16_t n = cell_degree(mesh::CellType::hexahedron, num_nodes);
240452

241453
std::vector<std::uint16_t> map(num_nodes);
242454

@@ -253,7 +465,7 @@ std::vector<std::uint16_t> vtk_hexahedron(int num_nodes)
253465
// Edges
254466
int j = 8;
255467
int base = 8;
256-
const int edge_nodes = n - 2;
468+
const int edge_nodes = n - 1;
257469
const std::vector<int> edges = {0, 3, 5, 1, 8, 10, 11, 9, 2, 4, 7, 6};
258470
for (int e : edges)
259471
{

0 commit comments

Comments
 (0)