GetFEM  5.5
getfem_export.cc
1 /*===========================================================================
2 
3  Copyright (C) 2001-2026 Yves Renard
4 
5  This file is a part of GetFEM
6 
7  GetFEM is free software; you can redistribute it and/or modify it
8  under the terms of the GNU Lesser General Public License as published
9  by the Free Software Foundation; either version 3 of the License, or
10  (at your option) any later version along with the GCC Runtime Library
11  Exception either version 3.1 or (at your option) any later version.
12  This program is distributed in the hope that it will be useful, but
13  WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
14  or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public
15  License and GCC Runtime Library Exception for more details.
16  You should have received a copy of the GNU Lesser General Public License
17  along with this program. If not, see https://www.gnu.org/licenses/.
18 
19 ===========================================================================*/
20 
21 #include <iomanip>
22 #include "getfem/dal_singleton.h"
24 #include "getfem/getfem_export.h"
25 
26 namespace getfem
27 {
28 
29  /* try to check if a quad or hexahedric cell is "rectangular" and oriented
30  along the axes */
31  template<typename C> static bool check_voxel(const C& c) {
32  scalar_type h[3];
33  unsigned N = c[0].size();
34  if (c.size() != (1U << N)) return false;
35  const base_node P0 = c[0];
36  h[0] = c[1][0] - P0[0];
37  h[1] = c[2][0] - P0[0];
38  if (c.size() != 4) h[2] = c[4][0] - P0[0];
39  for (unsigned i=1; i < c.size(); ++i) {
40  const base_node d = c[i] - P0;
41  for (unsigned j=0; j < N; ++j)
42  if (gmm::abs(d[j]) > 1e-7*h[j] && gmm::abs(d[j] - h[j]) > 1e-7*h[j])
43  return false;
44  }
45  return true;
46  }
47 
48  /* Base64 encoding (https://en.wikipedia.org/wiki/Base64) */
49  std::string base64_encode(const std::vector<unsigned char>& src)
50  {
51  const std::string table("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/");
52  std::string dst;
53  for (size_type i = 0; i < src.size(); ++i) {
54  switch (i % 3) {
55  case 0:
56  dst.push_back(table[(src[i] & 0xFC) >> 2]);
57  if (i + 1 == src.size()) {
58  dst.push_back(table[(src[i] & 0x03) << 4]);
59  dst.push_back('=');
60  dst.push_back('=');
61  }
62  break;
63  case 1:
64  dst.push_back(table[((src[i - 1] & 0x03) << 4) | ((src[i + 0] & 0xF0) >> 4)]);
65  if (i + 1 == src.size()) {
66  dst.push_back(table[(src[i] & 0x0F) << 2]);
67  dst.push_back('=');
68  }
69  break;
70  case 2:
71  dst.push_back(table[((src[i - 1] & 0x0F) << 2) | ((src[i + 0] & 0xC0) >> 6)]);
72  dst.push_back(table[src[i] & 0x3F]);
73  break;
74  }
75  }
76  return dst;
77  }
78 
79  /* -------------------------------------------------------------
80  * VTK export
81  * ------------------------------------------------------------- */
82 
83  struct gf2vtk_dof_mapping : public std::vector<std::vector<unsigned> > {};
84  struct gf2vtk_vtk_type : public std::vector<int> {};
85 
86  typedef enum { VTK_VERTEX = 1,
87  VTK_LINE = 3,
88  VTK_TRIANGLE = 5,
89  VTK_PIXEL = 8,
90  VTK_QUAD = 9,
91  VTK_TETRA = 10,
92  VTK_VOXEL = 11,
93  VTK_HEXAHEDRON = 12,
94  VTK_WEDGE = 13,
95  VTK_PYRAMID = 14,
96  VTK_QUADRATIC_EDGE = 21,
97  VTK_QUADRATIC_TRIANGLE = 22,
98  VTK_QUADRATIC_QUAD = 23,
99  VTK_QUADRATIC_TETRA = 24,
100  VTK_QUADRATIC_HEXAHEDRON = 25,
101  VTK_QUADRATIC_WEDGE = 26,
102  VTK_QUADRATIC_PYRAMID = 27,
103  VTK_BIQUADRATIC_QUAD = 28,
104  VTK_TRIQUADRATIC_HEXAHEDRON = 29,
105  VTK_BIQUADRATIC_QUADRATIC_WEDGE = 32 } vtk_cell_type;
106 
107  typedef enum { NO_VTK_MAPPING,
108  N1_TO_VTK_VERTEX,
109  N2_TO_VTK_LINE,
110  N3_TO_VTK_TRIANGLE,
111  N4_TO_VTK_PIXEL,
112  N4_TO_VTK_QUAD,
113  N4_TO_VTK_TETRA,
114  N8_TO_VTK_VOXEL,
115  N8_TO_VTK_HEXAHEDRON,
116  N6_TO_VTK_WEDGE,
117  N5_TO_VTK_PYRAMID,
118  N3_TO_VTK_QUADRATIC_EDGE,
119  N6_TO_VTK_QUADRATIC_TRIANGLE,
120  N8_TO_VTK_QUADRATIC_QUAD,
121  N10_TO_VTK_QUADRATIC_TETRA,
122  N20_TO_VTK_QUADRATIC_HEXAHEDRON,
123  N15_TO_VTK_QUADRATIC_WEDGE,
124  N13_TO_VTK_QUADRATIC_PYRAMID,
125  N14_TO_VTK_QUADRATIC_PYRAMID,
126  N9_TO_VTK_BIQUADRATIC_QUAD,
127  N27_TO_VTK_TRIQUADRATIC_HEXAHEDRON,
128  N18_TO_VTK_BIQUADRATIC_QUADRATIC_WEDGE } vtk_mapping_type;
129 
130 
131  void init_gf2vtk() {
132  // see https://www.kitware.com/products/books/VTKUsersGuide.pdf
133  // and https://www.kitware.com/products/books/VTKTextbook.pdf
134  // (there are some conflicts between the two)
135  gf2vtk_dof_mapping &vtkmaps = dal::singleton<gf2vtk_dof_mapping>::instance();
136  gf2vtk_vtk_type &vtktypes = dal::singleton<gf2vtk_vtk_type>::instance();
137  vtkmaps.resize(25);
138  vtktypes.resize(25);
139 
140  vtktypes[N1_TO_VTK_VERTEX] = VTK_VERTEX;
141  vtkmaps [N1_TO_VTK_VERTEX] = {0};
142  vtktypes[N2_TO_VTK_LINE] = VTK_LINE;
143  vtkmaps [N2_TO_VTK_LINE] = {0, 1};
144  vtktypes[N3_TO_VTK_TRIANGLE] = VTK_TRIANGLE;
145  vtkmaps [N3_TO_VTK_TRIANGLE] = {0, 1, 2};
146  vtktypes[N4_TO_VTK_PIXEL] = VTK_PIXEL;
147  vtkmaps [N4_TO_VTK_PIXEL] = {0, 1, 2, 3};
148  vtktypes[N4_TO_VTK_QUAD] = VTK_QUAD;
149  vtkmaps [N4_TO_VTK_QUAD] = {0, 1, 3, 2};
150  vtktypes[N4_TO_VTK_TETRA] = VTK_TETRA;
151  vtkmaps [N4_TO_VTK_TETRA] = {0, 1, 2, 3};
152  vtktypes[N8_TO_VTK_VOXEL] = VTK_VOXEL;
153  vtkmaps [N8_TO_VTK_VOXEL] = {0, 1, 2, 3, 4, 5, 6, 7};
154  vtktypes[N8_TO_VTK_HEXAHEDRON] = VTK_HEXAHEDRON;
155  vtkmaps [N8_TO_VTK_HEXAHEDRON] = {0, 1, 3, 2, 4, 5, 7, 6};
156  vtktypes[N6_TO_VTK_WEDGE] = VTK_WEDGE;
157  vtkmaps [N6_TO_VTK_WEDGE] = {0, 1, 2, 3, 4, 5};
158  vtktypes[N5_TO_VTK_PYRAMID] = VTK_PYRAMID;
159  vtkmaps [N5_TO_VTK_PYRAMID] = {0, 1, 3, 2, 4};
160  vtktypes[N3_TO_VTK_QUADRATIC_EDGE] = VTK_QUADRATIC_EDGE;
161  vtkmaps [N3_TO_VTK_QUADRATIC_EDGE] = {0, 2, 1};
162  vtktypes[N6_TO_VTK_QUADRATIC_TRIANGLE] = VTK_QUADRATIC_TRIANGLE;
163  vtkmaps [N6_TO_VTK_QUADRATIC_TRIANGLE] = {0, 2, 5, 1, 4, 3};
164  vtktypes[N8_TO_VTK_QUADRATIC_QUAD] = VTK_QUADRATIC_QUAD;
165  vtkmaps [N8_TO_VTK_QUADRATIC_QUAD] = {0, 2, 7, 5, 1, 4, 6, 3};
166  vtktypes[N10_TO_VTK_QUADRATIC_TETRA] = VTK_QUADRATIC_TETRA;
167  vtkmaps [N10_TO_VTK_QUADRATIC_TETRA] = {0, 2, 5, 9, 1, 4, 3, 6, 7, 8};
168  vtktypes[N20_TO_VTK_QUADRATIC_HEXAHEDRON] = VTK_QUADRATIC_HEXAHEDRON;
169  vtkmaps [N20_TO_VTK_QUADRATIC_HEXAHEDRON] = {0, 2, 7, 5, 12, 14, 19, 17, 1, 4, 6, 3, 13, 16, 18, 15, 8, 9, 11, 10};
170  vtktypes[N15_TO_VTK_QUADRATIC_WEDGE] = VTK_QUADRATIC_WEDGE;
171  vtkmaps [N15_TO_VTK_QUADRATIC_WEDGE] = {0, 2, 5, 9, 11, 14, 1, 4, 3, 10, 13, 12, 6, 7, 8};
172  // = {0, 5, 2, 9, 14, 11, 3, 4, 1, 12, 13, 10, 6, 8, 7};
173  vtktypes[N13_TO_VTK_QUADRATIC_PYRAMID] = VTK_QUADRATIC_PYRAMID;
174  vtkmaps [N13_TO_VTK_QUADRATIC_PYRAMID] = {0, 2, 7, 5, 12, 1, 4, 6, 3, 8, 9, 11, 10};
175  vtktypes[N14_TO_VTK_QUADRATIC_PYRAMID] = VTK_QUADRATIC_PYRAMID;
176  vtkmaps [N14_TO_VTK_QUADRATIC_PYRAMID] = {0, 2, 8, 6, 13, 1, 5, 7, 3, 9, 10, 12, 11};
177  vtktypes[N9_TO_VTK_BIQUADRATIC_QUAD] = VTK_BIQUADRATIC_QUAD;
178  vtkmaps [N9_TO_VTK_BIQUADRATIC_QUAD] = {0, 2, 8, 6, 1, 5, 7, 3, 4};
179  vtktypes[N27_TO_VTK_TRIQUADRATIC_HEXAHEDRON] = VTK_TRIQUADRATIC_HEXAHEDRON;
180  vtkmaps [N27_TO_VTK_TRIQUADRATIC_HEXAHEDRON] = {0, 2, 8, 6, 18, 20, 26, 24, 1, 5, 7, 3, 19, 23, 25, 21, 9, 11, 17, 15, 12, 14, 10, 16, 4, 22};
181  vtktypes[N18_TO_VTK_BIQUADRATIC_QUADRATIC_WEDGE] = VTK_BIQUADRATIC_QUADRATIC_WEDGE;
182  vtkmaps [N18_TO_VTK_BIQUADRATIC_QUADRATIC_WEDGE] = {0, 2, 5, 12, 14, 17, 1, 4, 3, 13, 16, 15, 6, 8, 11, 7, 10, 9};
183  }
184 
185  static const std::vector<unsigned> &
186  select_vtk_dof_mapping(unsigned t) {
187  gf2vtk_dof_mapping &vtkmaps = dal::singleton<gf2vtk_dof_mapping>::instance();
188  if (vtkmaps.size() == 0) init_gf2vtk();
189  return vtkmaps[t];
190  }
191 
192  int select_vtk_type(unsigned t) {
193  gf2vtk_vtk_type &vtktypes = dal::singleton<gf2vtk_vtk_type>::instance();
194  if (vtktypes.size() == 0) init_gf2vtk();
195  return vtktypes[t];
196  }
197 
198 
199  vtk_export::vtk_export(std::ostream &os_, bool ascii_, bool vtk_)
200  : os(os_), ascii(ascii_), vtk(vtk_) { init(); }
201 
202  vtk_export::vtk_export(const std::string& fname, bool ascii_, bool vtk_)
203  : os(real_os), ascii(ascii_), vtk(vtk_),
204  real_os(fname.c_str(), !ascii ? std::ios_base::binary | std::ios_base::out
205  : std::ios_base::out) {
206  GMM_ASSERT1(real_os, "impossible to write to file '" << fname << "'");
207  init();
208  }
209 
210  vtk_export::~vtk_export(){
211  if (!vtk) {
212  if (state == IN_CELL_DATA) os << "</CellData>\n";
213  if (state == IN_POINT_DATA) os << "</PointData>\n";
214  os << "</Piece>\n";
215  os << "</UnstructuredGrid>\n";
216  os << "</VTKFile>\n";
217  }
218  }
219 
220  void vtk_export::init() {
221  strcpy(header, "Exported by GetFEM");
222  psl = 0; dim_ = dim_type(-1);
223  static int test_endian = 0x01234567;
224  reverse_endian = (*((char*)&test_endian) == 0x67);
225  state = EMPTY;
226  clear_vals();
227  }
228 
229  void vtk_export::switch_to_cell_data() {
230  if (state != IN_CELL_DATA) {
231  if (vtk) {
232  size_type nb_cells=0;
233  if (psl) for (auto i : {0,1,2,3}) nb_cells += psl->nb_simplexes(i);
234  else nb_cells = pmf->convex_index().card();
235  write_separ();
236  os << "CELL_DATA " << nb_cells << "\n";
237  write_separ();
238  } else {
239  if (state == IN_POINT_DATA) os << "</PointData>\n";
240  os << "<CellData>\n";
241  }
242  state = IN_CELL_DATA;
243  }
244  }
245 
246  void vtk_export::switch_to_point_data() {
247  if (state != IN_POINT_DATA) {
248  if (vtk) {
249  write_separ();
250  os << "POINT_DATA " << (psl ? psl->nb_points()
251  : pmf_dof_used.card()) << "\n";
252  write_separ();
253  } else {
254  if (state == IN_CELL_DATA) os << "</CellData>\n";
255  os << "<PointData>\n";
256  }
257  state = IN_POINT_DATA;
258  }
259  }
260 
261 
262  void vtk_export::exporting(const stored_mesh_slice& sl) {
263  psl = &sl; dim_ = dim_type(sl.dim());
264  GMM_ASSERT1(psl->dim() <= 3, "attempt to export a " << int(dim_)
265  << "D slice (not supported)");
266  }
267 
268  void vtk_export::exporting(const mesh& m) {
269  dim_ = m.dim();
270  GMM_ASSERT1(dim_ <= 3, "attempt to export a " << int(dim_)
271  << "D mesh (not supported)");
272  pmf = std::make_unique<mesh_fem>(const_cast<mesh&>(m), dim_type(1));
273  for (dal::bv_visitor cv(m.convex_index()); !cv.finished(); ++cv) {
274  bgeot::pgeometric_trans pgt = m.trans_of_convex(cv);
275  pfem pf = getfem::classical_fem(pgt, pgt->complexity() > 1 ? 2 : 1);
276  pmf->set_finite_element(cv, pf);
277  }
278  exporting(*pmf);
279  }
280 
281  void vtk_export::exporting(const mesh_fem& mf) {
282  dim_ = mf.linked_mesh().dim();
283  GMM_ASSERT1(dim_ <= 3, "attempt to export a " << int(dim_)
284  << "D mesh_fem (not supported)");
285  if (&mf != pmf.get())
286  pmf = std::make_unique<mesh_fem>(mf.linked_mesh());
287  /* initialize pmf with finite elements suitable for VTK (which only knows
288  isoparametric FEMs of order 1 and 2) */
289  for (dal::bv_visitor cv(mf.convex_index()); !cv.finished(); ++cv) {
290  bgeot::pgeometric_trans pgt = mf.linked_mesh().trans_of_convex(cv);
291  pfem pf = mf.fem_of_element(cv);
292 
293  if (pf == fem_descriptor("FEM_Q2_INCOMPLETE(2)") ||
294  pf == fem_descriptor("FEM_Q2_INCOMPLETE(3)") ||
295  pf == fem_descriptor("FEM_PYRAMID_Q2_INCOMPLETE") ||
296  pf == fem_descriptor("FEM_PYRAMID_Q2_INCOMPLETE_DISCONTINUOUS") ||
297  pf == fem_descriptor("FEM_PRISM_INCOMPLETE_P2") ||
298  pf == fem_descriptor("FEM_PRISM_INCOMPLETE_P2_DISCONTINUOUS"))
299  pmf->set_finite_element(cv, pf);
300  else {
301  bool discontinuous = false;
302  for (unsigned i=0; i < pf->nb_dof(cv); ++i) {
303  /* could be a better test for discontinuity .. */
304  if (!dof_linkable(pf->dof_types()[i])) { discontinuous = true; break; }
305  }
306 
307  pfem classical_pf1 = discontinuous ? classical_discontinuous_fem(pgt, 1)
308  : classical_fem(pgt, 1);
309 
310  short_type degree = 1;
311  if ((pf != classical_pf1 && pf->estimated_degree() > 1) ||
312  pgt->structure() != pgt->basic_structure())
313  degree = 2;
314 
315  pmf->set_finite_element(cv, discontinuous ?
316  classical_discontinuous_fem(pgt, degree, 0, true) :
317  classical_fem(pgt, degree, true));
318  }
319  }
320  /* find out which dof will be exported to VTK/VTU */
321 
322  const mesh &m = pmf->linked_mesh();
323  pmf_mapping_type.resize(pmf->convex_index().last_true() + 1, unsigned(-1));
324  pmf_dof_used.sup(0, pmf->nb_basic_dof());
325  for (dal::bv_visitor cv(pmf->convex_index()); !cv.finished(); ++cv) {
326  vtk_mapping_type t = NO_VTK_MAPPING;
327  size_type nbd = pmf->fem_of_element(cv)->nb_dof(cv);
328  switch (pmf->fem_of_element(cv)->dim()) {
329  case 0: t = N1_TO_VTK_VERTEX; break;
330  case 1:
331  if (nbd == 2) t = N2_TO_VTK_LINE;
332  else if (nbd == 3) t = N3_TO_VTK_QUADRATIC_EDGE;
333  break;
334  case 2:
335  if (nbd == 3) t = N3_TO_VTK_TRIANGLE;
336  else if (nbd == 4)
337  t = check_voxel(m.points_of_convex(cv)) ? N4_TO_VTK_PIXEL
338  : N4_TO_VTK_QUAD;
339  else if (nbd == 6) t = N6_TO_VTK_QUADRATIC_TRIANGLE;
340  else if (nbd == 8) t = N8_TO_VTK_QUADRATIC_QUAD;
341  else if (nbd == 9) t = N9_TO_VTK_BIQUADRATIC_QUAD;
342  break;
343  case 3:
344  if (nbd == 4) t = N4_TO_VTK_TETRA;
345  else if (nbd == 10) t = N10_TO_VTK_QUADRATIC_TETRA;
346  else if (nbd == 8)
347  t = check_voxel(m.points_of_convex(cv)) ? N8_TO_VTK_VOXEL
348  : N8_TO_VTK_HEXAHEDRON;
349  else if (nbd == 20) t = N20_TO_VTK_QUADRATIC_HEXAHEDRON;
350  else if (nbd == 27) t = N27_TO_VTK_TRIQUADRATIC_HEXAHEDRON;
351  else if (nbd == 5) t = N5_TO_VTK_PYRAMID;
352  else if (nbd == 13) t = N13_TO_VTK_QUADRATIC_PYRAMID;
353  else if (nbd == 14) t = N14_TO_VTK_QUADRATIC_PYRAMID;
354  else if (nbd == 6) t = N6_TO_VTK_WEDGE;
355  else if (nbd == 15) t = N15_TO_VTK_QUADRATIC_WEDGE;
356  else if (nbd == 18) t = N18_TO_VTK_BIQUADRATIC_QUADRATIC_WEDGE;
357  break;
358  }
359  GMM_ASSERT1(t != -1, "semi internal error. Could not map " <<
360  name_of_fem(pmf->fem_of_element(cv))
361  << " to a VTK cell type");
362  pmf_mapping_type[cv] = t;
363 
364  const std::vector<unsigned> &dmap = select_vtk_dof_mapping(t);
365  //cout << "nbd = " << nbd << ", t = " << t << ", dmap = "<<dmap << "\n";
366  GMM_ASSERT1(dmap.size() <= pmf->nb_basic_dof_of_element(cv),
367  "inconsistency in vtk_dof_mapping");
368  for (unsigned i=0; i < dmap.size(); ++i)
369  pmf_dof_used.add(pmf->ind_basic_dof_of_element(cv)[dmap[i]]);
370  }
371  // cout << "mf.nb_dof = " << mf.nb_dof() << ", pmf->nb_dof="
372  // << pmf->nb_dof() << ", dof_used = " << pmf_dof_used.card() << "\n";
373  }
374 
375 
376  const stored_mesh_slice& vtk_export::get_exported_slice() const {
377  GMM_ASSERT1(psl, "no slice!");
378  return *psl;
379  }
380 
381  const mesh_fem& vtk_export::get_exported_mesh_fem() const {
382  GMM_ASSERT1(pmf.get(), "no mesh_fem!");
383  return *pmf;
384  }
385 
386  void vtk_export::set_header(const std::string& s)
387  {
388  strncpy(header, s.c_str(), 256);
389  header[255] = 0;
390  }
391 
392  void vtk_export::check_header() {
393  if (state >= HEADER_WRITTEN) return;
394  if (vtk) {
395  os << "# vtk DataFile Version 2.0\n";
396  os << header << "\n";
397  os << (ascii ? "ASCII\n" : "BINARY\n");
398  } else {
399  os << "<?xml version=\"1.0\"?>\n";
400  os << "<VTKFile type=\"UnstructuredGrid\" version=\"0.1\" ";
401  os << "byte_order=\"" << (reverse_endian ? "LittleEndian" : "BigEndian") << "\">\n";
402  os << "<!--" << header << "-->\n";
403  os << "<UnstructuredGrid>\n";
404  }
405  state = HEADER_WRITTEN;
406  }
407 
408  void vtk_export::write_separ()
409  { if (ascii) os << "\n"; }
410 
411  void vtk_export::clear_vals()
412  { if (!vtk && !ascii) vals.clear(); }
413 
414  void vtk_export::write_vals() {
415  if (!vtk && !ascii) {
416  os << base64_encode(vals);
417  clear_vals();
418  }
419  }
420 
421  void vtk_export::write_mesh() {
422  if (psl) write_mesh_structure_from_slice();
423  else write_mesh_structure_from_mesh_fem();
424  }
425 
426  /* export the slice data as an unstructured mesh composed of simplexes */
427  void vtk_export::write_mesh_structure_from_slice() {
428  /* element type code for (linear) simplexes of dimensions 0,1,2,3 in VTK */
429  static int vtk_simplex_code[4] = { VTK_VERTEX, VTK_LINE, VTK_TRIANGLE, VTK_TETRA };
430  if (state >= STRUCTURE_WRITTEN) return;
431  check_header();
432  /* count total number of simplexes, and total number of entries */
433  size_type cells_cnt = 0, splx_cnt = 0;
434  for (size_type ic=0; ic < psl->nb_convex(); ++ic) {
435  for (const slice_simplex &s : psl->simplexes(ic))
436  cells_cnt += s.dim() + 2;
437  splx_cnt += psl->simplexes(ic).size();
438  }
439  if (vtk) {
440  /* possible improvement: detect structured grids */
441  os << "DATASET UNSTRUCTURED_GRID\n";
442  os << "POINTS " << psl->nb_points() << " float\n";
443  } else {
444  os << "<Piece NumberOfPoints=\"" << psl->nb_points();
445  os << "\" NumberOfCells=\"" << splx_cnt << "\">\n";
446  os << "<Points>\n";
447  os << "<DataArray type=\"Float32\" Name=\"Points\" ";
448  os << "NumberOfComponents=\"3\" ";
449  os << (ascii ? "format=\"ascii\">\n" : "format=\"binary\">\n");
450  if (!ascii) write_val(int(sizeof(float)*psl->nb_points()*3));
451  }
452  /*
453  points are not merge, vtk is mostly fine with that (except for
454  transparency and normals at vertices of 3D elements: all simplex faces
455  are considered to be "boundary" faces by vtk)
456  */
457  for (size_type ic=0; ic < psl->nb_convex(); ++ic) {
458  for (size_type i=0; i < psl->nodes(ic).size(); ++i)
459  write_vec(psl->nodes(ic)[i].pt.begin(),psl->nodes(ic)[i].pt.size());
460  write_separ();
461  }
462  write_vals();
463  if (!vtk) {
464  os << (ascii ? "" : "\n") << "</DataArray>\n";
465  os << "</Points>\n";
466  }
467 
468  /* CELLS section */
469  size_type nodes_cnt = 0;
470  if (vtk) {
471  write_separ(); os << "CELLS " << splx_cnt << " " << cells_cnt << "\n";
472  } else {
473  os << "<Cells>\n";
474  os << "<DataArray type=\"Int32\" Name=\"connectivity\" ";
475  os << (ascii ? "format=\"ascii\">\n" : "format=\"binary\">\n");
476  if (!ascii) {
477  int size = 0;
478  for (size_type ic=0; ic < psl->nb_convex(); ++ic) {
479  for (const slice_simplex &s : psl->simplexes(ic))
480  size += int((s.dim()+1)*sizeof(int));
481  }
482  write_val(size);
483  }
484  }
485  for (size_type ic=0; ic < psl->nb_convex(); ++ic) {
486  for (const slice_simplex &s : psl->simplexes(ic)) {
487  if (vtk)
488  write_val(int(s.dim()+1));
489  for (size_type j=0; j < s.dim()+1; ++j)
490  write_val(int(s.inodes[j] + nodes_cnt));
491  write_separ();
492  }
493  nodes_cnt += psl->nodes(ic).size();
494  }
495  assert(nodes_cnt == psl->nb_points()); // sanity check
496  write_vals();
497  if (vtk) {
498  write_separ(); os << "CELL_TYPES " << splx_cnt << "\n";
499  } else {
500  os << (ascii ? "" : "\n") << "</DataArray>\n";
501  os << "<DataArray type=\"Int32\" Name=\"offsets\" ";
502  os << (ascii ? "format=\"ascii\">\n" : "format=\"binary\">\n");
503  if (!ascii) {
504  int size = 0;
505  for (size_type ic=0; ic < psl->nb_convex(); ++ic)
506  size += int(psl->simplexes(ic).size()*sizeof(int));
507  write_val(size);
508  }
509  }
510  int cnt = 0;
511  for (size_type ic=0; ic < psl->nb_convex(); ++ic) {
512  for (const slice_simplex &s : psl->simplexes(ic))
513  if (vtk) {
514  write_val(int(vtk_simplex_code[s.dim()]));
515  } else {
516  cnt += int(s.dim()+1);
517  write_val(cnt);
518  }
519  write_separ();
520  splx_cnt -= psl->simplexes(ic).size();
521  }
522  write_vals();
523  assert(splx_cnt == 0); // sanity check
524  if (!vtk) {
525  os << (ascii ? "" : "\n") << "</DataArray>\n";
526  os << "<DataArray type=\"Int32\" Name=\"types\" ";
527  os << (ascii ? "format=\"ascii\">\n" : "format=\"binary\">\n");
528  if (!ascii) {
529  int size = 0;
530  for (size_type ic=0; ic < psl->nb_convex(); ++ic)
531  size += int(psl->simplexes(ic).size()*sizeof(int));
532  write_val(size);
533  }
534  for (size_type ic=0; ic < psl->nb_convex(); ++ic)
535  for (const slice_simplex &s : psl->simplexes(ic))
536  write_val(int(vtk_simplex_code[s.dim()]));
537  write_vals();
538  os << "\n" << "</DataArray>\n";
539  os << "</Cells>\n";
540  }
541  state = STRUCTURE_WRITTEN;
542  }
543 
544 
545  void vtk_export::write_mesh_structure_from_mesh_fem() {
546  if (state >= STRUCTURE_WRITTEN) return;
547  check_header();
548  if (vtk) {
549  /* possible improvement: detect structured grids */
550  os << "DATASET UNSTRUCTURED_GRID\n";
551  os << "POINTS " << pmf_dof_used.card() << " float\n";
552  } else {
553  os << "<Piece NumberOfPoints=\"" << pmf_dof_used.card()
554  << "\" NumberOfCells=\"" << pmf->convex_index().card() << "\">\n";
555  os << "<Points>\n";
556  os << "<DataArray type=\"Float32\" Name=\"Points\" ";
557  os << "NumberOfComponents=\"3\" ";
558  os << (ascii ? "format=\"ascii\">\n" : "format=\"binary\">\n");
559  if (!ascii) write_val(int(sizeof(float)*pmf_dof_used.card()*3));
560  }
561  std::vector<int> dofmap(pmf->nb_dof());
562  int cnt = 0;
563  for (dal::bv_visitor d(pmf_dof_used); !d.finished(); ++d) {
564  dofmap[d] = cnt++;
565  base_node P = pmf->point_of_basic_dof(d);
566  write_vec(P.const_begin(),P.size());
567  write_separ();
568  }
569  write_vals();
570 
571  size_type nb_cell_values = 0;
572  for (dal::bv_visitor cv(pmf->convex_index()); !cv.finished(); ++cv)
573  nb_cell_values += (1 + select_vtk_dof_mapping(pmf_mapping_type[cv]).size());
574 
575  if (vtk) {
576  write_separ();
577  os << "CELLS " << pmf->convex_index().card() << " " << nb_cell_values << "\n";
578  } else {
579  os << (ascii ? "" : "\n") << "</DataArray>\n";
580  os << "</Points>\n";
581  os << "<Cells>\n";
582  os << "<DataArray type=\"Int32\" Name=\"connectivity\" ";
583  os << (ascii ? "format=\"ascii\">\n" : "format=\"binary\">\n");
584  if (!ascii) {
585  int size = 0;
586  for (dal::bv_visitor cv(pmf->convex_index()); !cv.finished(); ++cv) {
587  const std::vector<unsigned> &dmap = select_vtk_dof_mapping(pmf_mapping_type[cv]);
588  size += int(sizeof(int)*dmap.size());
589  }
590  write_val(size);
591  }
592  }
593 
594  for (dal::bv_visitor cv(pmf->convex_index()); !cv.finished(); ++cv) {
595  const std::vector<unsigned> &dmap = select_vtk_dof_mapping(pmf_mapping_type[cv]);
596  if (vtk) write_val(int(dmap.size()));
597  for (size_type i=0; i < dmap.size(); ++i)
598  write_val(int(dofmap[pmf->ind_basic_dof_of_element(cv)[dmap[i]]]));
599  write_separ();
600  }
601  write_vals();
602 
603  if (vtk) {
604  write_separ();
605  os << "CELL_TYPES " << pmf->convex_index().card() << "\n";
606  } else {
607  os << (ascii ? "" : "\n") << "</DataArray>\n";
608  os << "<DataArray type=\"Int32\" Name=\"offsets\" ";
609  os << (ascii ? "format=\"ascii\">\n" : "format=\"binary\">\n");
610  if (!ascii)
611  write_val(int(pmf->convex_index().card()*sizeof(int)));
612  cnt = 0;
613  for (dal::bv_visitor cv(pmf->convex_index()); !cv.finished(); ++cv) {
614  const std::vector<unsigned> &dmap = select_vtk_dof_mapping(pmf_mapping_type[cv]);
615  cnt += int(dmap.size());
616  write_val(cnt);
617  }
618  write_vals();
619  os << "\n" << "</DataArray>\n";
620  os << "<DataArray type=\"Int32\" Name=\"types\" ";
621  os << (ascii ? "format=\"ascii\">\n" : "format=\"binary\">\n");
622  if (!ascii)
623  write_val(int(pmf->convex_index().card()*sizeof(int)));
624  }
625  for (dal::bv_visitor cv(pmf->convex_index()); !cv.finished(); ++cv) {
626  write_val(int(select_vtk_type(pmf_mapping_type[cv])));
627  if (vtk) write_separ();
628  }
629  write_vals();
630  if (!vtk) os << "\n" << "</DataArray>\n" << "</Cells>\n";
631 
632  state = STRUCTURE_WRITTEN;
633  }
634 
635  void vtk_export::write_mesh_quality(const mesh &m) {
636  if (psl) {
637  mesh_fem mf(const_cast<mesh&>(m),1);
639  std::vector<scalar_type> q(mf.nb_dof());
640  for (size_type d=0; d < mf.nb_dof(); ++d) {
642  }
643  write_point_data(mf, q, "convex_quality");
644  } else {
645  std::vector<scalar_type> q(pmf->convex_index().card());
646  for (dal::bv_visitor cv(pmf->convex_index()); !cv.finished(); ++cv) {
647  q[cv] = m.convex_quality_estimate(cv);
648  }
649  write_cell_data(q, "convex_quality");
650  }
651  }
652 
653 
654  /* -------------------------------------------------------------
655  * OPENDX export
656  * ------------------------------------------------------------- */
657 
658  dx_export::dx_export(std::ostream &os_, bool ascii_)
659  : os(os_), ascii(ascii_) { init(); }
660 
661  dx_export::dx_export(const std::string& fname, bool ascii_, bool append_)
662  : os(real_os), ascii(ascii_) {
663  real_os.open(fname.c_str(),
664  std::ios_base::openmode(std::ios_base::in |
665  std::ios_base::out |
666  (append_ ? std::ios_base::ate : std::ios_base::trunc)));
667  GMM_ASSERT1(real_os.good(), "impossible to write to dx file '"
668  << fname << "'");
669  init();
670  if (append_) { reread_metadata(); header_written = true; }
671  }
672 
673  dx_export::~dx_export() {
674  std::ios::pos_type p = os.tellp();
675  write_series();
676  os << "\n# --end of getfem export\nend\n";
677  update_metadata(p);
678  }
679 
680  void dx_export::init() {
681  strcpy(header, "Exported by GetFEM");
682  psl = 0; dim_ = dim_type(-1); connections_dim = dim_type(-1);
683  psl_use_merged = false;
684  header_written = false;
685  }
686 
687  void dx_export::write_separ()
688  { if (ascii) os << "\n"; }
689 
690  template<typename T> static typename std::list<T>::iterator
691  get_from_name(std::list<T> &c,
692  const std::string& name, bool raise_error) {
693  for (typename std::list<T>::iterator it = c.begin();
694  it != c.end(); ++it) {
695  if (it->name == name) return it;
696  }
697  GMM_ASSERT1(!raise_error, "object not found in dx file: " << name);
698  return c.end();
699  }
700 
701  std::list<dx_export::dxMesh>::iterator
702  dx_export::get_mesh(const std::string& name, bool raise_error) {
703  return get_from_name(meshes,name,raise_error);
704  }
705  std::list<dx_export::dxObject>::iterator
706  dx_export::get_object(const std::string& name, bool raise_error) {
707  return get_from_name(objects,name,raise_error);
708  }
709 
710 
711  bool dx_export::new_mesh(std::string &name) {
712  name = default_name(name, int(meshes.size()), "mesh");
713  std::list<dxMesh>::iterator it = get_mesh(name, false);
714  if (it != meshes.end()) {
715  if (&(*it) != &current_mesh())
716  std::swap(current_mesh(),*it);
717  return false;
718  } else {
719  meshes.push_back(dxMesh()); meshes.back().name = name;
720  return true;
721  }
722  }
723 
724  void dx_export::exporting(const stored_mesh_slice& sl, bool merge_points,
725  std::string name) {
726  if (!new_mesh(name)) return;
727  psl_use_merged = merge_points;
728  if (merge_points) sl.merge_nodes();
729  psl = &sl; dim_ = dim_type(sl.dim());
730  GMM_ASSERT1(psl->dim() <= 3, "4D slices and more are not supported");
731  for (dim_type d = 0; d <= psl->dim(); ++d) {
732  if (psl->nb_simplexes(d)) {
733  if (connections_dim == dim_type(-1)) connections_dim = d;
734  else GMM_ASSERT1(false, "Cannot export a slice containing "
735  "simplexes of different dimensions");
736  }
737  }
738  GMM_ASSERT1(connections_dim != dim_type(-1), "empty slice!");
739  }
740 
741 
742  void dx_export::exporting(const mesh_fem& mf, std::string name) {
743  name = default_name(name, int(meshes.size()), "mesh");
744  if (!new_mesh(name)) return;
745  const mesh &m = mf.linked_mesh();
746  GMM_ASSERT1(mf.linked_mesh().convex_index().card() != 0,
747  "won't export an empty mesh");
748 
749  dim_ = m.dim();
750  GMM_ASSERT1(dim_ <= 3, "4D meshes and more are not supported");
751  if (&mf != pmf.get())
752  pmf = std::make_unique<mesh_fem>(const_cast<mesh&>(m), dim_type(1));
753  bgeot::pgeometric_trans pgt = m.trans_of_convex(m.convex_index().first_true());
754  GMM_ASSERT1(dxname_of_convex_structure
755  (basic_structure(pgt->structure())) != 0,
756  "DX Cannot handle " <<
757  bgeot::name_of_geometric_trans(pgt) << ", use slices");
758  /* initialize pmf with finite elements suitable for OpenDX */
759  for (dal::bv_visitor cv(mf.convex_index()); !cv.finished(); ++cv) {
760  bgeot::pgeometric_trans pgt2 = mf.linked_mesh().trans_of_convex(cv);
761  GMM_ASSERT1(basic_structure(pgt->structure()) ==
762  basic_structure(pgt2->structure()),
763  "Cannot export this mesh to opendx, it contains "
764  "different convex types. Slice it first.");
765  pfem pf = mf.fem_of_element(cv);
766  bool discontinuous = false;
767  for (unsigned i=0; i < pf->nb_dof(cv); ++i) {
768  /* could be a better test for discontinuity .. */
769  if (!dof_linkable(pf->dof_types()[i])) { discontinuous = true; break; }
770  }
771  pfem classical_pf1 = discontinuous ? classical_discontinuous_fem(pgt, 1)
772  : classical_fem(pgt, 1);
773  pmf->set_finite_element(cv, classical_pf1);
774  }
775  pmf_dof_used.add(0, pmf->nb_basic_dof());
776  connections_dim = dim_type(pmf->nb_basic_dof_of_element(m.convex_index().first_true()));
777  }
778 
779  void dx_export::exporting(const mesh& m, std::string name) {
780  dim_ = m.dim();
781  GMM_ASSERT1(dim_ <= 3, "4D meshes and more are not supported");
782  pmf = std::make_unique<mesh_fem>(const_cast<mesh&>(m), dim_type(1));
783  pmf->set_classical_finite_element(1);
784  exporting(*pmf, name);
785  }
786 
787  void dx_export::write_series() {
788  for (std::list<dxSeries>::const_iterator it = series.begin();
789  it != series.end(); ++it) {
790  if (it->members.size() == 0) continue;
791  size_type count = 0;
792  os << "\nobject \"" << it->name << "\" class series\n";
793  for (std::list<std::string>::const_iterator ito = it->members.begin();
794  ito != it->members.end(); ++ito, ++count) {
795  os << " member " << count << " \"" << (*ito) << "\"\n";
796  }
797  }
798  }
799 
800  void dx_export::serie_add_object_(const std::string &serie_name,
801  const std::string &object_name) {
802  std::list<dxSeries>::iterator it = series.begin();
803  while (it != series.end() && it->name != serie_name) ++it;
804  if (it == series.end()) {
805  series.push_back(dxSeries()); it = series.end(); --it;
806  it->name = serie_name;
807  }
808  it->members.push_back(object_name);
809  }
810 
811  void dx_export::serie_add_object(const std::string &serie_name,
812  const std::string &object_name) {
813  /* create a series for edge data if possible (the cost is null
814  and it may be useful) */
815  std::list<dxObject>::iterator ito = get_object(object_name, false);
816  if (ito != objects.end()) {
817  std::list<dxMesh>::iterator itm = get_mesh(ito->mesh);
818  if (itm != meshes.end() && (itm->flags & dxMesh::WITH_EDGES)) {
819  serie_add_object_(serie_name + "_edges",
820  object_name + "_edges");
821  }
822  }
823  /* fill the real serie */
824  serie_add_object_(serie_name, object_name);
825  }
826 
827  void dx_export::set_header(const std::string& s)
828  { strncpy(header, s.c_str(), 256); header[255] = 0; }
829 
830  void dx_export::check_header() {
831  if (header_written) return;
832  header_written = true;
833  os << "# data file for IBM OpenDX, generated by GetFem++ v "
834  << GETFEM_VERSION << "\n";
835  os << "# " << header << "\n";
836  }
837 
838  void dx_export::update_metadata(std::ios::pos_type pos_series) {
839  os.seekp(0,std::ios::end);
840  os << "# This file contains the following objects\n";
841  std::ios::pos_type pos_end = os.tellp();
842  for (std::list<dxSeries>::const_iterator it = series.begin();
843  it != series.end(); ++it) {
844  os << "#S \"" << it->name << "\" which contains:\n";
845  for (std::list<std::string>::const_iterator its = it->members.begin();
846  its != it->members.end(); ++its)
847  os << "#+ \"" << *its << "\"\n";
848  }
849  for (std::list<dxObject>::const_iterator it = objects.begin();
850  it != objects.end(); ++it) {
851  os << "#O \"" << it->name << "\" \"" << it->mesh << "\"\n";
852  }
853  for (std::list<dxMesh>::const_iterator it = meshes.begin();
854  it != meshes.end(); ++it) {
855  os << "#M \"" << it->name << "\" " << it->flags << "\n";
856  }
857  os << "#E \"THE_END\" " << std::setw(20) << pos_series << std::setw(20) << pos_end << "\n";
858  }
859 
860  void dx_export::reread_metadata() {
861  char line[512];
862  real_os.seekg(0, std::ios::end);
863  int count=0; char c;
864  unsigned long lu_end, lu_series;
865  do {
866  real_os.seekg(-1, std::ios::cur);
867  c = char(real_os.peek());
868  } while (++count < 512 && c != '#');
869  real_os.getline(line, sizeof line);
870  if (sscanf(line, "#E \"THE_END\" %lu %lu", &lu_series, &lu_end) != 2)
871  GMM_ASSERT1(false, "this file was not generated by getfem, "
872  "cannot append data to it!\n");
873  real_os.seekg(lu_end, std::ios::beg);
874  do {
875  char name[512]; unsigned n;
876  int pos;
877  real_os.getline(line, sizeof line);
878  if (sscanf(line, "#%c \"%512[^\"]\"%n", &c, name, &pos) < 1)
879  GMM_ASSERT1(false, "corrupted file! your .dx file is broken\n");
880  if (c == 'S') {
881  series.push_back(dxSeries()); series.back().name = name;
882  } else if (c == '+') {
883  series.back().members.push_back(name);
884  } else if (c == 'O') {
885  objects.push_back(dxObject()); objects.back().name = name;
886  sscanf(line+pos, " \"%512[^\"]\"", name); objects.back().mesh = name;
887  } else if (c == 'M') {
888  meshes.push_back(dxMesh()); meshes.back().name = name;
889  sscanf(line+pos, "%u", &n); meshes.back().flags = n;
890  } else if (c == 'E') {
891  break;
892  } else GMM_ASSERT1(false, "corrupted file! your .dx file is broken\n");
893  } while (1);
894  real_os.seekp(lu_series, std::ios::beg);
895  }
896 
897  void dx_export::write_convex_attributes(bgeot::pconvex_structure cvs) {
898  const char *s_elem_type = dxname_of_convex_structure(cvs);
899  if (!s_elem_type)
900  GMM_WARNING1("OpenDX won't handle this kind of convexes");
901  os << "\n attribute \"element type\" string \"" << s_elem_type << "\"\n"
902  << " attribute \"ref\" string \"positions\"\n\n";
903  }
904 
905  const char *dx_export::dxname_of_convex_structure(bgeot::pconvex_structure cvs) {
906  const char *s_elem_type = 0;
907  switch (cvs->dim()) {
908  /* TODO: do something for point data */
909  case 1: s_elem_type = "lines"; break;
910  case 2:
911  if (cvs->nb_points() == 3)
912  s_elem_type = "triangles";
913  else if (cvs->nb_points() == 4)
914  s_elem_type = "quads";
915  break;
916  case 3:
917  if (cvs->nb_points() == 4)
918  s_elem_type = "tetrahedra";
919  else if (cvs->nb_points() == 8)
920  s_elem_type = "cubes";
921  break;
922  }
923  return s_elem_type;
924  }
925 
926  void dx_export::write_mesh() {
927  check_header();
928  if (current_mesh().flags & dxMesh::STRUCTURE_WRITTEN) return;
929  if (psl) write_mesh_structure_from_slice();
930  else write_mesh_structure_from_mesh_fem();
931 
932  os << "\nobject \"" << current_mesh_name() << "\" class field\n"
933  << " component \"positions\" value \""
934  << name_of_pts_array(current_mesh_name()) << "\"\n"
935  << " component \"connections\" value \""
936  << name_of_conn_array(current_mesh_name()) << "\"\n";
937  current_mesh().flags |= dxMesh::STRUCTURE_WRITTEN;
938  }
939 
940  /* export the slice data as an unstructured mesh composed of simplexes */
941  void dx_export::write_mesh_structure_from_slice() {
942  os << "\nobject \"" << name_of_pts_array(current_mesh_name())
943  << "\" class array type float rank 1 shape "
944  << int(psl->dim())
945  << " items " << (psl_use_merged ? psl->nb_merged_nodes() : psl->nb_points());
946  if (!ascii) os << " " << endianness() << " binary";
947  os << " data follows\n";
948  if (psl_use_merged) {
949  for (size_type i=0; i < psl->nb_merged_nodes(); ++i) {
950  for (size_type k=0; k < psl->dim(); ++k)
951  write_val(float(psl->merged_point(i)[k]));
952  write_separ();
953  }
954  } else {
955  for (size_type ic=0; ic < psl->nb_convex(); ++ic) {
956  for (size_type i=0; i < psl->nodes(ic).size(); ++i)
957  for (size_type k=0; k < psl->dim(); ++k)
958  write_val(float(psl->nodes(ic)[i].pt[k]));
959  write_separ();
960  }
961  }
962 
963  os << "\nobject \"" << name_of_conn_array(current_mesh_name())
964  << "\" class array type int rank 1 shape "
965  << int(connections_dim+1)
966  << " items " << psl->nb_simplexes(connections_dim);
967  if (!ascii) os << " " << endianness() << " binary";
968  os << " data follows\n";
969 
970  size_type nodes_cnt = 0; /* <- a virer , global_index le remplace */
971  for (size_type ic=0; ic < psl->nb_convex(); ++ic) {
972  for (const slice_simplex &s : psl->simplexes(ic)) {
973  if (s.dim() == connections_dim) {
974  for (size_type j=0; j < s.dim()+1; ++j) {
975  size_type k;
976  if (psl_use_merged)
977  k = psl->merged_index(ic, s.inodes[j]);
978  else k = psl->global_index(ic, s.inodes[j]);
979  write_val(int(k));
980  }
981  write_separ();
982  }
983  }
984  nodes_cnt += psl->nodes(ic).size();
985  }
986 
987  write_convex_attributes(bgeot::simplex_structure(connections_dim));
988  assert(nodes_cnt == psl->nb_points()); // sanity check
989  }
990 
991  void dx_export::write_mesh_structure_from_mesh_fem() {
992  os << "\nobject \"" << name_of_pts_array(current_mesh_name())
993  << "\" class array type float rank 1 shape "
994  << int(pmf->linked_mesh().dim())
995  << " items " << pmf->nb_dof();
996  if (!ascii) os << " " << endianness() << " binary";
997  os << " data follows\n";
998 
999  /* possible improvement: detect structured grids */
1000  for (size_type d = 0; d < pmf->nb_basic_dof(); ++d) {
1001  const base_node P = pmf->point_of_basic_dof(d);
1002  for (size_type k=0; k < dim_; ++k)
1003  write_val(float(P[k]));
1004  write_separ();
1005  }
1006 
1007  os << "\nobject \"" << name_of_conn_array(current_mesh_name())
1008  << "\" class array type int rank 1 shape "
1009  << int(connections_dim)
1010  << " items " << pmf->convex_index().card();
1011  if (!ascii) os << " " << endianness() << " binary";
1012  os << " data follows\n";
1013 
1014  for (dal::bv_visitor cv(pmf->convex_index()); !cv.finished(); ++cv) {
1015  for (size_type i=0; i < connections_dim; ++i)
1016  write_val(int(pmf->ind_basic_dof_of_element(cv)[i]));
1017  write_separ();
1018  }
1019  write_convex_attributes(basic_structure(pmf->linked_mesh().structure_of_convex(pmf->convex_index().first_true())));
1020  }
1021 
1022  void dx_export::exporting_mesh_edges(bool with_slice_edges) {
1023  write_mesh();
1024  if (current_mesh().flags & dxMesh::WITH_EDGES) return;
1025  if (psl) write_mesh_edges_from_slice(with_slice_edges);
1026  else write_mesh_edges_from_mesh();
1027  current_mesh().flags |= dxMesh::WITH_EDGES;
1028  os << "\nobject \"" << name_of_edges_array(current_mesh_name())
1029  << "\" class field\n"
1030  << " component \"positions\" value \""
1031  << name_of_pts_array(current_mesh_name()) << "\"\n"
1032  << " component \"connections\" value \""
1033  << name_of_conn_array(name_of_edges_array(current_mesh_name()))
1034  << "\"\n";
1035  }
1036 
1037  void dx_export::write_mesh_edges_from_slice(bool with_slice_edges) {
1038  std::vector<size_type> edges;
1039  dal::bit_vector slice_edges;
1040  psl->get_edges(edges, slice_edges, psl_use_merged);
1041  if (with_slice_edges) slice_edges.clear();
1042  os << "\nobject \""
1043  << name_of_conn_array(name_of_edges_array(current_mesh_name()))
1044  << "\" class array type int rank 1 shape 2"
1045  << " items " << edges.size()/2 - slice_edges.card();
1046  if (!ascii) os << " " << endianness() << " binary";
1047  os << " data follows\n";
1048  for (size_type i=0; i < edges.size()/2; ++i) {
1049  if (!slice_edges.is_in(i)) {
1050  write_val(int(edges[2*i]));
1051  write_val(int(edges[2*i+1]));
1052  }
1053  if ((i+1)%10 == 0) write_separ();
1054  }
1055  write_separ();
1056  write_convex_attributes(bgeot::simplex_structure(1));
1057  }
1058 
1059  void dx_export::write_mesh_edges_from_mesh() {
1060  bgeot::mesh_structure ms(pmf->linked_mesh()); ms.to_edges();
1061  os << "\nobject \""
1062  << name_of_conn_array(name_of_edges_array(current_mesh_name()))
1063  << "\" class array type int rank 1 shape 2"
1064  << " items " << ms.convex_index().card();
1065  if (!ascii) os << " " << endianness() << " binary";
1066  os << " data follows\n";
1067  for (dal::bv_visitor cv(ms.convex_index()); !cv.finished(); ++cv) {
1068  write_val(int(ms.ind_points_of_convex(cv)[0]));
1069  write_val(int(ms.ind_points_of_convex(cv)[1]));
1070  if ((cv+1)%20 == 0) write_separ();
1071  }
1072  write_separ();
1073  write_convex_attributes(bgeot::simplex_structure(1));
1074  }
1075 
1076 
1077  /* -------------------------------------------------------------
1078  * POS export (Gmsh post-processing format)
1079  * ------------------------------------------------------------- */
1080  struct gf2pos_dof_mapping : public std::vector<std::vector<unsigned> > {};
1081 
1082  static const std::vector<unsigned>& getfem_to_pos_dof_mapping(int t) {
1083  gf2pos_dof_mapping &dm = dal::singleton<gf2pos_dof_mapping>::instance();
1084  if (dm.size() == 0) {
1085  dm.resize(8);
1086  dm[pos_export::POS_PT] = {0};
1087  dm[pos_export::POS_LN] = {0, 1};
1088  dm[pos_export::POS_TR] = {0, 1, 2};
1089  dm[pos_export::POS_QU] = {0, 1, 3, 2};
1090  dm[pos_export::POS_SI] = {0, 1, 2, 3};
1091  dm[pos_export::POS_HE] = {0, 1, 3, 2, 4, 5, 7, 6};
1092  dm[pos_export::POS_PR] = {0, 1, 2, 3, 4, 5};
1093  dm[pos_export::POS_PY] = {0, 1, 3, 2, 4};
1094  }
1095  return dm[t];
1096  }
1097 
1098  pos_export::pos_export(std::ostream& osname): os(osname) {
1099  init();
1100  }
1101 
1102  pos_export::pos_export(const std::string& fname)
1103  : os(real_os), real_os(fname.c_str()) {
1104  GMM_ASSERT1(real_os, "impossible to write to pos file '" << fname << "'");
1105  init();
1106  }
1107 
1108  void pos_export::init() {
1109  strcpy(header, "Exported by GetFEM");
1110  state = EMPTY;
1111  view = 0;
1112  }
1113 
1114  void pos_export::set_header(const std::string& s){
1115  strncpy(header, s.c_str(), 256);
1116  header[255] = 0;
1117  }
1118 
1119  void pos_export::check_header() {
1120  if (state >= HEADER_WRITTEN) return;
1121  os << "/* " << header << " */\n";
1122  os << "General.FastRedraw = 0;\n";
1123  os << "General.ColorScheme = 1;\n";
1124  state = HEADER_WRITTEN;
1125  }
1126 
1127  void pos_export::exporting(const mesh& m) {
1128  if (state >= STRUCTURE_WRITTEN) return;
1129  dim = dim_type(m.dim());
1130  GMM_ASSERT1(int(dim) <= 3, "attempt to export a "
1131  << int(dim) << "D mesh (not supported)");
1132  pmf = std::make_unique<mesh_fem>(const_cast<mesh&>(m), dim_type(1));
1133  pmf->set_classical_finite_element(1);
1134  exporting(*pmf);
1135  state = STRUCTURE_WRITTEN;
1136  }
1137 
1138  void pos_export::exporting(const mesh_fem& mf) {
1139  if (state >= STRUCTURE_WRITTEN) return;
1140  dim = dim_type(mf.linked_mesh().dim());
1141  GMM_ASSERT1(int(dim) <= 3, "attempt to export a "
1142  << int(dim) << "D mesh_fem (not supported)");
1143  if (&mf != pmf.get())
1144  pmf = std::make_unique<mesh_fem>(mf.linked_mesh(), dim_type(1));
1145 
1146  /* initialize pmf with finite elements suitable for Gmsh */
1147  for (dal::bv_visitor cv(mf.convex_index()); !cv.finished(); ++cv) {
1148  bgeot::pgeometric_trans pgt = mf.linked_mesh().trans_of_convex(cv);
1149  pfem pf = mf.fem_of_element(cv);
1150 
1151  bool discontinuous = false;
1152  for (unsigned i=0; i < pf->nb_dof(cv); ++i) {
1153  // could be a better test for discontinuity ...
1154  if (!dof_linkable(pf->dof_types()[i])) { discontinuous = true; break; }
1155  }
1156  pfem classical_pf1 = discontinuous ?
1157  classical_discontinuous_fem(pgt, 1) : classical_fem(pgt, 1);
1158  pmf->set_finite_element(cv, classical_pf1);
1159  }
1160  psl = NULL;
1161 
1162  /* find out which dof will be exported to Gmsh */
1163  for (dal::bv_visitor cv(pmf->convex_index()); !cv.finished(); ++cv) {
1164  int t = -1;
1165  switch (pmf->fem_of_element(cv)->nb_dof(cv)){
1166  case 1: t = POS_PT; break;
1167  case 2: t = POS_LN; break;
1168  case 3: t = POS_TR; break;
1169  case 4:
1170  if ( 2 == pmf->fem_of_element(cv)->dim() ) t = POS_QU;
1171  else if (3 == pmf->fem_of_element(cv)->dim()) t = POS_SI;
1172  break;
1173  case 6: t = POS_PR; break;
1174  case 8: t = POS_HE; break;
1175  case 5: t = POS_PY; break;
1176  }
1177  GMM_ASSERT1(t != -1, "semi internal error. Could not map "
1178  << name_of_fem(pmf->fem_of_element(cv))
1179  << " to a POS primitive type");
1180  pos_cell_type.push_back(unsigned(t));
1181 
1182  const std::vector<unsigned>& dmap = getfem_to_pos_dof_mapping(t);
1183  GMM_ASSERT1(dmap.size() <= pmf->nb_basic_dof_of_element(cv),
1184  "inconsistency in pos_dof_mapping");
1185  std::vector<unsigned> cell_dof;
1186  cell_dof.resize(dmap.size(),unsigned(-1));
1187  for (size_type i=0; i < dmap.size(); ++i)
1188  cell_dof[i] = unsigned(pmf->ind_basic_dof_of_element(cv)[dmap[i]]);
1189  pos_cell_dof.push_back(cell_dof);
1190  }
1191  for (size_type i=0; i< pmf->nb_basic_dof(); ++i){
1192  std::vector<float> pt;
1193  pt.resize(dim,float(0));
1194  for (size_type j=0; j<dim; ++j)
1195  pt[j] = float(pmf->point_of_basic_dof(i)[j]);
1196  pos_pts.push_back(pt);
1197  }
1198  state = STRUCTURE_WRITTEN;
1199  }
1200 
1201  void pos_export::exporting(const stored_mesh_slice& sl) {
1202  if (state >= STRUCTURE_WRITTEN) return;
1203  psl = &sl;
1204  dim = dim_type(sl.dim());
1205  GMM_ASSERT1(int(dim) <= 3, "attempt to export a "
1206  << int(dim) << "D slice (not supported)");
1207 
1208  for (size_type ic=0, pcnt=0; ic < psl->nb_convex(); ++ic) {
1209  for (const slice_simplex &s : psl->simplexes(ic)) {
1210  int t = -1;
1211  switch (s.dim()){
1212  case 0: t = POS_PT; break;
1213  case 1: t = POS_LN; break;
1214  case 2: t = POS_TR; break;
1215  case 3: t = POS_SI; break;
1216  }
1217  GMM_ASSERT1(t != -1, "semi internal error.. could not map to a POS primitive type");
1218  pos_cell_type.push_back(unsigned(t));
1219 
1220  const std::vector<unsigned>& dmap = getfem_to_pos_dof_mapping(t);
1221  GMM_ASSERT1(dmap.size() <= size_type(t+1), "inconsistency in pos_dof_mapping");
1222 
1223  std::vector<unsigned> cell_dof;
1224  cell_dof.resize(dmap.size(),unsigned(-1));
1225  for (size_type i=0; i < dmap.size(); ++i)
1226  cell_dof[i] = unsigned(s.inodes[dmap[i]] + pcnt);
1227  pos_cell_dof.push_back(cell_dof);
1228  }
1229  for (const slice_node &n : psl->nodes(ic)) {
1230  std::vector<float> pt;
1231  pt.resize(dim,float(0));
1232  for (size_type i=0; i<dim; ++i)
1233  pt[i] = float(n.pt[i]);
1234  pos_pts.push_back(pt);
1235  }
1236  pcnt += psl->nodes(ic).size();
1237  }
1238  state = STRUCTURE_WRITTEN;
1239  }
1240 
1241  void pos_export::write(const mesh& m, const std::string &name){
1242  if (state >= IN_CELL_DATA) return;
1243  GMM_ASSERT1(int(m.dim()) <= 3, "attempt to export a "
1244  << int(m.dim()) << "D mesh (not supported)");
1245  pmf = std::make_unique<mesh_fem>(const_cast<mesh&>(m), dim_type(1));
1246  pmf->set_classical_finite_element(1);
1247  write(*pmf,name);
1248  state = IN_CELL_DATA;
1249  }
1250 
1251  void pos_export::write(const mesh_fem& mf, const std::string &name){
1252  if (state >= IN_CELL_DATA) return;
1253  check_header();
1254  exporting(mf);
1255 
1256  if (""==name) os << "View \"mesh " << view <<"\" {\n";
1257  else os << "View \"" << name <<"\" {\n";
1258 
1259  int t;
1260  std::vector<unsigned> cell_dof;
1261  std::vector<float> cell_dof_val;
1262  for (size_type cell = 0; cell < pos_cell_type.size(); ++cell) {
1263  t = pos_cell_type[cell];
1264  cell_dof = pos_cell_dof[cell];
1265  cell_dof_val.resize(cell_dof.size(),float(0));
1266  write_cell(t,cell_dof,cell_dof_val);
1267  }
1268 
1269  os << "};\n";
1270  os << "View[" << view << "].ShowScale = 0;\n";
1271  os << "View[" << view << "].ShowElement = 1;\n";
1272  os << "View[" << view << "].DrawScalars = 0;\n";
1273  os << "View[" << view << "].DrawVectors = 0;\n";
1274  os << "View[" << view++ << "].DrawTensors = 0;\n";
1275  state = IN_CELL_DATA;
1276  }
1277 
1278  void pos_export::write(const stored_mesh_slice& sl, const std::string &name){
1279  if (state >= IN_CELL_DATA) return;
1280  check_header();
1281  exporting(sl);
1282 
1283  if (""==name) os << "View \"mesh " << view <<"\" {\n";
1284  else os << "View \"" << name <<"\" {\n";
1285 
1286  int t;
1287  std::vector<unsigned> cell_dof;
1288  std::vector<float> cell_dof_val;
1289  for (size_type cell = 0; cell < pos_cell_type.size(); ++cell) {
1290  t = pos_cell_type[cell];
1291  cell_dof = pos_cell_dof[cell];
1292  cell_dof_val.resize(cell_dof.size(),float(0));
1293  write_cell(t,cell_dof,cell_dof_val);
1294  }
1295 
1296  os << "};\n";
1297  os << "View[" << view << "].ShowScale = 0;\n";
1298  os << "View[" << view << "].ShowElement = 1;\n";
1299  os << "View[" << view << "].DrawScalars = 0;\n";
1300  os << "View[" << view << "].DrawVectors = 0;\n";
1301  os << "View[" << view++ << "].DrawTensors = 0;\n";
1302  state = IN_CELL_DATA;
1303  }
1304 } /* end of namespace getfem. */
convenient initialization of vectors via overload of "operator,".
Mesh structure definition.
const dal::bit_vector & convex_index() const
Return the list of valid convex IDs.
static T & instance()
Instance from the current thread.
std::string current_mesh_name()
return the name of current mesh (use exporting(...) to change the current mesh)
void serie_add_object(const std::string &serie_name, const std::string &object_name)
add an object (typically the name of a data field) to a 'series', i.e.
void set_header(const std::string &s)
the header is the second line of text in the exported file, you can put whatever you want – call this...
void exporting_mesh_edges(bool with_slice_edge=true)
append edges information (if you want to draw the mesh and are using a refined slice.
Describe a finite element method linked to a mesh.
virtual size_type first_convex_of_basic_dof(size_type d) const
Shortcut for convex_to_dof(d)[0].
virtual size_type nb_dof() const
Return the total number of degrees of freedom.
const mesh & linked_mesh() const
Return a reference to the underlying mesh.
void set_classical_finite_element(size_type cv, dim_type fem_degree, bool complete=false)
Set a classical (i.e.
virtual pfem fem_of_element(size_type cv) const
Return the basic fem associated with an element (if no fem is associated, the function will crash!...
const dal::bit_vector & convex_index() const
Get the set of convexes where a finite element has been assigned.
Describe a mesh (collection of convexes (elements) and points).
Definition: getfem_mesh.h:98
scalar_type convex_quality_estimate(size_type ic) const
Return an estimate of the convex quality (0 <= Q <= 1).
Definition: getfem_mesh.cc:449
size_type dim() const
return the slice dimension
void nb_simplexes(std::vector< size_type > &c) const
return the simplex count, in an array.
const base_node merged_point(size_type i_merged) const
Return the physical position of the merged node.
const mesh_slicer::cs_nodes_ct & nodes(size_type ic) const
Return the list of nodes for the 'ic'th convex of the slice.
const mesh_slicer::cs_simplexes_ct & simplexes(size_type ic) const
Return the list of simplexes for the 'ic'th convex of the slice.
size_type nb_points() const
Return the number of nodes in the slice.
size_type nb_convex() const
return the number of convexes of the original mesh referenced in the slice
void get_edges(std::vector< size_type > &edges, dal::bit_vector &slice_edges, bool from_merged_nodes) const
Extract the list of mesh edges.
size_type nb_merged_nodes() const
Return the number of merged nodes in slice.
A simple singleton implementation.
Export solutions to various formats.
bool dof_linkable(pdof_description)
Says if the dof is linkable.
Definition: getfem_fem.cc:615
std::shared_ptr< const getfem::virtual_fem > pfem
type of pointer on a fem description
Definition: getfem_fem.h:243
pfem fem_descriptor(const std::string &name)
get a fem descriptor from its string name.
Definition: getfem_fem.cc:4659
pfem classical_discontinuous_fem(bgeot::pgeometric_trans pg, short_type k, scalar_type alpha=0, bool complete=false)
Give a pointer on the structures describing the classical polynomial discontinuous fem of degree k on...
Definition: getfem_fem.cc:4571
pfem classical_fem(bgeot::pgeometric_trans pgt, short_type k, bool complete=false)
Give a pointer on the structures describing the classical polynomial fem of degree k on a given conve...
Definition: getfem_fem.cc:4566
std::string name_of_fem(pfem p)
get the string name of a fem descriptor.
Definition: getfem_fem.cc:4668
gmm::uint16_type short_type
used as the common short type integer in the library
Definition: bgeot_config.h:72
std::string name_of_geometric_trans(pgeometric_trans p)
Get the string name of a geometric transformation.
std::shared_ptr< const convex_structure > pconvex_structure
Pointer on a convex structure description.
pconvex_structure simplex_structure(dim_type nc)
Give a pointer on the structures of a simplex of dimension d.
size_t size_type
used as the common size type in the library
Definition: bgeot_poly.h:48
pconvex_structure basic_structure(pconvex_structure cv)
Original structure (if concerned)
std::shared_ptr< const bgeot::geometric_trans > pgeometric_trans
pointer type for a geometric transformation
GEneric Tool for Finite Element Methods.