Nektar++
OutputVtk.cpp
Go to the documentation of this file.
1 ////////////////////////////////////////////////////////////////////////////////
2 //
3 // File: OutputVtk.cpp
4 //
5 // For more information, please see: http://www.nektar.info/
6 //
7 // The MIT License
8 //
9 // Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10 // Department of Aeronautics, Imperial College London (UK), and Scientific
11 // Computing and Imaging Institute, University of Utah (USA).
12 //
13 // Permission is hereby granted, free of charge, to any person obtaining a
14 // copy of this software and associated documentation files (the "Software"),
15 // to deal in the Software without restriction, including without limitation
16 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
17 // and/or sell copies of the Software, and to permit persons to whom the
18 // Software is furnished to do so, subject to the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be included
21 // in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24 // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26 // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29 // DEALINGS IN THE SOFTWARE.
30 //
31 // Description: VTK file format output using VTK library.
32 //
33 ////////////////////////////////////////////////////////////////////////////////
34 
35 #include <boost/core/ignore_unused.hpp>
36 #include <boost/format.hpp>
37 
38 #include "OutputVtk.h"
41 
42 #include <vtkCellType.h>
43 #include <vtkDoubleArray.h>
44 #include <vtkInformation.h>
45 #include <vtkMultiBlockDataSet.h>
46 #include <vtkPointData.h>
47 #include <vtkPoints.h>
48 #include <vtkXMLMultiBlockDataWriter.h>
49 #include <vtkXMLUnstructuredGridWriter.h>
50 
51 namespace Nektar
52 {
53 namespace FieldUtils
54 {
55 
57  ModuleKey(eOutputModule, "vtu"), OutputVtk::create, "Writes a VTU file.");
58 
60 {
61  m_requireEquiSpaced = true;
62  m_config["legacy"] =
63  ConfigOption(true, "0", "Output using legacy manual file writers");
64  m_config["highorder"] = ConfigOption(
65  true, "0", "Output using new high-order Lagrange elements");
66  m_config["multiblock"] = ConfigOption(
67  true, "0", "Output using multi-blocks to separate composites");
68  m_config["uncompress"] = ConfigOption(true, "0", "Uncompress xml sections");
69 }
70 
71 // Anonymous namespace for spectral order -> VTK order mapping functions
72 // be aware that these orderings are very similar to gmsh but not the same!
73 namespace
74 {
75 // Hashing function for the ordering map taking two integers
76 inline size_t key2(int i, int j)
77 {
78  return (size_t)i << 32 | (unsigned int)j;
79 }
80 
81 // Hashing function for the ordering map taking three integers
82 inline size_t key3(int i, int j, int k)
83 {
84  return (size_t)i << 10 ^ j << 5 ^ k;
85 }
86 
87 // Map that takes (a,b,c) -> m
88 typedef std::tuple<int, int, int> Mode;
89 
90 struct cmpop
91 {
92  bool operator()(Mode const &a, Mode const &b) const
93  {
94  if (std::get<0>(a) < std::get<0>(b))
95  {
96  return true;
97  }
98  if (std::get<0>(a) > std::get<0>(b))
99  {
100  return false;
101  }
102  if (std::get<1>(a) < std::get<1>(b))
103  {
104  return true;
105  }
106  if (std::get<1>(a) > std::get<1>(b))
107  {
108  return false;
109  }
110  if (std::get<2>(a) < std::get<2>(b))
111  {
112  return true;
113  }
114 
115  return false;
116  }
117 };
118 
119 void Rotate(int nrot, std::vector<long long> &surfVerts)
120 {
121  int n, i, j, cnt;
122  int np = static_cast<int>(
123  (sqrt(8.0 * static_cast<int>(surfVerts.size()) + 1.0) - 1) / 2);
124  std::vector<long long> tmp(np * np);
125 
126  for (n = 0; n < nrot; ++n)
127  {
128  for (cnt = i = 0; i < np; ++i)
129  {
130  for (j = 0; j < np - i; ++j, cnt++)
131  {
132  tmp[i * np + j] = surfVerts[cnt];
133  }
134  }
135  for (cnt = i = 0; i < np; ++i)
136  {
137  for (j = 0; j < np - i; ++j, cnt++)
138  {
139  surfVerts[cnt] = tmp[(np - 1 - i - j) * np + i];
140  }
141  }
142  }
143 }
144 
145 void Reflect(std::vector<long long> &surfVerts)
146 {
147  int i, j, cnt;
148  int np = static_cast<int>(
149  (sqrt(8.0 * static_cast<double>(surfVerts.size()) + 1.0) - 1) / 2);
150  std::vector<long long> tmp(np * np);
151 
152  for (cnt = i = 0; i < np; ++i)
153  {
154  for (j = 0; j < np - i; ++j, cnt++)
155  {
156  tmp[i * np + np - i - 1 - j] = surfVerts[cnt];
157  }
158  }
159 
160  for (cnt = i = 0; i < np; ++i)
161  {
162  for (j = 0; j < np - i; ++j, cnt++)
163  {
164  surfVerts[cnt] = tmp[i * np + j];
165  }
166  }
167 }
168 
169 void Align(std::vector<long long> thisVertId, std::vector<long long> vertId,
170  std::vector<long long> &surfVerts)
171 {
172  if (vertId[0] == thisVertId[0])
173  {
174  if (vertId[1] == thisVertId[1] || vertId[1] == thisVertId[2])
175  {
176  if (vertId[1] == thisVertId[2])
177  {
178  Rotate(1, surfVerts);
179  Reflect(surfVerts);
180  }
181  }
182  }
183  else if (vertId[0] == thisVertId[1])
184  {
185  if (vertId[1] == thisVertId[0] || vertId[1] == thisVertId[2])
186  {
187  if (vertId[1] == thisVertId[0])
188  {
189  Reflect(surfVerts);
190  }
191  else
192  {
193  Rotate(2, surfVerts);
194  }
195  }
196  }
197  else if (vertId[0] == thisVertId[2])
198  {
199  if (vertId[1] == thisVertId[0] || vertId[1] == thisVertId[1])
200  {
201  if (vertId[1] == thisVertId[1])
202  {
203  Rotate(2, surfVerts);
204  Reflect(surfVerts);
205  }
206  else
207  {
208  Rotate(1, surfVerts);
209  }
210  }
211  }
212 }
213 
214 std::vector<long long> quadTensorNodeOrdering(
215  const std::vector<long long> &nodes)
216 {
217  int nN = static_cast<int>(nodes.size());
218  int n = static_cast<int>(sqrt(nN));
219 
220  std::vector<long long> nodeList(nN);
221 
222  // Vertices
223  nodeList[0] = nodes[0];
224  if (n > 1)
225  {
226  nodeList[n - 1] = nodes[1];
227  nodeList[n * n - 1] = nodes[2];
228  nodeList[n * (n - 1)] = nodes[3];
229  }
230 
231  if (n > 2)
232  {
233  // Edge 0 -> 1
234  for (int i = 1; i < n - 1; ++i)
235  {
236  nodeList[i] = nodes[4 + i - 1];
237  }
238 
239  // Edge 3 -> 2
240  for (int i = 1; i < n - 1; ++i)
241  {
242  nodeList[n * (n - 1) + i] = nodes[4 + 2 * (n - 2) + i - 1];
243  }
244 
245  for (int j = 1; j < n - 1; ++j)
246  {
247  // Edge 0 -> 3
248  nodeList[n * (n - j - 1)] = nodes[4 + 3 * (n - 2) + n - 2 - j];
249 
250  // Interior
251  for (int i = 1; i < n - 1; ++i)
252  {
253  nodeList[j * n + i] = nodes[(i - 3) - 2 * j + (3 + j) * n];
254  }
255 
256  // Edge 1 -> 2
257  nodeList[(j + 1) * n - 1] = nodes[4 + (n - 2) + j - 1];
258  }
259  }
260 
261  return nodeList;
262 }
263 
264 std::vector<long long> triTensorNodeOrdering(
265  const std::vector<long long> &nodes)
266 {
267  int nN = static_cast<int>(nodes.size());
268  int n = static_cast<int>(std::sqrt(2 * nN));
269 
270  std::vector<long long> nodeList(nN);
271  int cnt2;
272 
273  // Vertices
274  nodeList[0] = nodes[0];
275  if (n > 1)
276  {
277  nodeList[n - 1] = nodes[1];
278  nodeList[n * (n + 1) / 2 - 1] = nodes[2];
279  }
280 
281  // Edges
282  int cnt = n;
283  for (int i = 1; i < n - 1; ++i)
284  {
285  nodeList[i] = nodes[3 + i - 1];
286  nodeList[cnt] = nodes[3 + 3 * (n - 2) - i];
287  nodeList[cnt + n - i - 1] = nodes[3 + (n - 2) + i - 1];
288  cnt += n - i;
289  }
290 
291  // Interior (recursion)
292  if (n > 3)
293  {
294  // Reorder interior nodes
295  std::vector<long long> interior((n - 3) * (n - 2) / 2);
296  std::copy(nodes.begin() + 3 + 3 * (n - 2), nodes.end(),
297  interior.begin());
298  interior = triTensorNodeOrdering(interior);
299 
300  // Copy into full node list
301  cnt = n;
302  cnt2 = 0;
303  for (int j = 1; j < n - 2; ++j)
304  {
305  for (int i = 0; i < n - j - 2; ++i)
306  {
307  nodeList[cnt + i + 1] = interior[cnt2 + i];
308  }
309  cnt += n - j;
310  cnt2 += n - 2 - j;
311  }
312  }
313 
314  return nodeList;
315 }
316 
317 std::vector<long long> tetTensorNodeOrdering(
318  const std::vector<long long> &nodes)
319 {
320  int nN = static_cast<int>(nodes.size());
321  int n = static_cast<int>(
322  std::cbrt(3 * nN + std::sqrt(9 * nN * nN - 1 / 27)) +
323  std::cbrt(3 * nN - std::sqrt(9 * nN * nN - 1 / 27)) - 0.5);
324 
325  std::vector<long long> nodeList(nN);
326  int nTri = n * (n + 1) / 2;
327  int nTet = n * (n + 1) * (n + 2) / 6;
328 
329  // Vertices
330  nodeList[0] = nodes[0];
331  if (n == 1)
332  {
333  return nodeList;
334  }
335 
336  nodeList[n - 1] = nodes[1];
337  nodeList[nTri - 1] = nodes[2];
338  nodeList[nTet - 1] = nodes[3];
339 
340  if (n == 2)
341  {
342  return nodeList;
343  }
344 
345  // Set up a map that takes (a,b,c) -> m to help us figure out where things
346  // are inside the tetrahedron.
347  std::map<Mode, int, cmpop> tmp;
348  for (int k = 0, cnt = 0; k < n; ++k)
349  {
350  for (int j = 0; j < n - k; ++j)
351  {
352  for (int i = 0; i < n - k - j; ++i)
353  {
354  tmp[Mode(i, j, k)] = cnt++;
355  }
356  }
357  }
358 
359  // Edges first 3
360  for (int i = 1; i < n - 1; ++i)
361  {
362  int eI = i - 1;
363  nodeList[tmp[Mode(i, 0, 0)]] = nodes[4 + eI];
364  nodeList[tmp[Mode(n - 1 - i, i, 0)]] = nodes[4 + (n - 2) + eI];
365  nodeList[tmp[Mode(0, n - 1 - i, 0)]] = nodes[4 + 2 * (n - 2) + eI];
366  }
367 
368  // Edges last 3 reversed (compared with NekMesh)
369  for (int i = 1; i < n - 1; ++i)
370  {
371  int eI = (n - 1 - i) - 1;
372  nodeList[tmp[Mode(0, 0, n - 1 - i)]] = nodes[4 + 3 * (n - 2) + eI];
373  nodeList[tmp[Mode(i, 0, n - 1 - i)]] = nodes[4 + 4 * (n - 2) + eI];
374  nodeList[tmp[Mode(0, i, n - 1 - i)]] = nodes[4 + 5 * (n - 2) + eI];
375  }
376 
377  if (n == 3)
378  {
379  return nodeList;
380  }
381 
382  // For faces, we use the triTensorNodeOrdering routine to make our lives
383  // slightly easier.
384  int nFacePts = (n - 3) * (n - 2) / 2;
385 
386  // Grab face points and reorder into a tensor-product type format
387  std::vector<std::vector<long long>> tmpNodes(4);
388  int offset = 4 + 6 * (n - 2);
389 
390  for (int i = 0; i < 4; ++i)
391  {
392  tmpNodes[i].resize(nFacePts);
393  for (int j = 0; j < nFacePts; ++j)
394  {
395  tmpNodes[i][j] = nodes[offset++];
396  }
397  tmpNodes[i] = triTensorNodeOrdering(tmpNodes[i]);
398  }
399 
400  if (n > 4)
401  {
402  // Now align faces (different to NekMesh)
403  std::vector<long long> triVertId(3), toAlign(3);
404  triVertId[0] = 0;
405  triVertId[1] = 1;
406  triVertId[2] = 2;
407 
408  toAlign[0] = 0;
409  toAlign[1] = 2;
410  toAlign[2] = 1;
411  Align(triVertId, toAlign, tmpNodes[2]);
412  Align(triVertId, toAlign, tmpNodes[3]);
413 
414  toAlign[0] = 2;
415  toAlign[1] = 0;
416  toAlign[2] = 1;
417  Align(triVertId, toAlign, tmpNodes[1]);
418  }
419 
420  // Reordered from NekMesh to put base last
421  for (int j = 1, cnt = 0; j < n - 2; ++j)
422  {
423  for (int i = 1; i < n - j - 1; ++i, ++cnt)
424  {
425  nodeList[tmp[Mode(i, j, 0)]] = tmpNodes[3][cnt];
426  nodeList[tmp[Mode(i, 0, j)]] = tmpNodes[0][cnt];
427  nodeList[tmp[Mode(n - 1 - i - j, i, j)]] = tmpNodes[1][cnt];
428  nodeList[tmp[Mode(0, i, j)]] = tmpNodes[2][cnt];
429  }
430  }
431 
432  if (n == 4)
433  {
434  return nodeList;
435  }
436 
437  // Finally, recurse on interior volume
438  std::vector<long long> intNodes, tmpInt;
439  for (int i = offset; i < nTet; ++i)
440  {
441  intNodes.emplace_back(nodes[i]);
442  }
443 
444  tmpInt = tetTensorNodeOrdering(intNodes);
445 
446  for (int k = 1, cnt = 0; k < n - 2; ++k)
447  {
448  for (int j = 1; j < n - k - 1; ++j)
449  {
450  for (int i = 1; i < n - k - j - 1; ++i)
451  {
452  nodeList[tmp[Mode(i, j, k)]] = tmpInt[cnt++];
453  }
454  }
455  }
456 
457  return nodeList;
458 }
459 
460 // This prism mapping is NOT using the standard spectral mapping! The point
461 // output from ProcessEquiSpacedOutput is not the same as the rest of the code.
462 std::vector<long long> prismTensorNodeOrdering(
463  const std::vector<long long> &nodes)
464 {
465  int nN = static_cast<int>(nodes.size());
466  int n = static_cast<int>(std::cbrt(2 * nN));
467 
468  std::vector<long long> nodeList(nN);
469  int nQuad = n * n;
470  int nPrism = n * n * (n + 1) / 2;
471  int edge = n - 2;
472 
473  // Vertices
474  nodeList[0] = nodes[0];
475  if (n > 1)
476  {
477  nodeList[nPrism - n] = nodes[1];
478  nodeList[n - 1] = nodes[2];
479  nodeList[nQuad - n] = nodes[3];
480  nodeList[nPrism - 1] = nodes[4];
481  nodeList[nQuad - 1] = nodes[5];
482  }
483 
484  if (n == 2)
485  {
486  return nodeList;
487  }
488 
489  int nPts = 6;
490  int cnt = 0;
491  for (int i = 0; i < n - 2; ++i)
492  {
493  // Triangle 1 edges
494  cnt += n * (n - i); // This is adding the quads as they reduce a side by
495  // one each time
496  nodeList[cnt] = nodes[nPts + i]; // Edge 1
497  nodeList[cnt + edge - i] = nodes[nPts + 2 * edge - i - 1]; // Edge 2
498  nodeList[i + 1] = nodes[nPts + 3 * edge - i - 1]; // Edge 3
499  }
500 
501  cnt = nQuad;
502  for (int i = 1; i < n - 1; ++i)
503  {
504  // Triangle 2 edges
505  cnt += n * (n - i); // This is adding the quads as they reduce a side by
506  // one each time
507  nodeList[cnt - n + i] = nodes[nPts + 3 * edge + i - 1]; // Edge 1 (4)
508  nodeList[cnt - 1] = nodes[nPts + 5 * edge - i]; // Edge 2 (5)
509  nodeList[nQuad - i - 1] = nodes[nPts + 5 * edge + i - 1]; // Edge 3 (6)
510  }
511 
512  // Vertical edges
513  for (int i = 1; i < n - 1; ++i)
514  {
515  // Vertical prism edges
516  nodeList[n * i] = nodes[nPts + 6 * edge + i - 1]; // Edge 1 (7)
517  nodeList[nPrism - n + i] = nodes[nPts + 7 * edge + i - 1]; // Edge 2 (8)
518  nodeList[n * i + n - 1] = nodes[nPts + 8 * edge + i - 1]; // Edge 3 (9)
519  }
520 
521  // Do quad faces first as when n == 3 we have no tri faces
522  int nSmallTri = (n - 3) * (n - 2) / 2;
523  int nSmallQuad = (n - 2) * (n - 2);
524  nPts = 6 + 9 * edge + 2 * nSmallTri;
525  cnt = nQuad;
526  for (int i = 1; i < n - 1; ++i)
527  {
528  for (int j = 0; j < n - 2; ++j)
529  {
530  nodeList[cnt + (n - i) + (n - i) * j] =
531  nodes[nPts + j * edge + (i - 1)];
532 
533  nodeList[cnt + 2 * (n - i) - 1 + (n - i) * j] =
534  nodes[nPts + nSmallQuad + (j + 1) * edge - i];
535 
536  nodeList[i * n + j + 1] =
537  nodes[nPts + 2 * nSmallQuad + i * edge - j - 1];
538  }
539 
540  cnt += n * (n - i);
541  }
542 
543  if (n == 3)
544  {
545  return nodeList;
546  }
547 
548  // Triangular alignment
549  std::vector<long long> tmpNodes(nSmallTri);
550  std::iota(tmpNodes.begin(), tmpNodes.end(), 0);
551  if (n > 4)
552  {
553  std::vector<long long> triVertId(3), toAlign(3);
554  triVertId[0] = 0;
555  triVertId[1] = 1;
556  triVertId[2] = 2;
557 
558  toAlign[0] = 0;
559  toAlign[1] = 2;
560  toAlign[2] = 1;
561  Align(triVertId, toAlign, tmpNodes);
562  }
563 
564  // Triangle face 1
565  nPts = 6 + 9 * edge;
566  cnt = nQuad;
567  int cnt2 = 0;
568  for (int i = 1; i < n - 2; ++i)
569  {
570  for (int j = 1; j < n - i - 1; ++j)
571  {
572  nodeList[cnt + j] = nPts + tmpNodes[cnt2++];
573  }
574 
575  cnt += n * (n - i);
576  }
577 
578  // Triangle face 2
579  cnt = nQuad;
580  cnt2 = 0;
581  for (int i = 1; i < n - 2; ++i)
582  {
583  cnt += n * (n - i);
584  for (int j = 1; j < n - i - 1; ++j)
585  {
586  nodeList[cnt - (n - i) + j] = nPts + nSmallTri + tmpNodes[cnt2++];
587  }
588  }
589 
590  // Triangular volume (uses same alignment mapping)
591  nPts = 6 + 9 * edge + 2 * nSmallTri + 3 * nSmallQuad;
592  for (int k = 1; k < n - 1; ++k)
593  {
594  cnt = nQuad;
595  cnt2 = 0;
596  for (int i = 1; i < n - 2; ++i)
597  {
598  for (int j = 1; j < n - i - 1; ++j)
599  {
600  nodeList[cnt + k * (n - i) + j] = nPts + tmpNodes[cnt2++];
601  }
602 
603  cnt += n * (n - i);
604  }
605 
606  nPts += nSmallTri;
607  }
608 
609  return nodeList;
610 }
611 
612 std::vector<long long> hexTensorNodeOrdering(
613  const std::vector<long long> &nodes)
614 {
615  int nN = static_cast<int>(nodes.size());
616  int n = static_cast<int>(std::cbrt(nN));
617 
618  std::vector<long long> nodeList(nN);
619 
620  // Vertices
621  nodeList[0] = nodes[0];
622  if (n == 1)
623  {
624  return nodeList;
625  }
626 
627  // Vertices: same order as Nektar++
628  nodeList[n - 1] = nodes[1];
629  nodeList[n * n - 1] = nodes[2];
630  nodeList[n * (n - 1)] = nodes[3];
631  nodeList[n * n * (n - 1)] = nodes[4];
632  nodeList[n - 1 + n * n * (n - 1)] = nodes[5];
633  nodeList[n * n - 1 + n * n * (n - 1)] = nodes[6];
634  nodeList[n * (n - 1) + n * n * (n - 1)] = nodes[7];
635 
636  if (n == 2)
637  {
638  return nodeList;
639  }
640 
641  int hexEdges[12][2] = {{0, 1},
642  {n - 1, n},
643  {n * n - 1, -1},
644  {n * (n - 1), -n},
645  {0, n * n},
646  {n - 1, n * n},
647  {n * n - 1, n * n},
648  {n * (n - 1), n * n},
649  {n * n * (n - 1), 1},
650  {n * n * (n - 1) + n - 1, n},
651  {n * n * n - 1, -1},
652  {n * n * (n - 1) + n * (n - 1), -n}};
653  int hexFaces[6][3] = {{0, 1, n}, {0, 1, n * n},
654  {n - 1, n, n * n}, {n * (n - 1), 1, n * n},
655  {0, n, n * n}, {n * n * (n - 1), 1, n}};
656 
657  int gmshToNekEdge[12] = {0, 1, -2, -3, 8, 9, -10, -11, 4, 5, 6, 7};
658 
659  // Edges
660  int offset = 8;
661  for (int i : gmshToNekEdge)
662  {
663  int e = abs(i);
664 
665  if (i >= 0)
666  {
667  for (int j = 1; j < n - 1; ++j)
668  {
669  nodeList[hexEdges[e][0] + j * hexEdges[e][1]] = nodes[offset++];
670  }
671  }
672  else
673  {
674  for (int j = 1; j < n - 1; ++j)
675  {
676  nodeList[hexEdges[e][0] + (n - j - 1) * hexEdges[e][1]] =
677  nodes[offset++];
678  }
679  }
680  }
681 
682  // Faces
683  int gmsh2NekFace[6] = {4, 2, 1, 3, 0, 5};
684 
685  // Map which defines orientation between Gmsh and Nektar++ faces.
686  StdRegions::Orientation faceOrient[6] = {
693 
694  for (int i = 0; i < 6; ++i)
695  {
696  int n2 = (n - 2) * (n - 2);
697  int face = gmsh2NekFace[i];
698  offset = 8 + 12 * (n - 2) + i * n2;
699 
700  // Create a list of interior face nodes for this face only.
701  std::vector<long long> faceNodes(n2);
702  for (int j = 0; j < n2; ++j)
703  {
704  faceNodes[j] = nodes[offset + j];
705  }
706 
707  // Now get the reordering of this face, which puts Gmsh
708  // recursive ordering into Nektar++ row-by-row order.
709  std::vector<long long> tmp(n2);
710 
711  // Finally reorient the face according to the geometry
712  // differences.
713  if (faceOrient[i] == StdRegions::eDir1FwdDir1_Dir2FwdDir2)
714  {
715  // Orientation is the same, just copy.
716  tmp = faceNodes;
717  }
718  else if (faceOrient[i] == StdRegions::eDir1FwdDir2_Dir2FwdDir1)
719  {
720  // Tranposed faces
721  for (int j = 0; j < n - 2; ++j)
722  {
723  for (int k = 0; k < n - 2; ++k)
724  {
725  tmp[j * (n - 2) + k] = faceNodes[k * (n - 2) + j];
726  }
727  }
728  }
729  else if (faceOrient[i] == StdRegions::eDir1BwdDir1_Dir2FwdDir2)
730  {
731  for (int j = 0; j < n - 2; ++j)
732  {
733  for (int k = 0; k < n - 2; ++k)
734  {
735  tmp[j * (n - 2) + k] = faceNodes[j * (n - 2) + (n - k - 3)];
736  }
737  }
738  }
739 
740  // Now put this into the right place in the output array
741  for (int k = 1; k < n - 1; ++k)
742  {
743  for (int j = 1; j < n - 1; ++j)
744  {
745  nodeList[hexFaces[face][0] + j * hexFaces[face][1] +
746  k * hexFaces[face][2]] =
747  faceNodes[(k - 1) * (n - 2) + j - 1];
748  }
749  }
750  }
751 
752  // Finally, recurse on interior volume
753  std::vector<long long> intNodes, tmpInt;
754  for (int i = 8 + 12 * (n - 2) + 6 * (n - 2) * (n - 2); i < n * n * n; ++i)
755  {
756  intNodes.push_back(nodes[i]);
757  }
758 
759  if (!intNodes.empty())
760  {
761  tmpInt = hexTensorNodeOrdering(intNodes);
762  for (int k = 1, cnt = 0; k < n - 1; ++k)
763  {
764  for (int j = 1; j < n - 1; ++j)
765  {
766  for (int i = 1; i < n - 1; ++i)
767  {
768  nodeList[i + j * n + k * n * n] = tmpInt[cnt++];
769  }
770  }
771  }
772  }
773 
774  return nodeList;
775 }
776 
777 std::vector<long long> lowOrderMapping(int nDim, Array<OneD, int> nquad)
778 {
779  std::vector<long long> p;
780  switch (nDim)
781  {
782  case 3:
783  for (int j = 0; j < nquad[0] - 1; ++j)
784  {
785  for (int k = 0; k < nquad[1] - 1; ++k)
786  {
787  for (int l = 0; l < nquad[2] - 1; ++l)
788  {
789  p.insert(
790  p.end(),
791  {l * nquad[0] * nquad[1] + k * nquad[0] + j,
792  l * nquad[0] * nquad[1] + k * nquad[0] + j + 1,
793  l * nquad[0] * nquad[1] + (k + 1) * nquad[0] + j +
794  1,
795  l * nquad[0] * nquad[1] + (k + 1) * nquad[0] + j,
796  (l + 1) * nquad[0] * nquad[1] + k * nquad[0] + j,
797  (l + 1) * nquad[0] * nquad[1] + k * nquad[0] + j +
798  1,
799  (l + 1) * nquad[0] * nquad[1] +
800  (k + 1) * nquad[0] + j + 1,
801  (l + 1) * nquad[0] * nquad[1] +
802  (k + 1) * nquad[0] + j});
803  }
804  }
805  }
806  break;
807  case 2:
808  for (int j = 0; j < nquad[0] - 1; ++j)
809  {
810  for (int k = 0; k < nquad[1] - 1; ++k)
811  {
812  p.insert(p.end(), {k * nquad[0] + j, k * nquad[0] + j + 1,
813  (k + 1) * nquad[0] + j + 1,
814  (k + 1) * nquad[0] + j});
815  }
816  }
817  break;
818  case 1:
819  for (int j = 0; j < nquad[0] - 1; ++j)
820  {
821  p.insert(p.end(), {j, j + 1});
822  }
823  break;
824  default:
825  break;
826  }
827 
828  return p;
829 }
830 
831 int GetHighOrderVtkCellType(int sType)
832 {
833 #if VTK_MAJOR_VERSION >= 8
834  // For deformed elements this is a map of shape type to VTK type
835  static const std::map<int, int> vtkCellType = {
836  {{LibUtilities::eSegment, VTK_LAGRANGE_CURVE},
837  {LibUtilities::eTriangle, VTK_LAGRANGE_TRIANGLE},
838  {LibUtilities::eQuadrilateral, VTK_LAGRANGE_QUADRILATERAL},
839  {LibUtilities::eTetrahedron, VTK_LAGRANGE_TETRAHEDRON},
840  {LibUtilities::ePyramid, VTK_LAGRANGE_PYRAMID},
841  {LibUtilities::ePrism, VTK_LAGRANGE_WEDGE},
842  {LibUtilities::eHexahedron, VTK_LAGRANGE_HEXAHEDRON}}};
843 
844  return vtkCellType.at(sType);
845 #else
846  boost::ignore_unused(sType);
847  NEKERROR(
849  "High-order VTK output requires minimum VTK library version of 8.0")
850  return 0;
851 #endif
852 }
853 
854 } // namespace
855 
856 vtkSmartPointer<vtkUnstructuredGrid> OutputVtk::OutputFromExpLowOrder()
857 {
858  // Mesh file to output
859  vtkSmartPointer<vtkUnstructuredGrid> vtkMesh =
860  vtkSmartPointer<vtkUnstructuredGrid>::New();
861 
862  // Cache ordering so we aren't recreating mappings
863  std::unordered_map<size_t, std::vector<long long>> mappingCache;
864 
865  // Choose cell types and num quad points based on dimension
866  int nDim = m_f->m_exp[0]->GetShapeDimension();
867  int nHomoDir = m_f->m_numHomogeneousDir;
868 
869  auto type = (nDim + nHomoDir == 3) ? VTK_HEXAHEDRON
870  : (nDim + nHomoDir == 2) ? VTK_QUAD
871  : VTK_LINE;
872  int nQpts = (nDim + nHomoDir == 3) ? 8 : (nDim + nHomoDir == 2) ? 4 : 2;
873 
874  // Fill homogeneous info
875  if (nHomoDir != 0)
876  {
877  ASSERTL0(nHomoDir == 1 &&
878  m_f->m_exp[0]->GetExpType() == MultiRegions::e3DH1D,
879  "Only regular expansions and the 3DH1D homogeneous expansion "
880  "are supported in the new VTK writer. Please use the 'legacy' "
881  "option for all other expansion types.")
882 
883  m_numPlanes = m_f->m_exp[0]->GetHomogeneousBasis()->GetNumModes();
884  // Extra plane if fourier
885  m_extraPlane = (m_f->m_exp[0]->GetHomogeneousBasis()->GetBasisType() ==
887  m_f->m_exp[0]->GetHomogeneousBasis()->GetPointsType() ==
889  }
890 
891  // Insert points
892  vtkNew<vtkPoints> vtkPoints;
893  vtkPoints->SetDataType(VTK_DOUBLE);
894 
895  int offset = 0;
896  int nElmts = static_cast<int>(m_f->m_exp[0]->GetNumElmts());
897  for (int i = 0; i < nElmts; ++i)
898  {
899  Array<OneD, int> nquad(3, 0);
900  for (int j = 0; j < nDim; ++j)
901  {
902  nquad[j] = m_f->m_exp[0]->GetExp(i)->GetNumPoints(j);
903  }
904 
905  // Add homogeneous direction num points
906  if (nHomoDir != 0)
907  {
908  nquad[nDim] = m_numPlanes;
909 
910  // Add extra plane if fourier
911  if (m_extraPlane)
912  {
913  nquad[nDim]++;
914  }
915  }
916 
917  // Get total points by multiplying all nquads together (accounts for
918  // homo)
919  int nPts = nquad[0];
920  for (int j = 1; j < nDim + nHomoDir; ++j)
921  {
922  nPts *= nquad[j];
923  }
924 
925  // Get all coordinates at quadrature points in this element
926  Array<OneD, NekDouble> x(nPts, 0.0), y(nPts, 0.0), z(nPts, 0.0);
927  m_f->m_exp[0]->GetCoords(i, x, y, z);
928 
929  // If add extra plane for fourier need to fill last plane coordinates
930  if (m_extraPlane)
931  {
932  // Copy x & y to extra plane
934  Vmath::Vcopy(nquad[0] * nquad[1], x, 1,
935  tmp = x + (nquad[2] - 1) * nquad[0] * nquad[1], 1);
936  Vmath::Vcopy(nquad[0] * nquad[1], y, 1,
937  tmp = y + (nquad[2] - 1) * nquad[0] * nquad[1], 1);
938 
939  // Fill z on extra plane
940  NekDouble zDouble = z[nquad[0] * nquad[1] * (nquad[2] - 1) - 1] +
941  (z[nquad[0] * nquad[1]] - z[0]);
942  Vmath::Fill(nquad[0] * nquad[1], zDouble,
943  tmp = z + (nquad[2] - 1) * nquad[0] * nquad[1], 1);
944  }
945 
946  // Insert points in to vtk mesh
947  for (int j = 0; j < nPts; ++j)
948  {
949  vtkPoints->InsertNextPoint(x[j], y[j], z[j]);
950  }
951 
952  // Insert elements
953  auto oIt = mappingCache.find(key3(nquad[0], nquad[1], nquad[2]));
954  if (oIt == mappingCache.end())
955  {
956  auto p = lowOrderMapping(nDim + nHomoDir, nquad);
957  oIt = mappingCache
958  .insert(
959  std::make_pair(key3(nquad[0], nquad[1], nquad[2]), p))
960  .first;
961  }
962 
963  auto p = oIt->second;
964  std::for_each(p.begin(), p.end(),
965  [offset](long long &d) { d += offset; });
966  for (int j = 0; j < p.size(); j += nQpts)
967  {
968  vtkMesh->InsertNextCell(type, nQpts, &p[j]);
969  }
970 
971  offset += nPts;
972  }
973 
974  vtkMesh->SetPoints(vtkPoints.GetPointer());
975 
976  return vtkMesh;
977 }
978 
980  po::variables_map &vm, std::string &filename,
981  vtkSmartPointer<vtkUnstructuredGrid> &vtkMesh)
982 {
983  // Insert field information we iterate by element from first plane to last,
984  // while the fld file iterates by plane from first element to last
985  int nElmts = static_cast<int>(m_f->m_exp[0]->GetNumElmts());
986  int numPtsPerPlane = m_f->m_exp[0]->GetTotPoints() / m_numPlanes;
987  for (int v = 0; v < m_f->m_variables.size(); ++v)
988  {
989  vtkNew<vtkDoubleArray> fieldData;
990  for (int i = 0; i < nElmts; ++i)
991  {
992  int elmtOffset = m_f->m_exp[v]->GetPhys_Offset(i);
993  int nPtsPerElmtPerPlane = m_f->m_exp[v]->GetExp(i)->GetTotPoints();
994 
995  for (int j = 0; j < m_numPlanes; ++j)
996  {
997  int planeOffset = j * numPtsPerPlane;
998  for (int k = 0; k < nPtsPerElmtPerPlane; ++k)
999  {
1000  fieldData->InsertNextValue(
1001  m_f->m_exp[v]->GetPhys()[elmtOffset + planeOffset + k]);
1002  }
1003  }
1004 
1005  // if extra plane we copy the first plane values in to last plane
1006  if (m_extraPlane)
1007  {
1008  for (int k = 0; k < nPtsPerElmtPerPlane; ++k)
1009  {
1010  fieldData->InsertNextValue(
1011  m_f->m_exp[v]->GetPhys()[elmtOffset + k]);
1012  }
1013  }
1014  }
1015 
1016  fieldData->SetName(&m_f->m_variables[v][0]);
1017  vtkMesh->GetPointData()->AddArray(fieldData.GetPointer());
1018  }
1019 
1020  WriteVTK(vtkMesh, filename, vm);
1021 }
1022 
1024  std::string &filename)
1025 {
1026  ASSERTL0(
1027  m_f->m_numHomogeneousDir == 0,
1028  "Multi block VTK is not implemented for homogeneous expansion types.")
1029 
1030  ASSERTL0(m_f->m_comm->GetSpaceComm()->IsSerial(),
1031  "Multi block VTK is not implemented in parallel.")
1032 
1033  int dim = m_f->m_graph->GetMeshDimension();
1034 
1035  // Create mappings from geometry id to expansion ids
1036  std::array<std::map<int, std::pair<int, int>>, 4> geomIdToExpId;
1037  int nElmts = static_cast<int>(m_f->m_exp[0]->GetNumElmts());
1038  for (int i = 0; i < nElmts; ++i)
1039  {
1040  auto geom = m_f->m_exp[0]->GetExp(i)->GetGeom();
1041  geomIdToExpId[geom->GetShapeDim()][geom->GetGlobalID()] =
1042  std::make_pair(i, -1);
1043 
1044  for (int j = 0; j < geom->GetNumFaces(); ++j)
1045  {
1046  geomIdToExpId[2][geom->GetFid(j)] = std::make_pair(i, j);
1047  }
1048 
1049  for (int j = 0; j < geom->GetNumEdges(); ++j)
1050  {
1051  geomIdToExpId[1][geom->GetEid(j)] = std::make_pair(i, j);
1052  }
1053  }
1054 
1055  // Cache ordering so we aren't recreating mappings
1056  std::unordered_map<size_t, std::vector<long long>> mappingCache;
1057 
1058  std::map<int, vtkNew<vtkUnstructuredGrid>> vtkMesh;
1059  std::map<int, SpatialDomains::CompositeSharedPtr> composites =
1060  m_f->m_graph->GetComposites();
1061  std::map<int, std::string> compositeNames;
1062  for (auto &comp : composites)
1063  {
1064  // Vector of field data
1065  std::vector<vtkNew<vtkDoubleArray>> fieldData(m_f->m_variables.size());
1066 
1067  // Insert points
1068  vtkNew<vtkPoints> vtkPoints;
1069  vtkPoints->SetDataType(VTK_DOUBLE);
1070 
1071  int compId = comp.first;
1072  std::vector<std::shared_ptr<SpatialDomains::Geometry>> geomVec =
1073  comp.second->m_geomVec;
1074 
1075  unsigned int offset = 0;
1076  for (auto &geom : geomVec)
1077  {
1078  int geomId = geom->GetGlobalID();
1079  int sDim = geom->GetShapeDim();
1080  auto type = (sDim == 3) ? VTK_HEXAHEDRON
1081  : (sDim == 2) ? VTK_QUAD
1082  : VTK_LINE;
1083  int nQpts = (sDim == 3) ? 8 : (sDim == 2) ? 4 : 2;
1084 
1086  m_f->m_exp[0]->GetExp(geomIdToExpId[sDim][geomId].first);
1087 
1088  unsigned int nPts = exp->GetTotPoints();
1089  Array<OneD, NekDouble> x(nPts, 0.0), y(nPts, 0.0), z(nPts, 0.0);
1090  exp->GetCoords(x, y, z);
1091 
1092  int offsetPhys = m_f->m_exp[0]->GetPhys_Offset(
1093  geomIdToExpId[sDim][geomId].first);
1094  if (sDim == dim)
1095  {
1096  for (int j = 0; j < nPts; ++j)
1097  {
1098  vtkPoints->InsertNextPoint(x[j], y[j], z[j]);
1099 
1100  // Add field data
1101  for (int k = 0; k < m_f->m_variables.size(); ++k)
1102  {
1103  fieldData[k]->InsertNextValue(
1104  m_f->m_exp[k]->GetPhys()[j + offsetPhys]);
1105  }
1106  }
1107  }
1108  else
1109  {
1110  Array<OneD, int> pointsMap;
1111  exp->GetTracePhysMap(geomIdToExpId[sDim][geomId].second,
1112  pointsMap);
1113  for (int j : pointsMap)
1114  {
1115  vtkPoints->InsertNextPoint(x[j], y[j], z[j]);
1116 
1117  // Add field data
1118  for (int k = 0; k < m_f->m_variables.size(); ++k)
1119  {
1120  fieldData[k]->InsertNextValue(
1121  m_f->m_exp[k]->GetPhys()[offsetPhys + j]);
1122  }
1123  }
1124 
1125  exp = exp->GetTraceExp(geomIdToExpId[sDim][geomId].second);
1126  nPts = pointsMap.size();
1127  }
1128 
1129  Array<OneD, int> nquad(3, 0);
1130  for (int j = 0; j < sDim; ++j)
1131  {
1132  nquad[j] = exp->GetNumPoints(j);
1133  }
1134 
1135  auto oIt = mappingCache.find(key3(nquad[0], nquad[1], nquad[2]));
1136  if (oIt == mappingCache.end())
1137  {
1138  auto p = lowOrderMapping(sDim, nquad);
1139  oIt = mappingCache
1140  .insert(std::make_pair(
1141  key3(nquad[0], nquad[1], nquad[2]), p))
1142  .first;
1143  }
1144 
1145  auto p = oIt->second;
1146  std::for_each(p.begin(), p.end(),
1147  [offset](long long &d) { d += offset; });
1148 
1149  for (int j = 0; j < p.size(); j += nQpts)
1150  {
1151  vtkMesh[compId]->InsertNextCell(type, nQpts, &p[j]);
1152  }
1153 
1154  offset += nPts;
1155  }
1156 
1157  vtkMesh[compId]->SetPoints(vtkPoints.GetPointer());
1158 
1159  // Add all fields to vtkMesh
1160  for (int k = 0; k < m_f->m_variables.size(); ++k)
1161  {
1162  fieldData[k]->SetName(&m_f->m_variables[k][0]);
1163  vtkMesh[compId]->GetPointData()->AddArray(
1164  fieldData[k].GetPointer());
1165  }
1166 
1167  // Find composite names if exists and store in map
1168  // Set name as boundary label if it exists otherwise index ID
1169  compositeNames[compId] = "Composite ID " + std::to_string(compId);
1170  auto clabels = m_f->m_graph->GetCompositesLabels();
1171  auto oIt = clabels.find(compId);
1172  if (oIt != clabels.end())
1173  {
1174  compositeNames[compId] = oIt->second;
1175  }
1176  }
1177 
1178  // Main multi-block set
1179  vtkNew<vtkMultiBlockDataSet> mainBlock;
1180 
1181  std::set<int> compSet;
1182 
1183  // Fill domain multi blocks from composites
1184  vtkNew<vtkMultiBlockDataSet> mainDomainBlock;
1185  auto domains = m_f->m_graph->GetDomain();
1186  std::vector<vtkNew<vtkMultiBlockDataSet>> domainMultiBlocks(domains.size());
1187  for (int i = 0; i < domains.size(); ++i)
1188  {
1189  auto dom = domains[i];
1190 
1191  // Loop over composites and see if in domain
1192  for (auto &comp : composites)
1193  {
1194  int compId = comp.first;
1195  if (dom.find(compId) != dom.end())
1196  {
1197  unsigned int nBlock = domainMultiBlocks[i]->GetNumberOfBlocks();
1198  domainMultiBlocks[i]->SetBlock(nBlock,
1199  vtkMesh[compId].GetPointer());
1200  domainMultiBlocks[i]->GetMetaData(nBlock)->Set(
1201  vtkCompositeDataSet::NAME(),
1202  compositeNames[compId].c_str());
1203  compSet.insert(compId);
1204  }
1205  }
1206 
1207  unsigned int nBlock = mainDomainBlock->GetNumberOfBlocks();
1208  mainDomainBlock->SetBlock(nBlock, domainMultiBlocks[i].GetPointer());
1209  mainDomainBlock->GetMetaData(nBlock)->Set(
1210  vtkCompositeDataSet::NAME(),
1211  ("Domain ID " + std::to_string(i)).c_str());
1212  }
1213 
1214  if (mainDomainBlock->GetNumberOfBlocks() != 0)
1215  {
1216  auto nBlock = static_cast<unsigned int>(mainBlock->GetNumberOfBlocks());
1217  mainBlock->SetBlock(nBlock, mainDomainBlock.GetPointer());
1218  mainBlock->GetMetaData(nBlock)->Set(vtkCompositeDataSet::NAME(),
1219  "Domains");
1220  }
1221 
1222  // Fill boundary multi blocks from composites
1223  SpatialDomains::BoundaryConditions bcs(m_f->m_session,
1224  m_f->m_exp[0]->GetGraph());
1226  bcs.GetBoundaryRegions();
1227 
1228  vtkNew<vtkMultiBlockDataSet> mainBoundaryBlock;
1229  std::vector<vtkNew<vtkMultiBlockDataSet>> boundaryMultiBlocks(
1230  bregions.size());
1231  int cnt = 0;
1232  for (auto &boundary : bregions)
1233  {
1234  // Loop over composites and see if in boundary
1235  for (auto &comp : composites)
1236  {
1237  int compId = comp.first;
1238  if (boundary.second->find(compId) != boundary.second->end())
1239  {
1240  unsigned int nBlock =
1241  boundaryMultiBlocks[cnt]->GetNumberOfBlocks();
1242  boundaryMultiBlocks[cnt]->SetBlock(
1243  nBlock, vtkMesh[compId].GetPointer());
1244  boundaryMultiBlocks[cnt]->GetMetaData(nBlock)->Set(
1245  vtkCompositeDataSet::NAME(),
1246  compositeNames[compId].c_str());
1247  compSet.insert(compId);
1248  }
1249  }
1250 
1251  unsigned int nBlock = mainBoundaryBlock->GetNumberOfBlocks();
1252  mainBoundaryBlock->SetBlock(nBlock,
1253  boundaryMultiBlocks[cnt++].GetPointer());
1254 
1255  // Set name as boundary label if it exists otherwise index ID
1256  std::string name = "Boundary ID " + std::to_string(boundary.first);
1257  auto blabels = bcs.GetBoundaryLabels();
1258  auto oIt = blabels.find(boundary.first);
1259  if (oIt != blabels.end())
1260  {
1261  name = oIt->second;
1262  }
1263 
1264  mainBoundaryBlock->GetMetaData(nBlock)->Set(vtkCompositeDataSet::NAME(),
1265  name.c_str());
1266  }
1267 
1268  if (mainBoundaryBlock->GetNumberOfBlocks() != 0)
1269  {
1270  auto nBlock = static_cast<unsigned int>(mainBlock->GetNumberOfBlocks());
1271  mainBlock->SetBlock(nBlock, mainBoundaryBlock.GetPointer());
1272  mainBlock->GetMetaData(nBlock)->Set(vtkCompositeDataSet::NAME(),
1273  "Boundaries");
1274  }
1275 
1276  // Include all other composites (not domains or boundaries)
1277  vtkNew<vtkMultiBlockDataSet> compsMultiBlocks;
1278  cnt = 0;
1279  for (auto &comp : composites)
1280  {
1281  int compId = comp.first;
1282  if (compSet.find(compId) == compSet.end())
1283  {
1284  unsigned int nBlock = compsMultiBlocks->GetNumberOfBlocks();
1285  compsMultiBlocks->SetBlock(nBlock, vtkMesh[compId].GetPointer());
1286  compsMultiBlocks->GetMetaData(nBlock)->Set(
1287  vtkCompositeDataSet::NAME(), compositeNames[compId].c_str());
1288  }
1289  }
1290 
1291  if (compsMultiBlocks->GetNumberOfBlocks() != 0)
1292  {
1293  auto nBlock = static_cast<unsigned int>(mainBlock->GetNumberOfBlocks());
1294  mainBlock->SetBlock(nBlock, compsMultiBlocks.GetPointer());
1295  mainBlock->GetMetaData(nBlock)->Set(vtkCompositeDataSet::NAME(),
1296  "Other composites");
1297  }
1298 
1299  WriteVTK(mainBlock.GetPointer(), filename, vm);
1300 }
1301 
1302 vtkSmartPointer<vtkUnstructuredGrid> OutputVtk::OutputFromExpHighOrder(
1303  po::variables_map &vm)
1304 {
1305  ASSERTL0(
1306  m_f->m_numHomogeneousDir == 0,
1307  "High order VTK is not implemented for homogeneous expansion types.")
1308 
1309  // Save shapetypes before convert to equispaced because that nukes the
1310  // explist
1311  std::vector<LibUtilities::ShapeType> sType;
1312  for (auto &i : *m_f->m_exp[0]->GetExp())
1313  {
1314  sType.emplace_back(i->GetGeom()->GetShapeType());
1315  }
1316 
1317  // Convert expansion to an equispaced points field
1318  auto equispaced =
1320  equispaced->Process(vm);
1321 
1322  // Insert points
1323  vtkNew<vtkPoints> vtkPoints;
1324  LibUtilities::PtsFieldSharedPtr fPts = m_f->m_fieldPts;
1325  vtkPoints->SetDataType(VTK_DOUBLE);
1326 
1328  fPts->GetPts(pts);
1329 
1330  int dim = static_cast<int>(fPts->GetDim());
1331  int nPts = static_cast<int>(fPts->GetNpoints());
1332  switch (dim)
1333  {
1334  case 3:
1335  for (int i = 0; i < nPts; ++i)
1336  {
1337  vtkPoints->InsertNextPoint(pts[0][i], pts[1][i], pts[2][i]);
1338  }
1339  break;
1340  case 2:
1341  for (int i = 0; i < nPts; ++i)
1342  {
1343  vtkPoints->InsertNextPoint(pts[0][i], pts[1][i], 0.0);
1344  }
1345  break;
1346  case 1:
1347  for (int i = 0; i < nPts; ++i)
1348  {
1349  vtkPoints->InsertNextPoint(pts[0][i], 0.0, 0.0);
1350  }
1351  break;
1352  default:
1353  break;
1354  }
1355 
1356  // Mesh file to output
1357  vtkSmartPointer<vtkUnstructuredGrid> vtkMesh =
1358  vtkSmartPointer<vtkUnstructuredGrid>::New();
1359  vtkMesh->SetPoints(vtkPoints.GetPointer());
1360 
1361  // Cache ordering for shape type & npts so we aren't recreating mappings
1362  std::unordered_map<size_t, std::vector<long long>> mappingCache;
1363 
1364  // Get offset per element in a vector from ppe
1365  std::vector<int> ppe = m_f->m_fieldPts->GetPointsPerElement();
1366  Array<OneD, int> ppeOffset(ppe.size() + 1, 0.0);
1367  std::partial_sum(ppe.begin(), ppe.end(), &ppeOffset[1]);
1368 
1369  // Insert elements
1370  for (int i = 0; i < ppe.size(); ++i)
1371  {
1372  // Construct inverse of input reordering.
1373  // First try to find it in our mapping cache.
1374  auto oIt = mappingCache.find(key2(sType[i], ppe[i]));
1375  if (oIt == mappingCache.end())
1376  {
1377  std::vector<long long> p(ppe[i]);
1378  std::iota(p.begin(), p.end(), 0);
1379  switch (sType[i])
1380  {
1382  p = quadTensorNodeOrdering(p);
1383  break;
1385  p = triTensorNodeOrdering(p);
1386  break;
1388  p = tetTensorNodeOrdering(p);
1389  break;
1391  p = hexTensorNodeOrdering(p);
1392  break;
1393  case LibUtilities::ePrism:
1394  p = prismTensorNodeOrdering(p);
1395  break;
1396  default:
1398  "High-order VTU output not set up for the " +
1399  static_cast<std::string>(
1400  LibUtilities::ShapeTypeMap[sType[i]]) +
1401  " shape type.");
1402  break;
1403  }
1404 
1405  // Invert the ordering as this is for spectral -> recursive (VTU)
1406  std::vector<long long> inv(ppe[i]);
1407  for (int j = 0; j < ppe[i]; ++j)
1408  {
1409  inv[p[j]] = j;
1410  }
1411 
1412  oIt =
1413  mappingCache.insert(std::make_pair(key2(sType[i], ppe[i]), inv))
1414  .first;
1415  }
1416 
1417  // Add offset to reordering
1418  std::vector<long long> p = oIt->second;
1419  for (long long &j : p)
1420  {
1421  j += ppeOffset[i];
1422  }
1423 
1424  vtkMesh->InsertNextCell(GetHighOrderVtkCellType(sType[i]), ppe[i],
1425  &p[0]);
1426  }
1427 
1428  return vtkMesh;
1429 }
1430 
1432  po::variables_map &vm, std::string &filename,
1433  vtkSmartPointer<vtkUnstructuredGrid> &vtkMesh)
1434 {
1435  // Convert expansion to an equispaced points field
1436  if (m_cachedMesh)
1437  {
1438  auto equispaced =
1440  equispaced->Process(vm);
1441  }
1442  LibUtilities::PtsFieldSharedPtr fPts = m_f->m_fieldPts;
1443 
1444  int nPts = static_cast<int>(fPts->GetNpoints());
1445  int dim = static_cast<int>(fPts->GetDim());
1446 
1448  fPts->GetPts(pts);
1449 
1450  // Insert field information
1451  for (int i = 0; i < fPts->GetNFields(); ++i)
1452  {
1453  vtkNew<vtkDoubleArray> fieldData;
1454  fieldData->SetArray(&pts[dim + i][0], nPts, 1);
1455  fieldData->SetName(&fPts->GetFieldName(i)[0]);
1456  vtkMesh->GetPointData()->AddArray(fieldData.GetPointer());
1457  }
1458 
1459  WriteVTK(vtkMesh, filename, vm);
1460 }
1461 
1462 void OutputVtk::WriteVTK(vtkDataObject *vtkMesh, std::string &filename,
1463  po::variables_map &vm)
1464 {
1465  // Initialise base writer class as default structured grid writer
1466  vtkSmartPointer<vtkXMLWriter> vtkMeshWriter =
1467  vtkNew<vtkXMLUnstructuredGridWriter>().GetPointer();
1468 
1469  // If multi block we need to switch to the MultiBlock writer
1470  if (m_config["multiblock"].m_beenSet)
1471  {
1472  vtkMeshWriter = vtkNew<vtkXMLMultiBlockDataWriter>().GetPointer();
1473  }
1474 
1475  // Choose the correct extension based on which mesh writer is used
1476  int dot = static_cast<int>(filename.find_last_of('.'));
1477  std::string body = filename.substr(0, dot + 1);
1478  filename = body + vtkMeshWriter->GetDefaultFileExtension();
1479 
1480  // Set time information if exists
1481  if (!m_f->m_fieldMetaDataMap["Time"].empty())
1482  {
1483  vtkMesh->GetInformation()->Set(
1484  vtkDataObject::DATA_TIME_STEP(),
1485  std::stod(m_f->m_fieldMetaDataMap["Time"]));
1486  }
1487  else if (!m_f->m_fieldMetaDataMap["FinalTime"].empty())
1488  {
1489  vtkMesh->GetInformation()->Set(
1490  vtkDataObject::DATA_TIME_STEP(),
1491  std::stod(m_f->m_fieldMetaDataMap["FinalTime"]));
1492  }
1493 
1494  // Write out the new mesh in XML format (don't support legacy
1495  // format here as we still have standard OutputVtk.cpp)
1496  vtkMeshWriter->SetFileName(filename.c_str());
1497 
1498 #if VTK_MAJOR_VERSION <= 5
1499  vtkMeshWriter->SetInput(vtkMesh);
1500 #else
1501  vtkMeshWriter->SetInputData(vtkMesh);
1502 #endif
1503 
1504  if (m_config["uncompress"].m_beenSet)
1505  {
1506  vtkMeshWriter->SetDataModeToAscii();
1507  }
1508 
1509  vtkMeshWriter->Update();
1510 
1511  std::cout << "Written file: " << filename << std::endl;
1512 
1513  // Output parallel file information if needed - using a manual write.
1514  // We could use the VTK lib to do this, but that requires VTK with MPI
1515  // enabled & messing about with the parallel controller & changing
1516  // our file naming scheme as VTK forces _${proc-number} as a suffix...
1517  if (m_f->m_comm->GetSpaceComm()->TreatAsRankZero() &&
1518  !m_f->m_comm->GetSpaceComm()->IsSerial())
1519  {
1520  WritePVtu(vm);
1521  }
1522 }
1523 
1524 void OutputVtk::WritePVtu(po::variables_map &vm)
1525 {
1526  std::string filename = m_config["outfile"].as<std::string>();
1527  int dot = static_cast<int>(filename.find_last_of('.'));
1528  std::string body = filename.substr(0, dot);
1529  filename = body + ".pvtu";
1530 
1531  std::ofstream outfile(filename.c_str());
1532 
1533  int nprocs = m_f->m_comm->GetSpaceComm()->GetSize();
1534  std::string path =
1536 
1537  outfile << "<?xml version=\"1.0\"?>" << endl;
1538  outfile << "<VTKFile type=\"PUnstructuredGrid\" version=\"0.1\" "
1539  << "byte_order=\"LittleEndian\">" << endl;
1540  outfile << " <PUnstructuredGrid GhostLevel=\"0\">" << endl;
1541 
1542  // Add time if exists
1543  if (!m_f->m_fieldMetaDataMap["Time"].empty())
1544  {
1545  outfile << " <FieldData> " << endl;
1546  outfile << " <DataArray type=\"Float64\" Name=\"TimeValue\" "
1547  "NumberOfTuples=\"1\" format=\"ascii\">"
1548  << endl;
1549  outfile << m_f->m_fieldMetaDataMap["Time"] << std::endl;
1550  outfile << " </DataArray>" << std::endl;
1551  outfile << " </FieldData> " << endl;
1552  }
1553 
1554  // Add point coordinates
1555  outfile << " <PPoints> " << endl;
1556  outfile << " <PDataArray type=\"Float64\" NumberOfComponents=\"" << 3
1557  << "\"/> " << endl;
1558  outfile << " </PPoints>" << endl;
1559 
1560  // Add cell information
1561  outfile << " <PCells>" << endl;
1562  outfile << " <PDataArray type=\"Int64\" Name=\"connectivity\" "
1563  "NumberOfComponents=\"1\"/>"
1564  << endl;
1565  outfile << " <PDataArray type=\"Int64\" Name=\"offsets\" "
1566  "NumberOfComponents=\"1\"/>"
1567  << endl;
1568  outfile << " <PDataArray type=\"UInt8\" Name=\"types\" "
1569  "NumberOfComponents=\"1\"/>"
1570  << endl;
1571  outfile << " </PCells>" << endl;
1572 
1573  // Add fields (point data)
1574  outfile << " <PPointData>" << endl;
1575  for (auto &var : m_f->m_variables)
1576  {
1577  outfile << " <PDataArray type=\"Float64\" Name=\"" << var << "\"/>"
1578  << endl;
1579  }
1580  outfile << " </PPointData>" << endl;
1581 
1582  // Add parallel files
1583  for (int i = 0; i < nprocs; ++i)
1584  {
1585  boost::format pad("P%1$07d.vtu");
1586  pad % i;
1587  outfile << " <Piece Source=\"" << path << "/" << pad.str() << "\"/>"
1588  << endl;
1589  }
1590  outfile << " </PUnstructuredGrid>" << endl;
1591  outfile << "</VTKFile>" << endl;
1592 
1593  cout << "Written file: " << filename << endl;
1594 }
1595 
1596 void OutputVtk::v_OutputFromData(po::variables_map &vm)
1597 {
1598  boost::ignore_unused(vm);
1600  "OutputVtk can't write using only FieldData. You may need "
1601  "to add a mesh XML file to your input files.");
1602 }
1603 
1604 void OutputVtk::v_OutputFromPts(po::variables_map &vm)
1605 {
1607 }
1608 
1609 void OutputVtk::v_OutputFromExp(po::variables_map &vm)
1610 {
1611  if (m_config["legacy"].m_beenSet)
1612  {
1613  ASSERTL0(!m_config["multiblock"].m_beenSet,
1614  "Multi block VTK is not implemented for legacy output.")
1615 
1616  ASSERTL0(!m_config["highorder"].m_beenSet,
1617  "High order VTK is not implemented for legacy output.")
1618 
1619  // No caching of mesh data in legacy output
1621  return;
1622  }
1623 
1624  // Extract the output filename and extension
1625  std::string filename = OutputVtkBase::PrepareOutput(vm);
1626 
1627  // Save mesh state (if using filter this allows us to only ProcessEquispaced
1628  // if needed)
1630 
1631  if (m_config["highorder"].m_beenSet)
1632  {
1633  ASSERTL0(!m_config["multiblock"].m_beenSet,
1634  "Multi block VTK is not implemented for high-order output.")
1635 
1636  if (!m_cachedMesh)
1637  {
1639  }
1640 
1641  AddFieldDataToVTKHighOrder(vm, filename, m_vtkMesh);
1642  }
1643  else if (m_config["multiblock"].m_beenSet)
1644  {
1645  // No mesh caching for multiblock due to complexity of field data
1646  OutputFromExpLowOrderMultiBlock(vm, filename);
1647  }
1648  else
1649  {
1650  if (!m_cachedMesh)
1651  {
1653  }
1654 
1655  AddFieldDataToVTKLowOrder(vm, filename, m_vtkMesh);
1656  }
1657 }
1658 
1659 } // namespace FieldUtils
1660 } // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define NEKERROR(type, msg)
Assert Level 0 – Fundamental assert which is used whether in FULLDEBUG, DEBUG or OPT compilation mode...
Definition: ErrorUtil.hpp:209
FieldSharedPtr m_f
Field object.
Definition: Module.h:234
std::map< std::string, ConfigOption > m_config
List of configuration values.
Definition: Module.h:263
Converter from fld to vtk.
Definition: OutputVtkBase.h:48
std::string PrepareOutput(po::variables_map &vm)
virtual fs::path v_GetPath(std::string &filename, po::variables_map &vm) override
virtual void v_OutputFromExp(po::variables_map &vm) override
Write from m_exp to output file.
virtual void v_OutputFromPts(po::variables_map &vm) override
Write from pts to output file.
vtkSmartPointer< vtkUnstructuredGrid > OutputFromExpLowOrder()
Prepare low order VTK output.
Definition: OutputVtk.cpp:856
void AddFieldDataToVTKHighOrder(po::variables_map &vm, std::string &filename, vtkSmartPointer< vtkUnstructuredGrid > &vtkMesh)
Add field data to high order Lagrange VTK output.
Definition: OutputVtk.cpp:1431
void OutputFromExpLowOrderMultiBlock(po::variables_map &vm, std::string &filename)
Prepare low order multi-block VTK output & add field data.
Definition: OutputVtk.cpp:1023
OutputVtk(FieldSharedPtr f)
Definition: OutputVtk.cpp:59
vtkSmartPointer< vtkUnstructuredGrid > m_vtkMesh
Cache file for unstructured grid VTK mesh data.
Definition: OutputVtk.h:82
bool m_extraPlane
Flag if extra plane in case of fourier expansion in homogeneous dir.
Definition: OutputVtk.h:88
int m_numPlanes
Number of planes if homogeneous.
Definition: OutputVtk.h:85
virtual void v_OutputFromPts(po::variables_map &vm) override final
Write from pts to output file.
Definition: OutputVtk.cpp:1604
void AddFieldDataToVTKLowOrder(po::variables_map &vm, std::string &filename, vtkSmartPointer< vtkUnstructuredGrid > &vtkMesh)
Add field data to low order VTK output.
Definition: OutputVtk.cpp:979
void WriteVTK(vtkDataObject *vtkMesh, std::string &filename, po::variables_map &vm)
Write VTK file using.
Definition: OutputVtk.cpp:1462
static std::shared_ptr< Module > create(const FieldSharedPtr &f)
Creates an instance of this class.
Definition: OutputVtk.h:55
vtkSmartPointer< vtkUnstructuredGrid > OutputFromExpHighOrder(po::variables_map &vm)
Prepare high order Lagrange VTK output.
Definition: OutputVtk.cpp:1302
static ModuleKey m_className
Definition: OutputVtk.h:60
virtual void v_OutputFromData(po::variables_map &vm) override final
Write from data to output file.
Definition: OutputVtk.cpp:1596
virtual void v_OutputFromExp(po::variables_map &vm) override final
Write from m_exp to output file.
Definition: OutputVtk.cpp:1609
bool m_cachedMesh
Flag if mesh has been cached.
Definition: OutputVtk.h:91
void WritePVtu(po::variables_map &vm)
Write the parallel .pvtu file.
Definition: OutputVtk.cpp:1524
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:198
static std::shared_ptr< DataType > AllocateSharedPtr(const Args &...args)
Allocate a shared pointer from the memory pool.
const BoundaryRegionCollection & GetBoundaryRegions(void) const
Definition: Conditions.h:234
const std::map< int, std::string > & GetBoundaryLabels(void) const
Definition: Conditions.h:265
def copy(self)
Definition: pycml.py:2663
std::shared_ptr< Field > FieldSharedPtr
Definition: Field.hpp:991
std::pair< ModuleType, std::string > ModuleKey
Definition: Module.h:317
ModuleFactory & GetModuleFactory()
Definition: Module.cpp:49
const char *const ShapeTypeMap[SIZE_ShapeType]
Definition: ShapeType.hpp:79
std::string PortablePath(const boost::filesystem::path &path)
create portable path on different platforms for boost::filesystem path
Definition: FileSystem.cpp:45
std::shared_ptr< PtsField > PtsFieldSharedPtr
Definition: PtsField.h:190
@ eFourierEvenlySpaced
1D Evenly-spaced points using Fourier Fit
Definition: PointsType.h:76
@ eFourier
Fourier Expansion .
Definition: BasisType.h:57
std::shared_ptr< Expansion > ExpansionSharedPtr
Definition: Expansion.h:68
std::map< int, BoundaryRegionShPtr > BoundaryRegionCollection
Definition: Conditions.h:210
std::tuple< unsigned int, unsigned int, unsigned int, unsigned int > Mode
Definition: StdPyrExp.h:47
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
double NekDouble
void Fill(int n, const T alpha, T *x, const int incx)
Fill a vector with a constant value.
Definition: Vmath.cpp:45
void Vcopy(int n, const T *x, const int incx, T *y, const int incy)
Definition: Vmath.cpp:1255
scalarT< T > abs(scalarT< T > in)
Definition: scalar.hpp:298
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:294
Represents a command-line configuration option.
Definition: Module.h:131