Line data Source code
1 : // The libMesh Finite Element Library.
2 : // Copyright (C) 2002-2025 Benjamin S. Kirk, John W. Peterson, Roy H. Stogner
3 :
4 : // This library is free software; you can redistribute it and/or
5 : // modify it under the terms of the GNU Lesser General Public
6 : // License as published by the Free Software Foundation; either
7 : // version 2.1 of the License, or (at your option) any later version.
8 :
9 : // This library is distributed in the hope that it will be useful,
10 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 : // Lesser General Public License for more details.
13 :
14 : // You should have received a copy of the GNU Lesser General Public
15 : // License along with this library; if not, write to the Free Software
16 : // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 :
18 :
19 : // Local includes
20 : #include "libmesh/libmesh_config.h"
21 : #include "libmesh/vtk_io.h"
22 : #include "libmesh/mesh_base.h"
23 : #include "libmesh/equation_systems.h"
24 : #include "libmesh/numeric_vector.h"
25 : #include "libmesh/system.h"
26 : #include "libmesh/node.h"
27 : #include "libmesh/elem.h"
28 : #include "libmesh/enum_io_package.h"
29 : #include "libmesh/utility.h"
30 :
31 : #ifdef LIBMESH_HAVE_VTK
32 :
33 : // I get a lot of "warning: extra ';' inside a class [-Wextra-semi]" from clang
34 : // on VTK header files.
35 : #include "libmesh/ignore_warnings.h"
36 :
37 : #include "vtkXMLUnstructuredGridReader.h"
38 : #include "vtkXMLPUnstructuredGridReader.h"
39 : #include "vtkXMLUnstructuredGridWriter.h"
40 : #include "vtkXMLPUnstructuredGridWriter.h"
41 : #include "vtkUnstructuredGrid.h"
42 : #include "vtkIntArray.h"
43 : #include "vtkCellArray.h"
44 : #include "vtkCellData.h"
45 : #include "vtkDoubleArray.h"
46 : #include "vtkGenericCell.h"
47 : #include "vtkPointData.h"
48 : #include "vtkPoints.h"
49 : #include "vtkSmartPointer.h"
50 :
51 : #ifdef LIBMESH_HAVE_MPI
52 : #include "vtkMPI.h"
53 : #include "vtkMPICommunicator.h"
54 : #include "vtkMPIController.h"
55 : #endif
56 :
57 : #include "libmesh/restore_warnings.h"
58 :
59 : // C++ includes
60 : #include <fstream>
61 :
62 :
63 : // A convenient macro for comparing VTK versions. Returns 1 if the
64 : // current VTK version is < major.minor.subminor and zero otherwise.
65 : //
66 : // It relies on the VTK version numbers detected during configure. Note that if
67 : // LIBMESH_HAVE_VTK is not defined, none of the LIBMESH_DETECTED_VTK_VERSION_* variables will
68 : // be defined either.
69 : #define VTK_VERSION_LESS_THAN(major,minor,subminor) \
70 : ((LIBMESH_DETECTED_VTK_VERSION_MAJOR < (major) || \
71 : (LIBMESH_DETECTED_VTK_VERSION_MAJOR == (major) && (LIBMESH_DETECTED_VTK_VERSION_MINOR < (minor) || \
72 : (LIBMESH_DETECTED_VTK_VERSION_MINOR == (minor) && \
73 : LIBMESH_DETECTED_VTK_VERSION_SUBMINOR < (subminor))))) ? 1 : 0)
74 :
75 : #endif // LIBMESH_HAVE_VTK
76 :
77 :
78 :
79 : namespace libMesh
80 : {
81 :
82 : // Constructor for reading
83 0 : VTKIO::VTKIO (MeshBase & mesh) :
84 : MeshInput<MeshBase> (mesh, /*is_parallel_format=*/true),
85 0 : MeshOutput<MeshBase>(mesh, /*is_parallel_format=*/true)
86 : #ifdef LIBMESH_HAVE_VTK
87 : ,_compress(false)
88 : #endif
89 : {
90 0 : }
91 :
92 :
93 :
94 : // Constructor for writing
95 0 : VTKIO::VTKIO (const MeshBase & mesh) :
96 0 : MeshOutput<MeshBase>(mesh, /*is_parallel_format=*/true)
97 : #ifdef LIBMESH_HAVE_VTK
98 : ,_compress(false)
99 : #endif
100 : {
101 0 : }
102 :
103 :
104 :
105 : // Output the mesh without solutions to a .pvtu file
106 0 : void VTKIO::write (const std::string & name)
107 : {
108 0 : std::vector<Number> soln;
109 0 : std::vector<std::string> names;
110 0 : this->write_nodal_data(name, soln, names);
111 0 : }
112 :
113 :
114 :
115 : // The rest of the file is wrapped in ifdef LIBMESH_HAVE_VTK except for
116 : // a couple of "stub" functions at the bottom.
117 : #ifdef LIBMESH_HAVE_VTK
118 :
119 : // Initialize the static _element_maps map.
120 : std::map<ElemMappingType, VTKIO::ElementMaps> VTKIO::_element_maps = VTKIO::build_element_maps();
121 :
122 : // Static function which constructs the ElementMaps object.
123 : std::map<ElemMappingType, VTKIO::ElementMaps> VTKIO::build_element_maps()
124 : {
125 : // Object to be filled up
126 : std::map<ElemMappingType, VTKIO::ElementMaps> all_maps;
127 : ElementMaps em; // Lagrange element maps
128 :
129 : em.associate(EDGE2, VTK_LINE);
130 : em.associate(EDGE3, VTK_QUADRATIC_EDGE);
131 : em.associate(TRI3, VTK_TRIANGLE);
132 : em.associate(TRI6, VTK_QUADRATIC_TRIANGLE);
133 : em.associate(QUAD4, VTK_QUAD);
134 : em.associate(QUAD8, VTK_QUADRATIC_QUAD);
135 : em.associate(TET4, VTK_TETRA);
136 : em.associate(TET10, VTK_QUADRATIC_TETRA);
137 : em.associate(HEX8, VTK_HEXAHEDRON);
138 : em.associate(HEX20, VTK_QUADRATIC_HEXAHEDRON);
139 : em.associate(HEX27, VTK_TRIQUADRATIC_HEXAHEDRON);
140 : em.associate(PRISM6, VTK_WEDGE);
141 : em.associate(PRISM15, VTK_QUADRATIC_WEDGE);
142 : em.associate(PRISM18, VTK_BIQUADRATIC_QUADRATIC_WEDGE);
143 : em.associate(PYRAMID5, VTK_PYRAMID);
144 :
145 : // VTK_BIQUADRATIC_QUAD has been around since VTK 5.0
146 : #if VTK_MAJOR_VERSION > 5 || (VTK_MAJOR_VERSION == 5 && VTK_MINOR_VERSION > 0)
147 : em.associate(QUAD9, VTK_BIQUADRATIC_QUAD);
148 : #endif
149 :
150 : // TRI3SUBDIVISION is for writing only
151 : em.writing_map[TRI3SUBDIVISION] = VTK_TRIANGLE;
152 : all_maps[ElemMappingType::LAGRANGE_MAP] = em;
153 :
154 :
155 : // VTK_BEZIER_* types were introduced in VTK 9.0
156 : #if VTK_VERSION_LESS_THAN(9,0,0)
157 : // Revert back to previous behavior when using an older version of VTK
158 : all_maps[ElemMappingType::RATIONAL_BERNSTEIN_MAP] = em;
159 : #else
160 : ElementMaps bem; // Rational Bernstein element maps
161 : bem.associate(EDGE2, VTK_LINE);
162 : bem.associate(EDGE3, VTK_BEZIER_CURVE);
163 : bem.associate(TRI3, VTK_TRIANGLE);
164 : bem.associate(TRI6, VTK_BEZIER_TRIANGLE);
165 : bem.associate(QUAD4, VTK_QUAD);
166 : bem.associate(QUAD8, VTK_QUADRATIC_QUAD);
167 : bem.associate(QUAD9, VTK_BEZIER_QUADRILATERAL);
168 : bem.associate(TET4, VTK_TETRA);
169 : bem.associate(TET10, VTK_QUADRATIC_TETRA);
170 : bem.associate(HEX8, VTK_HEXAHEDRON);
171 : bem.associate(HEX20, VTK_QUADRATIC_HEXAHEDRON);
172 : bem.associate(HEX27, VTK_BEZIER_HEXAHEDRON);
173 : bem.associate(PRISM6, VTK_WEDGE);
174 : bem.associate(PRISM15, VTK_QUADRATIC_WEDGE);
175 : bem.associate(PRISM18, VTK_BEZIER_WEDGE);
176 : bem.associate(PYRAMID5, VTK_PYRAMID);
177 : bem.writing_map[TRI3SUBDIVISION] = VTK_TRIANGLE;
178 : all_maps[ElemMappingType::RATIONAL_BERNSTEIN_MAP] = bem;
179 : #endif
180 :
181 : return all_maps;
182 : }
183 :
184 :
185 :
186 : void VTKIO::read (const std::string & name)
187 : {
188 : // This is a serial-only process for now;
189 : // the Mesh should be read on processor 0 and
190 : // broadcast later
191 : libmesh_assert_equal_to (MeshOutput<MeshBase>::mesh().processor_id(), 0);
192 :
193 : // Keep track of what kinds of elements this file contains
194 : elems_of_dimension.clear();
195 : elems_of_dimension.resize(4, false);
196 :
197 : // Use a typedef, because these names are just crazy
198 : typedef vtkSmartPointer<vtkXMLPUnstructuredGridReader> MyReader;
199 : MyReader reader = MyReader::New();
200 :
201 : // Pass the filename along to the reader
202 : reader->SetFileName(name.c_str());
203 :
204 : // Force reading
205 : reader->Update();
206 :
207 : // read in the grid
208 : _vtk_grid = reader->GetOutput();
209 :
210 : // Get a reference to the mesh
211 : MeshBase & mesh = MeshInput<MeshBase>::mesh();
212 :
213 : // Clear out any pre-existing data from the Mesh
214 : mesh.clear();
215 :
216 : // Try to preserve any libMesh ids and subdomain ids we find in the
217 : // file. This will be null if there are none, e.g. if a non-libMesh
218 : // program wrote this file.
219 :
220 : vtkAbstractArray * abstract_elem_id =
221 : _vtk_grid->GetCellData()->GetAbstractArray("libmesh_elem_id");
222 : vtkAbstractArray * abstract_node_id =
223 : _vtk_grid->GetPointData()->GetAbstractArray("libmesh_node_id");
224 : vtkAbstractArray * abstract_subdomain_id =
225 : _vtk_grid->GetCellData()->GetAbstractArray("subdomain_id");
226 :
227 : // Get ids as integers. This will be null if they are another data
228 : // type, e.g. if a non-libMesh program used the names we thought
229 : // were unique for different data.
230 : vtkIntArray * elem_id = vtkIntArray::SafeDownCast(abstract_elem_id);
231 : vtkIntArray * node_id = vtkIntArray::SafeDownCast(abstract_node_id);
232 : vtkIntArray * subdomain_id = vtkIntArray::SafeDownCast(abstract_subdomain_id);
233 :
234 : if (abstract_elem_id && !elem_id)
235 : libmesh_warning("Found non-integral libmesh_elem_id array; forced to ignore it.\n"
236 : "This is technically valid but probably broken.");
237 :
238 : if (abstract_node_id && !node_id)
239 : libmesh_warning("Found non-integral libmesh_node_id array; forced to ignore it.\n"
240 : "This is technically valid but probably broken.");
241 :
242 : if (abstract_subdomain_id && !subdomain_id)
243 : libmesh_warning("Found non-integral subdomain_id array; forced to ignore it.\n"
244 : "This is technically valid but probably broken.");
245 :
246 : // Get the number of points from the _vtk_grid object
247 : const unsigned int vtk_num_points = static_cast<unsigned int>(_vtk_grid->GetNumberOfPoints());
248 :
249 : // Map from VTK indexing to libMesh id if necessary
250 : std::vector<dof_id_type> vtk_node_to_libmesh;
251 : if (node_id)
252 : vtk_node_to_libmesh.resize(vtk_num_points);
253 :
254 : // always numbered nicely so we can loop like this
255 : for (unsigned int i=0; i<vtk_num_points; ++i)
256 : {
257 : // add to the id map
258 : // and add the actual point
259 : double pnt[3];
260 : _vtk_grid->GetPoint(static_cast<vtkIdType>(i), pnt);
261 : Point xyz(pnt[0], pnt[1], pnt[2]);
262 :
263 : if (node_id)
264 : {
265 : auto id = node_id->GetValue(i);
266 :
267 : // It would nice to distinguish between "duplicate nodes
268 : // because one was ghosted in a parallel file segment" and
269 : // "duplicate nodes because there was a bug", but I'm not
270 : // sure how to do that with vtkXMLPUnstructuredGridReader
271 : if (!mesh.query_node_ptr(id))
272 : mesh.add_point(xyz, id);
273 : vtk_node_to_libmesh[i] = id;
274 : }
275 : else
276 : mesh.add_point(xyz, i);
277 : }
278 :
279 : // Get the number of cells from the _vtk_grid object
280 : const unsigned int vtk_num_cells = static_cast<unsigned int>(_vtk_grid->GetNumberOfCells());
281 :
282 : auto& element_map = libmesh_map_find(_element_maps, mesh.default_mapping_type());
283 :
284 : vtkSmartPointer<vtkGenericCell> cell = vtkSmartPointer<vtkGenericCell>::New();
285 : for (unsigned int i=0; i<vtk_num_cells; ++i)
286 : {
287 : _vtk_grid->GetCell(i, cell);
288 :
289 : // Get the libMesh element type corresponding to this VTK element type.
290 : ElemType libmesh_elem_type = element_map.find(cell->GetCellType());
291 : auto elem = Elem::build(libmesh_elem_type);
292 :
293 : // get the straightforward numbering from the VTK cells
294 : for (auto j : elem->node_index_range())
295 : {
296 : const auto vtk_point_id = cell->GetPointId(j);
297 : const dof_id_type libmesh_node_id = node_id ?
298 : vtk_node_to_libmesh[vtk_point_id] : vtk_point_id;
299 :
300 : elem->set_node(j, mesh.node_ptr(libmesh_node_id));
301 : }
302 :
303 : // then get the connectivity
304 : std::vector<dof_id_type> conn;
305 : elem->connectivity(0, VTK, conn);
306 :
307 : // then reshuffle the nodes according to the connectivity, this
308 : // two-time-assign would evade the definition of the vtk_mapping
309 : for (unsigned int j=0,
310 : n_conn = cast_int<unsigned int>(conn.size());
311 : j != n_conn; ++j)
312 : elem->set_node(j, mesh.node_ptr(conn[j]));
313 :
314 : if (elem_id)
315 : {
316 : auto id = elem_id->GetValue(i);
317 : libmesh_error_msg_if
318 : (mesh.query_elem_ptr(id), "Duplicate element id " << id <<
319 : " found in libmesh_elem_ids");
320 : elem->set_id(id);
321 : }
322 : else
323 : elem->set_id(i);
324 :
325 : if (subdomain_id)
326 : {
327 : auto sbdid = subdomain_id->GetValue(i);
328 : elem->subdomain_id() = sbdid;
329 : }
330 :
331 : elems_of_dimension[elem->dim()] = true;
332 :
333 : mesh.add_elem(std::move(elem));
334 : } // end loop over VTK cells
335 :
336 : // Set the mesh dimension to the largest encountered for an element
337 : for (unsigned char i=0; i!=4; ++i)
338 : if (elems_of_dimension[i])
339 : mesh.set_mesh_dimension(i);
340 :
341 : #if LIBMESH_DIM < 3
342 : libmesh_error_msg_if(mesh.mesh_dimension() > LIBMESH_DIM,
343 : "Cannot open dimension "
344 : << mesh.mesh_dimension()
345 : << " mesh file when configured without "
346 : << mesh.mesh_dimension()
347 : << "D support.");
348 : #endif // LIBMESH_DIM < 3
349 : }
350 :
351 :
352 :
353 : void VTKIO::write_nodal_data (const std::string & fname,
354 : const std::vector<Number> & soln,
355 : const std::vector<std::string> & names)
356 : {
357 : // Warn that the .pvtu file extension should be used. Paraview
358 : // recognizes this, and it works in both serial and parallel. Only
359 : // warn about this once.
360 : if (!Utility::ends_with(fname, ".pvtu"))
361 : libmesh_do_once(libMesh::err << "The .pvtu extension should be used when writing VTK files in libMesh.");
362 :
363 : // If there are variable names being written, the solution vector
364 : // should not be empty, it should have been broadcast to all
365 : // processors by the MeshOutput base class, since VTK is a parallel
366 : // format. Verify this before going further.
367 : libmesh_error_msg_if(!names.empty() && soln.empty(),
368 : "Empty soln vector in VTKIO::write_nodal_data().");
369 :
370 : // Get a reference to the mesh
371 : const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
372 :
373 : // we only use Unstructured grids
374 : _vtk_grid = vtkSmartPointer<vtkUnstructuredGrid>::New();
375 : vtkSmartPointer<vtkXMLPUnstructuredGridWriter> writer = vtkSmartPointer<vtkXMLPUnstructuredGridWriter>::New();
376 : #ifdef LIBMESH_HAVE_MPI
377 : // Set VTK to the same communicator as libMesh
378 : vtkSmartPointer<vtkMPICommunicator> vtk_comm = vtkSmartPointer<vtkMPICommunicator>::New();
379 : MPI_Comm mpi_comm = mesh.comm().get();
380 : vtkMPICommunicatorOpaqueComm vtk_opaque_comm(&mpi_comm);
381 : vtk_comm->InitializeExternal(&vtk_opaque_comm);
382 :
383 : vtkSmartPointer<vtkMPIController> vtk_mpi_ctrl = vtkSmartPointer<vtkMPIController>::New();
384 : vtk_mpi_ctrl->SetCommunicator(vtk_comm);
385 :
386 : writer->SetController(vtk_mpi_ctrl);
387 : #endif
388 :
389 : // add nodes to the grid and update _local_node_map
390 : _local_node_map.clear();
391 : this->nodes_to_vtk();
392 :
393 : // add cells to the grid
394 : this->cells_to_vtk();
395 :
396 : // add nodal solutions to the grid, if solutions are given
397 : if (names.size() > 0)
398 : {
399 : std::size_t num_vars = names.size();
400 : std::vector<Number> local_values;
401 :
402 : #ifdef LIBMESH_USE_COMPLEX_NUMBERS
403 : std::vector<Real> local_real_values;
404 : #endif
405 :
406 : for (std::size_t variable=0; variable<num_vars; ++variable)
407 : {
408 : get_local_node_values(local_values, variable, soln, names);
409 :
410 : #ifdef LIBMESH_USE_COMPLEX_NUMBERS
411 : // write real part
412 : local_real_values.resize(local_values.size());
413 : std::transform(local_values.begin(), local_values.end(),
414 : local_real_values.begin(),
415 : [](Number x) { return x.real(); });
416 : node_values_to_vtk(names[variable] + "_real", local_real_values);
417 :
418 : // write imaginary part
419 : local_real_values.resize(local_values.size());
420 : std::transform(local_values.begin(), local_values.end(),
421 : local_real_values.begin(),
422 : [](Number x) { return x.imag(); });
423 : node_values_to_vtk(names[variable] + "_imag", local_real_values);
424 : #else
425 : node_values_to_vtk(names[variable], local_values);
426 : #endif
427 : }
428 : }
429 :
430 : // Tell the writer how many partitions exist and on which processor
431 : // we are currently
432 : writer->SetNumberOfPieces(mesh.n_processors());
433 : writer->SetStartPiece(mesh.processor_id());
434 : writer->SetEndPiece(mesh.processor_id());
435 :
436 : // partitions overlap by one node
437 : // FIXME: According to this document
438 : // http://paraview.org/Wiki/images/5/51/SC07_tut107_ParaView_Handouts.pdf
439 : // the ghosts are cells rather than nodes.
440 : writer->SetGhostLevel(1);
441 :
442 : // VTK 6 replaces SetInput() with SetInputData(). See
443 : // http://www.vtk.org/Wiki/VTK/VTK_6_Migration/Replacement_of_SetInput
444 : // for the full explanation.
445 : #if VTK_VERSION_LESS_THAN(6,0,0)
446 : writer->SetInput(_vtk_grid);
447 : #else
448 : writer->SetInputData(_vtk_grid);
449 : #endif
450 :
451 : writer->SetFileName(fname.c_str());
452 : writer->SetDataModeToAscii();
453 :
454 : // compress the output, if desired (switches also to binary)
455 : if (this->_compress)
456 : {
457 : #if !VTK_VERSION_LESS_THAN(5,6,0)
458 : writer->SetCompressorTypeToZLib();
459 : writer->SetDataModeToBinary();
460 : #else
461 : libmesh_do_once(libMesh::err << "Compression not implemented with old VTK libs!" << std::endl;);
462 : #endif
463 : }
464 :
465 : writer->Write();
466 :
467 : }
468 :
469 :
470 :
471 : vtkUnstructuredGrid * VTKIO::get_vtk_grid()
472 : {
473 : return _vtk_grid;
474 : }
475 :
476 :
477 :
478 : void VTKIO::set_compression(bool b)
479 : {
480 : this->_compress = b;
481 : }
482 :
483 :
484 :
485 : void VTKIO::nodes_to_vtk()
486 : {
487 : const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
488 :
489 : // containers for points and coordinates of points
490 : vtkSmartPointer<vtkPoints> points = vtkSmartPointer<vtkPoints>::New();
491 : vtkSmartPointer<vtkDoubleArray> pcoords = vtkSmartPointer<vtkDoubleArray>::New();
492 : // if this grid is to be used in VTK then the dimension of the points should be 3
493 : pcoords->SetNumberOfComponents(LIBMESH_DIM);
494 : pcoords->Allocate(3*mesh.n_local_nodes());
495 : points->SetNumberOfPoints(mesh.n_local_nodes()); // it seems that it needs this to prevent a segfault
496 :
497 : // SetRationalWeights() was introduced in VTK 9.0
498 : #if !VTK_VERSION_LESS_THAN(9,0,0)
499 : bool have_weights = false;
500 : int weight_index = 0;
501 : vtkSmartPointer<vtkDoubleArray> rational_weights;
502 :
503 : if (mesh.default_mapping_type() == ElemMappingType::RATIONAL_BERNSTEIN_MAP)
504 : {
505 : rational_weights = vtkSmartPointer<vtkDoubleArray>::New();
506 : rational_weights->SetName("RationalWeights");
507 : rational_weights->SetNumberOfComponents(1);
508 : weight_index = static_cast<int>(mesh.default_mapping_data());
509 : have_weights = true;
510 : }
511 : #endif
512 :
513 : vtkSmartPointer<vtkIntArray> node_id = vtkSmartPointer<vtkIntArray>::New();
514 : node_id->SetName("libmesh_node_id");
515 : node_id->SetNumberOfComponents(1);
516 :
517 : unsigned int local_node_counter = 0;
518 :
519 : for (const auto & node_ptr : mesh.local_node_ptr_range())
520 : {
521 : const Node & node = *node_ptr;
522 :
523 : double pnt[3] = {0, 0, 0};
524 : for (unsigned int i=0; i<LIBMESH_DIM; ++i)
525 : pnt[i] = double(node(i));
526 :
527 : // Fill mapping between global and local node numbers
528 : _local_node_map[node.id()] = local_node_counter;
529 :
530 : // add point
531 : #if VTK_VERSION_LESS_THAN(7,1,0)
532 : pcoords->InsertNextTupleValue(pnt);
533 : #else
534 : pcoords->InsertNextTuple(pnt);
535 : #endif
536 : #if !VTK_VERSION_LESS_THAN(9,0,0)
537 : if (have_weights)
538 : {
539 : Real weight = node.get_extra_datum<Real>(weight_index);
540 : rational_weights->InsertTuple1(local_node_counter, double(weight));
541 : }
542 : #endif
543 :
544 : node_id->InsertTuple1(local_node_counter, node.id());
545 :
546 : ++local_node_counter;
547 : }
548 :
549 : // add coordinates to points
550 : points->SetData(pcoords);
551 :
552 : // add points to grid
553 : _vtk_grid->SetPoints(points);
554 : _vtk_grid->GetPointData()->AddArray(node_id);
555 :
556 : #if !VTK_VERSION_LESS_THAN(9,0,0)
557 : if (have_weights)
558 : _vtk_grid->GetPointData()->SetRationalWeights(rational_weights);
559 :
560 : #endif
561 : }
562 :
563 :
564 :
565 : void VTKIO::cells_to_vtk()
566 : {
567 : const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
568 :
569 : auto& element_map = libmesh_map_find(_element_maps, mesh.default_mapping_type());
570 : vtkSmartPointer<vtkCellArray> cells = vtkSmartPointer<vtkCellArray>::New();
571 : vtkSmartPointer<vtkIdList> pts = vtkSmartPointer<vtkIdList>::New();
572 :
573 : std::vector<int> types(mesh.n_active_local_elem());
574 :
575 : // We already created this but we need to add more if we have any
576 : // ghost nodes
577 : vtkAbstractArray * abstract_node_id =
578 : _vtk_grid->GetPointData()->GetAbstractArray("libmesh_node_id");
579 : vtkIntArray * node_id = vtkIntArray::SafeDownCast(abstract_node_id);
580 : libmesh_assert(node_id);
581 :
582 : vtkSmartPointer<vtkIntArray> elem_id = vtkSmartPointer<vtkIntArray>::New();
583 : elem_id->SetName("libmesh_elem_id");
584 : elem_id->SetNumberOfComponents(1);
585 :
586 : vtkSmartPointer<vtkIntArray> subdomain_id = vtkSmartPointer<vtkIntArray>::New();
587 : subdomain_id->SetName("subdomain_id");
588 : subdomain_id->SetNumberOfComponents(1);
589 :
590 : vtkSmartPointer<vtkIntArray> elem_proc_id = vtkSmartPointer<vtkIntArray>::New();
591 : elem_proc_id->SetName("processor_id");
592 : elem_proc_id->SetNumberOfComponents(1);
593 :
594 : unsigned active_element_counter = 0;
595 : for (const auto & elem : mesh.active_local_element_ptr_range())
596 : {
597 : // When using rational bernstein these hold the weights
598 : if ( elem->type() == NODEELEM )
599 : continue;
600 :
601 : pts->SetNumberOfIds(elem->n_nodes());
602 :
603 : // get the connectivity for this element
604 : std::vector<dof_id_type> conn;
605 : elem->connectivity(0, VTK, conn);
606 :
607 : for (unsigned int i=0,
608 : n_conn = cast_int<unsigned int>(conn.size());
609 : i != n_conn; ++i)
610 : {
611 : // If the node ID is not found in the _local_node_map, we'll
612 : // add it to the _vtk_grid. NOTE[JWP]: none of the examples
613 : // I have actually enters this section of code...
614 : if (!_local_node_map.count(conn[i]))
615 : {
616 : dof_id_type global_node_id = elem->node_id(i);
617 :
618 : const Point & the_node = mesh.point(global_node_id);
619 :
620 : // InsertNextPoint accepts either a double or float array of length 3.
621 : double pt[3] = {0., 0., 0.};
622 : for (unsigned int d=0; d<LIBMESH_DIM; ++d)
623 : pt[d] = double(the_node(d));
624 :
625 : // Insert the point into the _vtk_grid
626 : vtkIdType local = _vtk_grid->GetPoints()->InsertNextPoint(pt);
627 :
628 : // Update the _local_node_map with the ID returned by VTK
629 : _local_node_map[global_node_id] =
630 : cast_int<dof_id_type>(local);
631 :
632 : node_id->InsertTuple1(local, global_node_id);
633 : }
634 :
635 : // Otherwise, the node ID was found in the _local_node_map, so
636 : // insert it into the vtkIdList.
637 : pts->InsertId(i, _local_node_map[conn[i]]);
638 : }
639 :
640 : vtkIdType vtkcellid = cells->InsertNextCell(pts);
641 : types[active_element_counter] = cast_int<int>(element_map.find(elem->type()));
642 :
643 : elem_id->InsertTuple1(vtkcellid, elem->id());
644 : subdomain_id->InsertTuple1(vtkcellid, elem->subdomain_id());
645 : elem_proc_id->InsertTuple1(vtkcellid, elem->processor_id());
646 : ++active_element_counter;
647 : } // end loop over active elements
648 :
649 : _vtk_grid->SetCells(types.data(), cells);
650 : _vtk_grid->GetCellData()->AddArray(elem_id);
651 : _vtk_grid->GetCellData()->AddArray(subdomain_id);
652 : _vtk_grid->GetCellData()->AddArray(elem_proc_id);
653 : }
654 :
655 : void VTKIO::node_values_to_vtk(const std::string & name,
656 : const std::vector<Real> & local_values)
657 : {
658 : vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
659 : data->SetName(name.c_str());
660 :
661 : libmesh_assert_equal_to(_local_node_map.size(), local_values.size());
662 :
663 : // number of local and ghost nodes
664 : data->SetNumberOfValues(_local_node_map.size());
665 :
666 : // copy values into vtk
667 : for (auto i : index_range(local_values)) {
668 : data->SetValue(i, double(local_values[i]));
669 : }
670 :
671 : _vtk_grid->GetPointData()->AddArray(data);
672 : }
673 :
674 : void VTKIO::get_local_node_values(std::vector<Number> & local_values,
675 : std::size_t variable,
676 : const std::vector<Number> & soln,
677 : const std::vector<std::string> & names)
678 : {
679 : const MeshBase & mesh = MeshOutput<MeshBase>::mesh();
680 : std::size_t num_vars = names.size();
681 : dof_id_type num_nodes = mesh.n_nodes();
682 :
683 : local_values.clear();
684 : local_values.resize(_local_node_map.size(), 0.0);
685 :
686 : // loop over all nodes and get the solution for the current
687 : // variable, if the node is in the current partition
688 : for (dof_id_type k=0; k<num_nodes; ++k)
689 : if (const auto local_node_it = _local_node_map.find(k);
690 : local_node_it != _local_node_map.end())
691 : local_values[local_node_it->second] = soln[k*num_vars + variable];
692 : }
693 :
694 :
695 :
696 : /**
697 : * FIXME: This is known to write nonsense on AMR meshes
698 : * and it strips the imaginary parts of complex Numbers
699 : *
700 : * This function is not currently used by anything, so it is commented
701 : * out, and may eventually be removed entirely.
702 : */
703 : // void VTKIO::system_vectors_to_vtk(const EquationSystems & es,
704 : // vtkUnstructuredGrid *& grid)
705 : // {
706 : // if (MeshOutput<MeshBase>::mesh().processor_id() == 0)
707 : // {
708 : // std::map<std::string, std::vector<Number>> vecs;
709 : // for (unsigned int i=0; i<es.n_systems(); ++i)
710 : // {
711 : // const System & sys = es.get_system(i);
712 : // System::const_vectors_iterator v_end = sys.vectors_end();
713 : // System::const_vectors_iterator it = sys.vectors_begin();
714 : // for (; it!= v_end; ++it)
715 : // {
716 : // // for all vectors on this system
717 : // std::vector<Number> values;
718 : // // libMesh::out<<"it "<<it->first<<std::endl;
719 : //
720 : // it->second->localize_to_one(values, 0);
721 : // // libMesh::out<<"finish localize"<<std::endl;
722 : // vecs[it->first] = values;
723 : // }
724 : // }
725 : //
726 : // std::map<std::string, std::vector<Number>>::iterator it = vecs.begin();
727 : //
728 : // for (; it!=vecs.end(); ++it)
729 : // {
730 : // vtkSmartPointer<vtkDoubleArray> data = vtkSmartPointer<vtkDoubleArray>::New();
731 : // data->SetName(it->first.c_str());
732 : // libmesh_assert_equal_to (it->second.size(), es.get_mesh().n_nodes());
733 : // data->SetNumberOfValues(it->second.size());
734 : //
735 : // for (auto i : index_range(it->second))
736 : // {
737 : // #ifdef LIBMESH_USE_COMPLEX_NUMBERS
738 : // libmesh_do_once (libMesh::err << "Only writing the real part for complex numbers!\n"
739 : // << "if you need this support contact " << LIBMESH_PACKAGE_BUGREPORT
740 : // << std::endl);
741 : // data->SetValue(i, it->second[i].real());
742 : // #else
743 : // data->SetValue(i, it->second[i]);
744 : // #endif
745 : //
746 : // }
747 : // grid->GetPointData()->AddArray(data);
748 : // }
749 : // }
750 : // }
751 :
752 :
753 :
754 : #else // !LIBMESH_HAVE_VTK
755 :
756 0 : void VTKIO::read (const std::string & name)
757 : {
758 0 : libmesh_error_msg("Cannot read VTK file: " << name \
759 : << "\nYou must have VTK installed and correctly configured to read VTK meshes.");
760 : }
761 :
762 :
763 :
764 0 : void VTKIO::write_nodal_data (const std::string & fname,
765 : const std::vector<Number> &,
766 : const std::vector<std::string> &)
767 : {
768 0 : libmesh_error_msg("Cannot write VTK file: " << fname \
769 : << "\nYou must have VTK installed and correctly configured to read VTK meshes.");
770 : }
771 :
772 :
773 : #endif // LIBMESH_HAVE_VTK
774 :
775 :
776 :
777 : } // namespace libMesh
|