Program Listing for File vtk_utils.cc¶
↰ Return to documentation for file (source/vtk_utils.cc)
#include "vtk_utils.h"
#include <string>
#include <vector>
#ifdef DEAL_II_WITH_VTK
# include <deal.II/distributed/fully_distributed_tria.h>
# include <deal.II/dofs/dof_renumbering.h>
# include <deal.II/fe/fe.h>
# include <deal.II/fe/fe_dgq.h>
# include <deal.II/fe/fe_nothing.h>
# include <deal.II/fe/fe_q.h>
# include <deal.II/fe/fe_system.h>
# include <deal.II/grid/grid_in.h>
# include <deal.II/grid/grid_out.h>
# include <deal.II/grid/grid_tools.h>
# include <deal.II/grid/tria.h>
# include <deal.II/grid/tria_accessor.h>
# include <deal.II/grid/tria_description.h>
# include <deal.II/grid/tria_iterator.h>
# include <vtkCellData.h>
# include <vtkCleanUnstructuredGrid.h>
# include <vtkDataArray.h>
# include <vtkPointData.h>
# include <vtkSmartPointer.h>
# include <vtkUnstructuredGrid.h>
# include <vtkUnstructuredGridReader.h>
# include <stdexcept>
namespace VTKUtils
{
template <int dim, int spacedim>
void
read_vtk(const std::string &vtk_filename,
Triangulation<dim, spacedim> &tria,
const bool cleanup)
{
auto reader = vtkSmartPointer<vtkUnstructuredGridReader>::New();
reader->SetFileName(vtk_filename.c_str());
reader->Update();
vtkUnstructuredGrid *grid = reader->GetOutput();
AssertThrow(grid, ExcMessage("Failed to read VTK file: " + vtk_filename));
auto cleaner = vtkSmartPointer<vtkCleanUnstructuredGrid>::New();
// Cleanup the triangulation if requested
if (cleanup)
{
cleaner->SetInputData(grid);
cleaner->Update();
grid = cleaner->GetOutput();
AssertThrow(grid,
ExcMessage("Failed to clean VTK file: " + vtk_filename));
}
// Read points
vtkPoints *vtk_points = grid->GetPoints();
const vtkIdType n_points = vtk_points->GetNumberOfPoints();
std::vector<Point<spacedim>> points(n_points);
for (vtkIdType i = 0; i < n_points; ++i)
{
std::array<double, 3> coords = {{0, 0, 0}};
vtk_points->GetPoint(i, coords.data());
for (unsigned int d = 0; d < spacedim; ++d)
points[i][d] = coords[d];
}
// Read cells
std::vector<CellData<dim>> cells;
const vtkIdType n_cells = grid->GetNumberOfCells();
for (vtkIdType i = 0; i < n_cells; ++i)
{
vtkCell *cell = grid->GetCell(i);
if constexpr (dim == 1)
{
if (cell->GetCellType() != VTK_LINE)
AssertThrow(false,
ExcMessage(
"Unsupported cell type in 1D VTK file: only "
"VTK_LINE is supported."));
AssertThrow(cell->GetNumberOfPoints() == 2,
ExcMessage(
"Only line cells with 2 points are supported."));
CellData<1> cell_data;
for (unsigned int j = 0; j < 2; ++j)
cell_data.vertices[j] = cell->GetPointId(j);
cell_data.material_id = 0;
cells.push_back(cell_data);
}
else if constexpr (dim == 2)
{
if (cell->GetCellType() == VTK_QUAD)
{
AssertThrow(cell->GetNumberOfPoints() == 4,
ExcMessage(
"Only quad cells with 4 points are supported."));
CellData<2> cell_data;
for (unsigned int j = 0; j < 4; ++j)
cell_data.vertices[j] = cell->GetPointId(j);
cell_data.material_id = 0;
cells.push_back(cell_data);
}
else if (cell->GetCellType() == VTK_TRIANGLE)
{
AssertThrow(
cell->GetNumberOfPoints() == 3,
ExcMessage(
"Only triangle cells with 3 points are supported."));
CellData<2> cell_data;
for (unsigned int j = 0; j < 3; ++j)
cell_data.vertices[j] = cell->GetPointId(j);
cell_data.material_id = 0;
cells.push_back(cell_data);
}
else
AssertThrow(false,
ExcMessage(
"Unsupported cell type in 2D VTK file: only "
"VTK_QUAD and VTK_TRIANGLE are supported."));
}
else if constexpr (dim == 3)
{
if (cell->GetCellType() == VTK_HEXAHEDRON)
{
AssertThrow(cell->GetNumberOfPoints() == 8,
ExcMessage(
"Only hex cells with 8 points are supported."));
CellData<3> cell_data;
for (unsigned int j = 0; j < 8; ++j)
cell_data.vertices[j] = cell->GetPointId(j);
cell_data.material_id = 0;
// Numbering of vertices in VTK files is different from deal.II
std::swap(cell_data.vertices[2], cell_data.vertices[3]);
std::swap(cell_data.vertices[6], cell_data.vertices[7]);
cells.push_back(cell_data);
}
else if (cell->GetCellType() == VTK_TETRA)
{
AssertThrow(
cell->GetNumberOfPoints() == 4,
ExcMessage(
"Only tetrahedron cells with 4 points are supported."));
CellData<3> cell_data;
for (unsigned int j = 0; j < 4; ++j)
cell_data.vertices[j] = cell->GetPointId(j);
cell_data.material_id = 0;
cells.push_back(cell_data);
}
else if (cell->GetCellType() == VTK_WEDGE)
{
AssertThrow(cell->GetNumberOfPoints() == 6,
ExcMessage(
"Only prism cells with 6 points are supported."));
CellData<3> cell_data;
for (unsigned int j = 0; j < 6; ++j)
cell_data.vertices[j] = cell->GetPointId(j);
cell_data.material_id = 0;
cells.push_back(cell_data);
}
else if (cell->GetCellType() == VTK_PYRAMID)
{
AssertThrow(
cell->GetNumberOfPoints() == 5,
ExcMessage(
"Only pyramid cells with 5 points are supported."));
CellData<3> cell_data;
for (unsigned int j = 0; j < 5; ++j)
cell_data.vertices[j] = cell->GetPointId(j);
cell_data.material_id = 0;
cells.push_back(cell_data);
}
else
AssertThrow(
false,
ExcMessage(
"Unsupported cell type in 3D VTK file: only "
"VTK_HEXAHEDRON, VTK_TETRA, VTK_WEDGE, and VTK_PYRAMID are supported."));
}
else
{
AssertThrow(false, ExcMessage("Unsupported dimension."));
}
}
// Create triangulation
tria.create_triangulation(points, cells, SubCellData());
}
void
read_cell_data(const std::string &vtk_filename,
const std::string &cell_data_name,
Vector<double> &output_vector)
{
auto reader = vtkSmartPointer<vtkUnstructuredGridReader>::New();
reader->SetFileName(vtk_filename.c_str());
reader->Update();
vtkUnstructuredGrid *grid = reader->GetOutput();
AssertThrow(grid, ExcMessage("Failed to read VTK file: " + vtk_filename));
vtkDataArray *data_array =
grid->GetCellData()->GetArray(cell_data_name.c_str());
AssertThrow(data_array,
ExcMessage("Cell data array '" + cell_data_name +
"' not found in VTK file: " + vtk_filename));
vtkIdType n_tuples = data_array->GetNumberOfTuples();
int n_components = data_array->GetNumberOfComponents();
output_vector.reinit(n_tuples * n_components);
for (vtkIdType i = 0; i < n_tuples; ++i)
for (int j = 0; j < n_components; ++j)
output_vector[i * n_components + j] = data_array->GetComponent(i, j);
}
void
read_vertex_data(const std::string &vtk_filename,
const std::string &point_data_name,
Vector<double> &output_vector)
{
auto reader = vtkSmartPointer<vtkUnstructuredGridReader>::New();
reader->SetFileName(vtk_filename.c_str());
reader->Update();
vtkUnstructuredGrid *grid = reader->GetOutput();
AssertThrow(grid, ExcMessage("Failed to read VTK file: " + vtk_filename));
vtkDataArray *data_array =
grid->GetPointData()->GetArray(point_data_name.c_str());
AssertThrow(data_array,
ExcMessage("Point data array '" + point_data_name +
"' not found in VTK file: " + vtk_filename));
vtkIdType n_tuples = data_array->GetNumberOfTuples();
int n_components = data_array->GetNumberOfComponents();
output_vector.reinit(n_tuples * n_components);
for (vtkIdType i = 0; i < n_tuples; ++i)
for (int j = 0; j < n_components; ++j)
output_vector[i * n_components + j] = data_array->GetComponent(i, j);
}
void
read_data(const std::string &vtk_filename, Vector<double> &output_vector)
{
auto reader = vtkSmartPointer<vtkUnstructuredGridReader>::New();
reader->SetFileName(vtk_filename.c_str());
reader->Update();
vtkUnstructuredGrid *grid = reader->GetOutput();
AssertThrow(grid, ExcMessage("Failed to read VTK file: " + vtk_filename));
std::vector<double> data;
vtkPointData *point_data = grid->GetPointData();
if (point_data)
{
for (int i = 0; i < point_data->GetNumberOfArrays(); ++i)
{
vtkDataArray *data_array = point_data->GetArray(i);
if (!data_array)
continue;
vtkIdType n_tuples = data_array->GetNumberOfTuples();
int n_components = data_array->GetNumberOfComponents();
unsigned int current_size = data.size();
data.resize(current_size + n_tuples * n_components, 0.0);
for (vtkIdType tuple_idx = 0; tuple_idx < n_tuples; ++tuple_idx)
for (int comp_idx = 0; comp_idx < n_components; ++comp_idx)
data[current_size + tuple_idx * n_components + comp_idx] =
data_array->GetComponent(tuple_idx, comp_idx);
}
}
vtkCellData *cell_data = grid->GetCellData();
if (cell_data)
{
for (int i = 0; i < cell_data->GetNumberOfArrays(); ++i)
{
vtkDataArray *data_array = cell_data->GetArray(i);
if (!data_array)
continue;
vtkIdType n_tuples = data_array->GetNumberOfTuples();
int n_components = data_array->GetNumberOfComponents();
unsigned int current_size = data.size();
data.resize(current_size + n_tuples * n_components, true);
for (vtkIdType tuple_idx = 0; tuple_idx < n_tuples; ++tuple_idx)
for (int comp_idx = 0; comp_idx < n_components; ++comp_idx)
data[current_size + tuple_idx * n_components + comp_idx] =
data_array->GetComponent(tuple_idx, comp_idx);
}
}
output_vector.reinit(data.size());
std::copy(data.begin(), data.end(), output_vector.begin());
}
template <int dim, int spacedim>
std::pair<std::unique_ptr<FiniteElement<dim, spacedim>>,
std::vector<std::string>>
vtk_to_finite_element(const std::string &vtk_filename)
{
std::vector<std::string> data_names;
auto reader = vtkSmartPointer<vtkUnstructuredGridReader>::New();
reader->SetFileName(vtk_filename.c_str());
reader->Update();
vtkUnstructuredGrid *grid = reader->GetOutput();
AssertThrow(grid, ExcMessage("Failed to read VTK file: " + vtk_filename));
vtkCellData *cell_data = grid->GetCellData();
vtkPointData *point_data = grid->GetPointData();
std::vector<std::shared_ptr<FiniteElement<dim, spacedim>>> fe_collection;
std::vector<unsigned int> n_components_collection;
// Query point data fields
for (int i = 0; i < point_data->GetNumberOfArrays(); ++i)
{
vtkDataArray *arr = point_data->GetArray(i);
if (!arr)
continue;
std::string name = arr->GetName();
int n_comp = arr->GetNumberOfComponents();
if (n_comp == 1)
fe_collection.push_back(std::make_shared<FE_Q<dim, spacedim>>(1));
else
// Use FESystem for vector fields
fe_collection.push_back(
std::make_shared<FESystem<dim, spacedim>>(FE_Q<dim, spacedim>(1),
n_comp));
n_components_collection.push_back(n_comp);
data_names.push_back(name);
}
// Query cell data fields
for (int i = 0; i < cell_data->GetNumberOfArrays(); ++i)
{
vtkDataArray *arr = cell_data->GetArray(i);
if (!arr)
continue;
std::string name = arr->GetName();
int n_comp = arr->GetNumberOfComponents();
if (n_comp == 1)
fe_collection.push_back(std::make_shared<FE_DGQ<dim, spacedim>>(0));
else
// Use FESystem for vector fields
fe_collection.push_back(
std::make_shared<FESystem<dim, spacedim>>(FE_DGQ<dim, spacedim>(0),
n_comp));
n_components_collection.push_back(n_comp);
data_names.push_back(name);
}
// Build a FESystem with all fields
std::vector<const FiniteElement<dim, spacedim> *> fe_ptrs;
std::vector<unsigned int> multiplicities;
for (const auto &fe : fe_collection)
{
fe_ptrs.push_back(fe.get());
multiplicities.push_back(1);
}
if (fe_ptrs.empty())
return std::make_pair(std::make_unique<FE_Nothing<dim, spacedim>>(),
std::vector<std::string>());
else
{
return std::make_pair(
std::make_unique<FESystem<dim, spacedim>>(fe_ptrs, multiplicities),
data_names);
}
}
template <int dim, int spacedim>
void
read_vtk(const std::string &vtk_filename,
DoFHandler<dim, spacedim> &dof_handler,
Vector<double> &output_vector,
std::vector<std::string> &data_names)
{
// Get a non-const reference to the triangulation
auto &tria = const_cast<Triangulation<dim, spacedim> &>(
dof_handler.get_triangulation());
// Make sure the triangulation is actually a serial triangulation
auto parallel_tria =
dynamic_cast<const parallel::TriangulationBase<dim, spacedim> *>(&tria);
AssertThrow(parallel_tria == nullptr,
ExcMessage(
"The input triangulation must be a serial triangulation."));
// Clear the triangulation to ensure it is empty before reading
tria.clear();
// Read the mesh from the VTK file
read_vtk(vtk_filename, tria, /*cleanup=*/true);
Vector<double> raw_data_vector;
read_data(vtk_filename, raw_data_vector);
auto [fe, data_names_from_fe] =
vtk_to_finite_element<dim, spacedim>(vtk_filename);
dof_handler.distribute_dofs(*fe);
output_vector.reinit(dof_handler.n_dofs());
data_to_dealii_vector(tria, raw_data_vector, dof_handler, output_vector);
AssertDimension(dof_handler.n_dofs(), output_vector.size());
AssertDimension(dof_handler.get_fe().n_blocks(), data_names_from_fe.size());
data_names = data_names_from_fe;
}
template <int dim, int spacedim>
void
serial_vector_to_distributed_vector(
const DoFHandler<dim, spacedim> &serial_dh,
const DoFHandler<dim, spacedim> ¶llel_dh,
const Vector<double> &serial_vec,
LinearAlgebra::distributed::Vector<double> ¶llel_vec)
{
AssertDimension(serial_vec.size(), serial_dh.n_dofs());
AssertDimension(parallel_vec.size(), parallel_dh.n_dofs());
AssertDimension(parallel_dh.n_dofs(), serial_dh.n_dofs());
// Check that the two fe are the same
AssertThrow(serial_dh.get_fe() == parallel_dh.get_fe(),
ExcMessage("The finite element systems of the serial and "
"parallel DoFHandlers must be the same."));
std::vector<types::global_dof_index> serial_dof_indices(
serial_dh.get_fe().n_dofs_per_cell());
std::vector<types::global_dof_index> parallel_dof_indices(
parallel_dh.get_fe().n_dofs_per_cell());
// Assumption: serial and parallel meshes have the same ordering of cells.
auto serial_cell = serial_dh.begin_active();
auto parallel_cell = parallel_dh.begin_active();
for (; parallel_cell != parallel_dh.end(); ++parallel_cell)
if (parallel_cell->is_locally_owned())
{
// Advanced serial cell until we reach the same cell index of the
// parallel cell
while (serial_cell->id() < parallel_cell->id())
++serial_cell;
serial_cell->get_dof_indices(serial_dof_indices);
parallel_cell->get_dof_indices(parallel_dof_indices);
unsigned int serial_index = 0;
for (const auto &i : parallel_dof_indices)
{
if (parallel_vec.in_local_range(i))
{
parallel_vec[i] =
serial_vec[serial_dof_indices[serial_index]];
}
++serial_index;
}
}
parallel_vec.compress(VectorOperation::insert);
}
template <int dim, int spacedim>
std::vector<types::global_vertex_index>
distributed_to_serial_vertex_indices(
const Triangulation<dim, spacedim> &serial_tria,
const Triangulation<dim, spacedim> ¶llel_tria)
{
const auto locally_owned_indices =
GridTools::get_locally_owned_vertices(parallel_tria);
std::vector<types::global_vertex_index>
distributed_to_serial_vertex_indices(parallel_tria.n_vertices(),
numbers::invalid_unsigned_int);
// Assumption: serial and parallel meshes have the same ordering of cells.
auto serial_cell = serial_tria.begin_active();
auto parallel_cell = parallel_tria.begin_active();
for (; parallel_cell != parallel_tria.end(); ++parallel_cell)
if (parallel_cell->is_locally_owned())
{
// Advanced serial cell until we reach the same cell index of the
// parallel cell
while (serial_cell->id() < parallel_cell->id())
++serial_cell;
for (const unsigned int &v : serial_cell->vertex_indices())
{
const auto serial_index = serial_cell->vertex_index(v);
const auto parallel_index = parallel_cell->vertex_index(v);
if (locally_owned_indices[parallel_index])
distributed_to_serial_vertex_indices[parallel_index] =
serial_index;
}
}
return distributed_to_serial_vertex_indices;
}
} // namespace VTKUtils
// Explicit instantiation for 1D, 2D and 3D
template void
VTKUtils::read_vtk(const std::string &, Triangulation<1, 1> &, const bool);
template void
VTKUtils::read_vtk(const std::string &, Triangulation<1, 2> &, const bool);
template void
VTKUtils::read_vtk(const std::string &, Triangulation<1, 3> &, const bool);
template void
VTKUtils::read_vtk(const std::string &, Triangulation<2, 2> &, const bool);
template void
VTKUtils::read_vtk(const std::string &, Triangulation<2, 3> &, const bool);
template void
VTKUtils::read_vtk(const std::string &, Triangulation<3, 3> &, const bool);
template std::pair<std::unique_ptr<FiniteElement<1, 1>>,
std::vector<std::string>>
VTKUtils::vtk_to_finite_element(const std::string &);
template std::pair<std::unique_ptr<FiniteElement<1, 2>>,
std::vector<std::string>>
VTKUtils::vtk_to_finite_element(const std::string &);
template std::pair<std::unique_ptr<FiniteElement<1, 3>>,
std::vector<std::string>>
VTKUtils::vtk_to_finite_element(const std::string &);
template std::pair<std::unique_ptr<FiniteElement<2, 2>>,
std::vector<std::string>>
VTKUtils::vtk_to_finite_element(const std::string &);
template std::pair<std::unique_ptr<FiniteElement<2, 3>>,
std::vector<std::string>>
VTKUtils::vtk_to_finite_element(const std::string &);
template std::pair<std::unique_ptr<FiniteElement<3, 3>>,
std::vector<std::string>>
VTKUtils::vtk_to_finite_element(const std::string &);
template void
VTKUtils::read_vtk(const std::string &,
DoFHandler<1, 1> &,
Vector<double> &,
std::vector<std::string> &);
template void
VTKUtils::read_vtk(const std::string &,
DoFHandler<1, 2> &,
Vector<double> &,
std::vector<std::string> &);
template void
VTKUtils::read_vtk(const std::string &,
DoFHandler<1, 3> &,
Vector<double> &,
std::vector<std::string> &);
template void
VTKUtils::read_vtk(const std::string &,
DoFHandler<2, 2> &,
Vector<double> &,
std::vector<std::string> &);
template void
VTKUtils::read_vtk(const std::string &,
DoFHandler<2, 3> &,
Vector<double> &,
std::vector<std::string> &);
template void
VTKUtils::read_vtk(const std::string &,
DoFHandler<3, 3> &,
Vector<double> &,
std::vector<std::string> &);
template void
VTKUtils::serial_vector_to_distributed_vector(
const DoFHandler<1, 1> &,
const DoFHandler<1, 1> &,
const Vector<double> &,
LinearAlgebra::distributed::Vector<double> &);
template void
VTKUtils::serial_vector_to_distributed_vector(
const DoFHandler<1, 2> &,
const DoFHandler<1, 2> &,
const Vector<double> &,
LinearAlgebra::distributed::Vector<double> &);
template void
VTKUtils::serial_vector_to_distributed_vector(
const DoFHandler<1, 3> &,
const DoFHandler<1, 3> &,
const Vector<double> &,
LinearAlgebra::distributed::Vector<double> &);
template void
VTKUtils::serial_vector_to_distributed_vector(
const DoFHandler<2, 2> &,
const DoFHandler<2, 2> &,
const Vector<double> &,
LinearAlgebra::distributed::Vector<double> &);
template void
VTKUtils::serial_vector_to_distributed_vector(
const DoFHandler<2, 3> &,
const DoFHandler<2, 3> &,
const Vector<double> &,
LinearAlgebra::distributed::Vector<double> &);
template void
VTKUtils::serial_vector_to_distributed_vector(
const DoFHandler<3, 3> &,
const DoFHandler<3, 3> &,
const Vector<double> &,
LinearAlgebra::distributed::Vector<double> &);
template std::vector<types::global_vertex_index>
VTKUtils::distributed_to_serial_vertex_indices(const Triangulation<1, 1> &,
const Triangulation<1, 1> &);
template std::vector<types::global_vertex_index>
VTKUtils::distributed_to_serial_vertex_indices(const Triangulation<1, 2> &,
const Triangulation<1, 2> &);
template std::vector<types::global_vertex_index>
VTKUtils::distributed_to_serial_vertex_indices(const Triangulation<1, 3> &,
const Triangulation<1, 3> &);
template std::vector<types::global_vertex_index>
VTKUtils::distributed_to_serial_vertex_indices(const Triangulation<2, 2> &,
const Triangulation<2, 2> &);
template std::vector<types::global_vertex_index>
VTKUtils::distributed_to_serial_vertex_indices(const Triangulation<2, 3> &,
const Triangulation<2, 3> &);
template std::vector<types::global_vertex_index>
VTKUtils::distributed_to_serial_vertex_indices(const Triangulation<3, 3> &,
const Triangulation<3, 3> &);
#endif // DEAL_II_WITH_VTK