LCOV - code coverage report
Current view: top level - src/mesh - vtk_io.C (source / functions) Hit Total Coverage
Test: libMesh/libmesh: #4556 (e46269) with base 54e0d5 Lines: 0 15 0.0 %
Date: 2026-09-17 10:15:14 Functions: 0 6 0.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.14