vtk-m/vtkm/cont/DataSet.cxx
Kenneth Moreland 2af555f6c9 Simplify serialization of DataSet objects
`vtkm::cont::DataSet` is a dynamic object that can hold cell sets and
fields of many different types, none of which are known until runtime. This
causes a problem with serialization, which has to know what type to compile
the serialization for, particularly when unserializing the type at the
receiving end. The original implementation "solved" the problem by creating
a secondary wrapper object that was templated on types of field arrays and
cell sets that might be serialized. This is not a great solution as it
punts the problem to algorithm developers.

This problem has been completely solved for fields, as it is possible to
serialize most types of arrays without knowing their type now. You still
need to iterate over every possible `CellSet` type, but there are not that
many `CellSet`s that are practically encountered. Thus, there is now a
direct implementation of `Serialization` for `DataSet` that covers all the
data types you are likely to encounter.

The old `SerializableDataSet` has been deprecated. In the unlikely event an
algorithm needs to transfer a non-standard type of `CellSet` (such as a
permuted cell set), it can use the replacement `DataSetWithCellSetTypes`,
which just specifies the cell set types.
2023-03-03 09:17:44 -07:00

375 lines
11 KiB
C++

//============================================================================
// Copyright (c) Kitware, Inc.
// All rights reserved.
// See LICENSE.txt for details.
//
// This software is distributed WITHOUT ANY WARRANTY; without even
// the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
// PURPOSE. See the above copyright notice for more information.
//============================================================================
#include <vtkm/cont/CellSetExplicit.h>
#include <vtkm/cont/CellSetExtrude.h>
#include <vtkm/cont/CellSetSingleType.h>
#include <vtkm/cont/CellSetStructured.h>
#include <vtkm/cont/DataSet.h>
#include <vtkm/cont/ErrorBadValue.h>
#include <vtkm/cont/Logging.h>
#include <algorithm>
namespace
{
VTKM_CONT void CheckFieldSize(const vtkm::cont::UnknownCellSet& cellSet,
const vtkm::cont::Field& field)
{
if (!cellSet.IsValid())
{
return;
}
switch (field.GetAssociation())
{
case vtkm::cont::Field::Association::Points:
if (cellSet.GetNumberOfPoints() != field.GetData().GetNumberOfValues())
{
VTKM_LOG_S(vtkm::cont::LogLevel::Warn,
"The size of field `"
<< field.GetName() << "` (" << field.GetData().GetNumberOfValues()
<< " values) does not match the size of the data set structure ("
<< cellSet.GetNumberOfPoints() << " points).");
}
break;
case vtkm::cont::Field::Association::Cells:
if (cellSet.GetNumberOfCells() != field.GetData().GetNumberOfValues())
{
VTKM_LOG_S(vtkm::cont::LogLevel::Warn,
"The size of field `"
<< field.GetName() << "` (" << field.GetData().GetNumberOfValues()
<< " values) does not match the size of the data set structure ("
<< cellSet.GetNumberOfCells() << " cells).");
}
break;
default:
// Ignore as the association does not match any topological element.
break;
}
}
VTKM_CONT void CheckFieldSizes(const vtkm::cont::UnknownCellSet& cellSet,
const vtkm::cont::internal::FieldCollection& fields)
{
vtkm::IdComponent numFields = fields.GetNumberOfFields();
for (vtkm::IdComponent fieldIndex = 0; fieldIndex < numFields; ++fieldIndex)
{
CheckFieldSize(cellSet, fields.GetField(fieldIndex));
}
}
} // anonymous namespace
namespace vtkm
{
namespace cont
{
VTKM_CONT std::string& GlobalGhostCellFieldName() noexcept
{
static std::string GhostCellName("vtkGhostCells");
return GhostCellName;
}
VTKM_CONT const std::string& GetGlobalGhostCellFieldName() noexcept
{
return GlobalGhostCellFieldName();
}
VTKM_CONT void SetGlobalGhostCellFieldName(const std::string& name) noexcept
{
GlobalGhostCellFieldName() = name;
}
void DataSet::Clear()
{
this->CoordSystemNames.clear();
this->Fields.Clear();
this->CellSet = this->CellSet.NewInstance();
}
void DataSet::AddField(const Field& field)
{
CheckFieldSize(this->CellSet, field);
this->Fields.AddField(field);
}
vtkm::Id DataSet::GetNumberOfCells() const
{
return this->CellSet.GetNumberOfCells();
}
vtkm::Id DataSet::GetNumberOfPoints() const
{
if (this->CellSet.IsValid())
{
return this->CellSet.GetNumberOfPoints();
}
// If there is no cell set, then try to use a coordinate system to get the number
// of points.
if (this->GetNumberOfCoordinateSystems() > 0)
{
return this->GetCoordinateSystem().GetNumberOfPoints();
}
// If there is no coordinate system either, we can try to guess the number of
// points by finding a point field.
for (vtkm::IdComponent fieldIdx = 0; fieldIdx < this->Fields.GetNumberOfFields(); ++fieldIdx)
{
const vtkm::cont::Field& field = this->Fields.GetField(fieldIdx);
if (field.GetAssociation() == vtkm::cont::Field::Association::Points)
{
return field.GetData().GetNumberOfValues();
}
}
// There are no point fields either.
return 0;
}
const std::string& DataSet::GetGhostCellFieldName() const
{
if (this->GhostCellName)
{
return *this->GhostCellName;
}
else
{
return GetGlobalGhostCellFieldName();
}
}
bool DataSet::HasGhostCellField() const
{
return this->HasCellField(this->GetGhostCellFieldName());
}
const vtkm::cont::Field& DataSet::GetGhostCellField() const
{
if (this->HasGhostCellField())
{
return this->GetCellField(this->GetGhostCellFieldName());
}
else
{
throw vtkm::cont::ErrorBadValue("No Ghost Cell Field");
}
}
vtkm::IdComponent DataSet::AddCoordinateSystem(const vtkm::cont::CoordinateSystem& cs)
{
this->AddField(cs);
return this->AddCoordinateSystem(cs.GetName());
}
vtkm::IdComponent DataSet::AddCoordinateSystem(const std::string& pointFieldName)
{
// Check to see if we already have this coordinate system.
vtkm::IdComponent index = this->GetCoordinateSystemIndex(pointFieldName);
if (index >= 0)
{
return index;
}
// Check to make sure this is a valid point field.
if (!this->HasPointField(pointFieldName))
{
throw vtkm::cont::ErrorBadValue("Cannot set point field named `" + pointFieldName +
"` as a coordinate system because it does not exist.");
}
// Add the field to the list of coordinates.
this->CoordSystemNames.push_back(pointFieldName);
return static_cast<vtkm::IdComponent>(this->CoordSystemNames.size() - 1);
}
void DataSet::SetCellSetImpl(const vtkm::cont::UnknownCellSet& cellSet)
{
CheckFieldSizes(cellSet, this->Fields);
this->CellSet = cellSet;
}
void DataSet::SetGhostCellFieldName(const std::string& name)
{
this->GhostCellName.reset(new std::string(name));
}
void DataSet::SetGhostCellField(const std::string& name)
{
if (this->HasCellField(name))
{
this->SetGhostCellFieldName(name);
}
else
{
throw vtkm::cont::ErrorBadValue("No such cell field " + name);
}
}
void DataSet::SetGhostCellField(const vtkm::cont::Field& field)
{
if (field.GetAssociation() == vtkm::cont::Field::Association::Cells)
{
this->SetGhostCellField(field.GetName(), field.GetData());
}
else
{
throw vtkm::cont::ErrorBadValue("A ghost cell field must be a cell field.");
}
}
void DataSet::SetGhostCellField(const std::string& fieldName,
const vtkm::cont::UnknownArrayHandle& field)
{
this->AddCellField(fieldName, field);
this->SetGhostCellField(fieldName);
}
void DataSet::SetGhostCellField(const vtkm::cont::UnknownArrayHandle& field)
{
this->SetGhostCellField(GetGlobalGhostCellFieldName(), field);
}
void DataSet::CopyStructure(const vtkm::cont::DataSet& source)
{
// Copy the cells.
this->CellSet = source.CellSet;
// Copy the coordinate systems.
this->CoordSystemNames.clear();
vtkm::IdComponent numCoords = source.GetNumberOfCoordinateSystems();
for (vtkm::IdComponent coordIndex = 0; coordIndex < numCoords; ++coordIndex)
{
this->AddCoordinateSystem(source.GetCoordinateSystem(coordIndex));
}
// Copy the ghost cells.
// Note that we copy the GhostCellName separately from the field it points to
// to preserve (or remove) the case where the ghost cell name follows the
// global ghost cell name.
this->GhostCellName = source.GhostCellName;
if (source.HasGhostCellField())
{
this->AddField(source.GetGhostCellField());
}
CheckFieldSizes(this->CellSet, this->Fields);
}
vtkm::cont::CoordinateSystem DataSet::GetCoordinateSystem(vtkm::Id index) const
{
VTKM_ASSERT((index >= 0) && (index < this->GetNumberOfCoordinateSystems()));
return this->GetPointField(this->CoordSystemNames[static_cast<std::size_t>(index)]);
}
vtkm::IdComponent DataSet::GetCoordinateSystemIndex(const std::string& name) const
{
auto nameIter = std::find(this->CoordSystemNames.begin(), this->CoordSystemNames.end(), name);
if (nameIter != this->CoordSystemNames.end())
{
return static_cast<vtkm::IdComponent>(std::distance(this->CoordSystemNames.begin(), nameIter));
}
else
{
return -1;
}
}
const std::string& DataSet::GetCoordinateSystemName(vtkm::Id index) const
{
VTKM_ASSERT((index >= 0) && (index < this->GetNumberOfCoordinateSystems()));
return this->CoordSystemNames[static_cast<std::size_t>(index)];
}
vtkm::cont::CoordinateSystem DataSet::GetCoordinateSystem(const std::string& name) const
{
vtkm::Id index = this->GetCoordinateSystemIndex(name);
if (index < 0)
{
std::string error_message("No coordinate system with the name " + name +
" valid names are: \n");
for (const auto& csn : this->CoordSystemNames)
{
error_message += csn + "\n";
}
throw vtkm::cont::ErrorBadValue(error_message);
}
return this->GetCoordinateSystem(index);
}
void DataSet::PrintSummary(std::ostream& out) const
{
out << "DataSet:\n";
out << " CoordSystems[" << this->CoordSystemNames.size() << "]\n";
out << " ";
for (const auto& csn : this->CoordSystemNames)
{
out << " " << csn;
}
out << "\n";
out << " CellSet \n";
this->GetCellSet().PrintSummary(out);
out << " Fields[" << this->GetNumberOfFields() << "]\n";
for (vtkm::Id index = 0; index < this->GetNumberOfFields(); index++)
{
this->GetField(index).PrintSummary(out);
}
out.flush();
}
void DataSet::ConvertToExpected()
{
for (vtkm::IdComponent coordIndex = 0; coordIndex < this->GetNumberOfCoordinateSystems();
++coordIndex)
{
this->GetCoordinateSystem(coordIndex).ConvertToExpected();
}
for (vtkm::IdComponent fieldIndex = 0; fieldIndex < this->GetNumberOfFields(); ++fieldIndex)
{
this->GetField(fieldIndex).ConvertToExpected();
}
}
} // namespace cont
} // namespace vtkm
namespace mangled_diy_namespace
{
using SerializedCellSetTypes = vtkm::ListAppend<VTKM_DEFAULT_CELL_SET_LIST,
vtkm::List<vtkm::cont::CellSetStructured<1>,
vtkm::cont::CellSetStructured<2>,
vtkm::cont::CellSetStructured<3>,
vtkm::cont::CellSetExplicit<>,
vtkm::cont::CellSetSingleType<>,
vtkm::cont::CellSetExtrude>>;
using DefaultDataSetWithCellTypes = vtkm::cont::DataSetWithCellSetTypes<SerializedCellSetTypes>;
void Serialization<vtkm::cont::DataSet>::save(BinaryBuffer& bb, const vtkm::cont::DataSet& obj)
{
vtkmdiy::save(bb, DefaultDataSetWithCellTypes{ obj });
}
void Serialization<vtkm::cont::DataSet>::load(BinaryBuffer& bb, vtkm::cont::DataSet& obj)
{
DefaultDataSetWithCellTypes data;
vtkmdiy::load(bb, data);
obj = data.DataSet;
}
} // namespace mangled_diy_namespace