From d82bca6e6dc37b15e5e3997c5b7b1697594c2e44 Mon Sep 17 00:00:00 2001 From: Bernd Flemisch <bernd@iws.uni-stuttgart.de> Date: Thu, 26 Jul 2018 13:07:01 +0200 Subject: [PATCH] [io][vtk][parallel] read from parallel Vtk files Works for non-staggered discretizations and solution vectors without state so far. --- dumux/io/loadsolution.hh | 182 ++++++++++++++++-- dumux/io/vtk/vtkreader.hh | 56 +++++- test/freeflow/navierstokes/test_channel.cc | 4 +- .../2p/implicit/incompressible/test_2p_fv.cc | 2 +- .../2p2c/implicit/test_2p2c_fv.cc | 2 +- 5 files changed, 224 insertions(+), 22 deletions(-) diff --git a/dumux/io/loadsolution.hh b/dumux/io/loadsolution.hh index dad597320e..c047c386d3 100644 --- a/dumux/io/loadsolution.hh +++ b/dumux/io/loadsolution.hh @@ -51,15 +51,61 @@ struct hasState } // end namespace Detail +template <class FieldType, class Container, class GridView, int codim> +class LoadSolutionDataHandle +: public Dune::CommDataHandleIF< LoadSolutionDataHandle<FieldType, Container, GridView, codim>, + FieldType > +{ + using ElementMapper = Dune::MultipleCodimMultipleGeomTypeMapper<GridView>; + +public: + LoadSolutionDataHandle(Container &container, + const GridView &gridView) + : mapper_(gridView, Dune::mcmgLayout(Dune::Codim<codim>())) + , container_(container) + {} + + bool contains(int dim, int cd) const + { return cd == codim; } + + bool fixedsize(int dim, int cd) const + { return true; } + + template<class EntityType> + size_t size (const EntityType &e) const + { return 1; } + + template<class MessageBufferImp, class EntityType> + void gather(MessageBufferImp &buff, const EntityType &e) const + { + int vIdx = mapper_.index(e); + buff.write(container_[vIdx]); + } + + template<class MessageBufferImp, class EntityType> + void scatter(MessageBufferImp &buff, const EntityType &e, size_t n) + { + int vIdx = mapper_.index(e); + + FieldType tmp; + buff.read(tmp); + container_[vIdx] = tmp; + } + +private: + ElementMapper mapper_; + Container &container_; +}; + /*! * \ingroup InputOutput - * \brief helper function to read from a file into a solution vector + * \brief read from a sequential file into a solution vector without state */ template <class SolutionVector, class PvNamesFunc> -auto loadSolutionFromVtkFile(const std::string fileName, - const VTKReader::DataType& dataType, - PvNamesFunc&& pvNamesFunc, - SolutionVector& sol) +auto loadSolutionFromSequentialVtkFile(const std::string fileName, + const VTKReader::DataType& dataType, + PvNamesFunc&& pvNamesFunc, + SolutionVector& sol) -> typename std::enable_if_t<!decltype(isValid(Detail::hasState())(sol[0]))::value, void> { VTKReader vtu(fileName); @@ -80,13 +126,73 @@ auto loadSolutionFromVtkFile(const std::string fileName, /*! * \ingroup InputOutput - * \brief helper function to read from a file into a solution vector + * \brief read from a parallel file into a solution vector without state + */ +template <class SolutionVector, class PvNamesFunc, class FVGridGeometry> +auto loadSolutionFromParallelVtkFile(const std::string fileName, + const VTKReader::DataType& dataType, + PvNamesFunc&& pvNamesFunc, + SolutionVector& sol, + const FVGridGeometry& fvGridGeometry) +-> typename std::enable_if_t<!decltype(isValid(Detail::hasState())(sol[0]))::value, void> +{ + VTKReader vtu(fileName); + using PrimaryVariables = typename SolutionVector::block_type; + using Scalar = typename PrimaryVariables::field_type; + using GridView = typename FVGridGeometry::GridView; + static constexpr auto dim = GridView::dimension; + + for (size_t pvIdx = 0; pvIdx < PrimaryVariables::dimension; ++pvIdx) + { + const auto pvName = pvNamesFunc(pvIdx); + auto vec = vtu.readData<std::vector<Scalar>>(pvName, dataType); + if (vec.size() != sol.size() && fvGridGeometry.gridView().comm().size() == 1) + { + DUNE_THROW(Dune::IOError, "Size mismatch between solution vector and read data (" + << sol.size() << " != " << vec.size() << ")"); + } + + std::vector<bool> visited; + if (dataType == VTKReader::DataType::pointData) + { + visited.resize(fvGridGeometry.gridView().size(dim), false); + } + + std::size_t i = 0; + for (const auto& element : elements(fvGridGeometry.gridView(), Dune::Partitions::interior)) + { + if (dataType == VTKReader::DataType::cellData) + { + auto eIdx = fvGridGeometry.elementMapper().index(element); + sol[eIdx][pvIdx] = vec[i]; + ++i; + } + else + { + for (int vIdxLocal = 0; vIdxLocal < element.subEntities(dim); ++vIdxLocal) + { + auto vIdxGlobal = fvGridGeometry.vertexMapper().subIndex(element, vIdxLocal, dim); + if (!visited[vIdxGlobal]) + { + sol[vIdxGlobal][pvIdx] = vec[i]; + ++i; + visited[vIdxGlobal] = true; + } + } + } + } + } +} + +/*! + * \ingroup InputOutput + * \brief read from a sequential file into a solution vector with state */ template <class SolutionVector, class PvNamesFunc> -auto loadSolutionFromVtkFile(const std::string fileName, - const VTKReader::DataType& dataType, - PvNamesFunc&& pvNamesFunc, - SolutionVector& sol) +auto loadSolutionFromSequentialVtkFile(const std::string fileName, + const VTKReader::DataType& dataType, + PvNamesFunc&& pvNamesFunc, + SolutionVector& sol) -> typename std::enable_if_t<decltype(isValid(Detail::hasState())(sol[0]))::value, void> { VTKReader vtu(fileName); @@ -121,6 +227,21 @@ auto loadSolutionFromVtkFile(const std::string fileName, } } +/*! + * \ingroup InputOutput + * \brief read from a parallel file into a solution vector with state + */ +template <class SolutionVector, class PvNamesFunc, class FVGridGeometry> +auto loadSolutionFromParallelVtkFile(const std::string fileName, + const VTKReader::DataType& dataType, + PvNamesFunc&& pvNamesFunc, + SolutionVector& sol, + const FVGridGeometry& fvGridGeometry) +-> typename std::enable_if_t<decltype(isValid(Detail::hasState())(sol[0]))::value, void> +{ + DUNE_THROW(Dune::NotImplemented, "reading solution with state from a parallel Vtk file"); +} + /*! * \ingroup InputOutput * \brief helper function to determine the primary variable names of a model with privar state @@ -223,25 +344,56 @@ std::string primaryVariableNameFace(int pvIdx) * \brief load a solution vector from file * \note Supports the following file extensions: *.vtu *.vtp */ -template <class SolutionVector, class PvNamesFunc> +template <class SolutionVector, class PvNamesFunc, class FVGridGeometry> void loadSolution(const std::string& fileName, DiscretizationMethod discMethod, PvNamesFunc&& pvNamesFunc, - SolutionVector& sol) + SolutionVector& sol, + const FVGridGeometry& fvGridGeometry) { const auto extension = fileName.substr(fileName.find_last_of(".") + 1); + auto dataType = discMethod == DiscretizationMethod::box ? + VTKReader::DataType::pointData : VTKReader::DataType::cellData; if (extension == "vtu" || extension == "vtp") { - auto dataType = discMethod == DiscretizationMethod::box ? - VTKReader::DataType::pointData : VTKReader::DataType::cellData; if (discMethod == DiscretizationMethod::staggered && extension == "vtp") dataType = VTKReader::DataType::pointData; - loadSolutionFromVtkFile(fileName, dataType, pvNamesFunc, sol); + loadSolutionFromSequentialVtkFile(fileName, dataType, pvNamesFunc, sol); + } + else if (extension == "pvtu" || extension == "pvtp") + { + if (discMethod == DiscretizationMethod::staggered) + DUNE_THROW(Dune::NotImplemented, "reading staggered solution from a parallel Vtk file"); + + loadSolutionFromParallelVtkFile(fileName, dataType, pvNamesFunc, sol, fvGridGeometry); } else DUNE_THROW(Dune::NotImplemented, "loadSolution for extension " << extension); + + // synchronize values on ghost and overlap dofs + if (fvGridGeometry.gridView().comm().size() > 1) + { + using PrimaryVariables = typename SolutionVector::block_type; + using GridView = typename FVGridGeometry::GridView; + if (dataType == VTKReader::DataType::cellData) + { + LoadSolutionDataHandle<PrimaryVariables, SolutionVector, GridView, 0> + dataHandle(sol, fvGridGeometry.gridView()); + fvGridGeometry.gridView().communicate(dataHandle, + Dune::InteriorBorder_All_Interface, + Dune::ForwardCommunication); + } + else + { + LoadSolutionDataHandle<PrimaryVariables, SolutionVector, GridView, GridView::dimension> + dataHandle(sol, fvGridGeometry.gridView()); + fvGridGeometry.gridView().communicate(dataHandle, + Dune::InteriorBorder_All_Interface, + Dune::ForwardCommunication); + } + } } } // namespace Dumux diff --git a/dumux/io/vtk/vtkreader.hh b/dumux/io/vtk/vtkreader.hh index 30ccb3fa28..127ce98b80 100644 --- a/dumux/io/vtk/vtkreader.hh +++ b/dumux/io/vtk/vtkreader.hh @@ -31,6 +31,7 @@ #include <type_traits> #include <unordered_map> +#include <dune/common/parallel/mpihelper.hh> #include <dune/common/exceptions.hh> #include <dune/grid/common/capabilities.hh> #include <dune/grid/io/file/vtk/common.hh> @@ -60,10 +61,13 @@ public: explicit VTKReader(const std::string& fileName) : fileName_(fileName) { + if (Dune::MPIHelper::getCollectiveCommunication().size() > 1) + getSequentialNameFromParallelName_(); + using namespace tinyxml2; - const auto eResult = doc_.LoadFile(fileName.c_str()); + const auto eResult = doc_.LoadFile(fileName_.c_str()); if (eResult != XML_SUCCESS) - DUNE_THROW(Dune::IOError, "Couldn't open XML file " << fileName << "."); + DUNE_THROW(Dune::IOError, "Couldn't open XML file " << fileName_ << "."); } /*! @@ -133,6 +137,52 @@ public: } private: + /*! + * \brief get sequential Vtk filename from a parallel one + */ + void getSequentialNameFromParallelName_() + { + using namespace tinyxml2; + + const auto eResult = doc_.LoadFile(fileName_.c_str()); + if (eResult != XML_SUCCESS) + DUNE_THROW(Dune::IOError, "Couldn't open XML file " << fileName_ << "."); + + const XMLElement* pieceNode = doc_.FirstChildElement("VTKFile"); + if (pieceNode == nullptr) + DUNE_THROW(Dune::IOError, "Couldn't get 'VTKFile' node in " << fileName_ << "."); + + pieceNode = pieceNode->FirstChildElement("PUnstructuredGrid"); + if (pieceNode == nullptr) + pieceNode = doc_.FirstChildElement("VTKFile")->FirstChildElement("PPolyData"); + if (pieceNode == nullptr) + DUNE_THROW(Dune::IOError, "Couldn't get 'PUnstructuredGrid' or 'PPolyData' node in " << fileName_ << "."); + + pieceNode = pieceNode->FirstChildElement("Piece"); + const auto& comm = Dune::MPIHelper::getCollectiveCommunication(); + for (int rank = 0; rank < comm.size(); ++rank) + { + if (pieceNode == nullptr) + DUNE_THROW(Dune::IOError, "Couldn't get 'Piece' node for rank " + << rank << " in " << fileName_ << "."); + + const char *seqName = pieceNode->Attribute("Source"); + + if (seqName == nullptr) + DUNE_THROW(Dune::IOError, "Couldn't get 'Source' attribute of a 'Piece' node in " << fileName_); + + if (comm.rank() == rank) + { + fileName_ = seqName; + break; + } + + pieceNode = pieceNode->NextSiblingElement("Piece"); + } + + doc_.Clear(); + } + /*! * \brief Read a grid from a vtk/vtu/vtp file * \param factory the (emtpy) grid factory @@ -387,7 +437,7 @@ private: adaptPointDimension_(std::vector<Dune::FieldVector<double, 3>>&& points3D) const { return points3D; } - const std::string fileName_; //!< the vtk file name + std::string fileName_; //!< the vtk file name tinyxml2::XMLDocument doc_; //!< the xml document created from file with name fileName_ }; diff --git a/test/freeflow/navierstokes/test_channel.cc b/test/freeflow/navierstokes/test_channel.cc index 7350a12d50..9b85acf622 100644 --- a/test/freeflow/navierstokes/test_channel.cc +++ b/test/freeflow/navierstokes/test_channel.cc @@ -145,11 +145,11 @@ int main(int argc, char** argv) try auto fileNameCell = getParam<std::string>("Restart.FileCell"); loadSolution(fileNameCell, FVGridGeometry::discMethod, - primaryVariableNameCell<ModelTraits>, x[Dune::index_constant<0>{}]); + primaryVariableNameCell<ModelTraits>, x[Dune::index_constant<0>{}], *fvGridGeometry); auto fileNameFace = getParam<std::string>("Restart.FileFace"); loadSolution(fileNameFace, FVGridGeometry::discMethod, - primaryVariableNameFace<ModelTraits>, x[Dune::index_constant<1>{}]); + primaryVariableNameFace<ModelTraits>, x[Dune::index_constant<1>{}], *fvGridGeometry); } else problem->applyInitialSolution(x); diff --git a/test/porousmediumflow/2p/implicit/incompressible/test_2p_fv.cc b/test/porousmediumflow/2p/implicit/incompressible/test_2p_fv.cc index 6098c4a07e..06f13ebd40 100644 --- a/test/porousmediumflow/2p/implicit/incompressible/test_2p_fv.cc +++ b/test/porousmediumflow/2p/implicit/incompressible/test_2p_fv.cc @@ -138,7 +138,7 @@ int main(int argc, char** argv) try { using ModelTraits = typename GET_PROP_TYPE(TypeTag, ModelTraits); auto fileName = getParam<std::string>("Restart.File"); - loadSolution(fileName, FVGridGeometry::discMethod, primaryVariableName<ModelTraits>, x); + loadSolution(fileName, FVGridGeometry::discMethod, primaryVariableName<ModelTraits>, x, *fvGridGeometry); } else problem->applyInitialSolution(x); diff --git a/test/porousmediumflow/2p2c/implicit/test_2p2c_fv.cc b/test/porousmediumflow/2p2c/implicit/test_2p2c_fv.cc index 3ef717512c..a1190fff94 100644 --- a/test/porousmediumflow/2p2c/implicit/test_2p2c_fv.cc +++ b/test/porousmediumflow/2p2c/implicit/test_2p2c_fv.cc @@ -107,7 +107,7 @@ int main(int argc, char** argv) try using ModelTraits = typename GET_PROP_TYPE(TypeTag, ModelTraits); using FluidSystem = typename GET_PROP_TYPE(TypeTag, FluidSystem); auto fileName = getParam<std::string>("Restart.File"); - loadSolution(fileName, FVGridGeometry::discMethod, primaryVariableName<ModelTraits, FluidSystem>, x); + loadSolution(fileName, FVGridGeometry::discMethod, primaryVariableName<ModelTraits, FluidSystem>, x, *fvGridGeometry); } else problem->applyInitialSolution(x); -- GitLab