Commit 9655072a authored by Praetorius, Simon's avatar Praetorius, Simon
Browse files

Merge branch 'feature/vtkwriter_test' into 'master'

add vtkwriter test based on dune-grid checkvtkfile

See merge request spraetor/dune-vtk!21
parents 09ecc54c 3cb9300e
......@@ -14,7 +14,7 @@ namespace Dune
virtual ~FileWriter () = default;
/// Write to file given by `filename` and (optionally) store additional data in `dataDir`
virtual void write (std::string const& filename, Std::optional<std::string> dataDir = {}) const = 0;
virtual std::string write (std::string const& filename, Std::optional<std::string> dataDir = {}) const = 0;
};
} // end namespace Dune
......@@ -52,7 +52,7 @@ namespace Dune
* \param dir (Ignored) Timestep files are already written and their filenames are
* stored in \ref timesteps_.
**/
virtual void write (std::string const& fn, Std::optional<std::string> dir = {}) const override;
virtual std::string write (std::string const& fn, Std::optional<std::string> dir = {}) const override;
/// Attach point data to the writer, \see VtkFunction for possible arguments
template <class Function, class... Args>
......
......@@ -49,7 +49,7 @@ void PvdWriter<W>
template <class W>
void PvdWriter<W>
std::string PvdWriter<W>
::write (std::string const& fn, Std::optional<std::string> /*dir*/) const
{
auto p = filesystem::path(fn);
......@@ -57,9 +57,12 @@ void PvdWriter<W>
p.remove_filename();
p /= name.string();
std::string outputFilename;
int commRank = vtkWriter_.comm().rank();
if (commRank == 0) {
std::ofstream out(p.string() + ".pvd", std::ios_base::ate | std::ios::binary);
outputFilename = p.string() + ".pvd";
std::ofstream out(outputFilename, std::ios_base::ate | std::ios::binary);
assert(out.is_open());
out.imbue(std::locale::classic());
......@@ -69,6 +72,8 @@ void PvdWriter<W>
writeFile(out);
}
return outputFilename;
}
......
......@@ -76,8 +76,10 @@ namespace Dune
*
* \param fn Filename of the Timeseries file. May contain a directory and any file extension.
* \param dir The optional parameter specifies the directory of the partition files.
*
* \returns File name of the actual written file
**/
virtual void write (std::string const& fn, Std::optional<std::string> dir = {}) const override;
virtual std::string write (std::string const& fn, Std::optional<std::string> dir = {}) const override;
/// Attach point data to the writer, \see VtkFunction for possible arguments
template <class Function, class... Args>
......
......@@ -74,7 +74,7 @@ void VtkTimeseriesWriter<W>
template <class W>
void VtkTimeseriesWriter<W>
std::string VtkTimeseriesWriter<W>
::write (std::string const& fn, Std::optional<std::string> dir) const
{
assert( initialized_ );
......@@ -96,9 +96,11 @@ void VtkTimeseriesWriter<W>
if (commSize > 1)
serial_fn += "_p" + std::to_string(commRank);
std::string outputFilename;
{ // write serial file
std::ofstream serial_out(serial_fn + "." + vtkWriter_.getFileExtension(),
std::ios_base::ate | std::ios::binary);
outputFilename = serial_fn + "." + vtkWriter_.getFileExtension();
std::ofstream serial_out(outputFilename, std::ios_base::ate | std::ios::binary);
assert(serial_out.is_open());
serial_out.imbue(std::locale::classic());
......@@ -111,8 +113,8 @@ void VtkTimeseriesWriter<W>
if (commSize > 1 && commRank == 0) {
// write parallel file
std::ofstream parallel_out(parallel_fn + ".p" + vtkWriter_.getFileExtension(),
std::ios_base::ate | std::ios::binary);
outputFilename = parallel_fn + ".p" + vtkWriter_.getFileExtension();
std::ofstream parallel_out(outputFilename, std::ios_base::ate | std::ios::binary);
assert(parallel_out.is_open());
parallel_out.imbue(std::locale::classic());
......@@ -122,6 +124,8 @@ void VtkTimeseriesWriter<W>
vtkWriter_.writeTimeseriesParallelFile(parallel_out, rel_fn, commSize, timesteps_);
}
return outputFilename;
}
} // end namespace Dune
......@@ -67,12 +67,7 @@ namespace Dune
, format_(format)
, datatype_(datatype)
{
#if !HAVE_VTK_ZLIB
if (format_ == Vtk::COMPRESSED) {
std::cout << "Dune is compiled without compression. Falling back to BINARY VTK output!\n";
format_ = Vtk::BINARY;
}
#endif
setFormat(format);
}
......@@ -80,8 +75,10 @@ namespace Dune
/**
* \param fn Filename of the VTK file. May contain a directory and any file extension.
* \param dir The optional parameter specifies the directory of the partition files for parallel writes.
*
* \returns File name that is actually written.
**/
virtual void write (std::string const& fn, Std::optional<std::string> dir = {}) const override;
virtual std::string write (std::string const& fn, Std::optional<std::string> dir = {}) const override;
/// \brief Attach point data to the writer
/**
......@@ -112,6 +109,26 @@ namespace Dune
return *this;
}
// Sets the VTK file format
void setFormat (Vtk::FormatTypes format)
{
format_ = format;
#if !HAVE_VTK_ZLIB
if (format_ == Vtk::COMPRESSED) {
std::cout << "Dune is compiled without compression. Falling back to BINARY VTK output!\n";
format_ = Vtk::BINARY;
}
#endif
}
// Sets the global datatype used for coordinates and other global float values
void setDatatype (Vtk::DataTypes datatype)
{
datatype_ = datatype;
}
private:
/// Write a serial VTK file in Unstructured format
virtual void writeSerialFile (std::ofstream& out) const = 0;
......
......@@ -23,7 +23,7 @@
namespace Dune {
template <class GV, class DC>
void VtkWriterInterface<GV,DC>
std::string VtkWriterInterface<GV,DC>
::write (std::string const& fn, Std::optional<std::string> dir) const
{
dataCollector_->update();
......@@ -43,8 +43,11 @@ void VtkWriterInterface<GV,DC>
if (comm().size() > 1)
serial_fn += "_p" + std::to_string(comm().rank());
std::string outputFilename;
{ // write serial file
std::ofstream serial_out(serial_fn + "." + fileExtension(), std::ios_base::ate | std::ios::binary);
outputFilename = serial_fn + "." + fileExtension();
std::ofstream serial_out(outputFilename, std::ios_base::ate | std::ios::binary);
assert(serial_out.is_open());
serial_out.imbue(std::locale::classic());
......@@ -56,8 +59,9 @@ void VtkWriterInterface<GV,DC>
}
if (comm().size() > 1 && comm().rank() == 0) {
// write parallel file
std::ofstream parallel_out(parallel_fn + ".p" + fileExtension(), std::ios_base::ate | std::ios::binary);
// write parallel filee
outputFilename = parallel_fn + ".p" + fileExtension();
std::ofstream parallel_out(outputFilename, std::ios_base::ate | std::ios::binary);
assert(parallel_out.is_open());
parallel_out.imbue(std::locale::classic());
......@@ -67,6 +71,8 @@ void VtkWriterInterface<GV,DC>
writeParallelFile(parallel_out, rel_fn, comm().size());
}
return outputFilename;
}
......
......@@ -9,3 +9,5 @@ install(FILES
vtkunstructuredgridwriter.hh
vtkunstructuredgridwriter.impl.hh
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/dune/vtkwriter/writers)
add_subdirectory(test)
\ No newline at end of file
dune_add_test(SOURCES test-vtkwriter.cc
LINK_LIBRARIES dunevtk)
\ No newline at end of file
#ifndef DUNE_VTK_TEST_CHECKVTKFILE_HH
#define DUNE_VTK_TEST_CHECKVTKFILE_HH
#include <cstddef>
#include <cstdlib>
#include <iostream>
#include <iomanip>
#include <ios>
#include <iostream>
#include <ostream>
#include <set>
#include <sstream>
#include <string>
#include <sys/wait.h>
#include <dune/common/exceptions.hh>
#include <dune/common/test/testsuite.hh>
namespace Dune {
namespace Impl {
// quote so the result can be used inside '...' in python
// quotes not included in the result
inline std::string pyq(const std::string &s)
{
std::ostringstream result;
for(std::size_t i = 0; i < s.size(); ++i)
{
char c = s[i];
switch(c) {
case '\'': result << "\\'"; break;
case '\\': result << "\\\\"; break;
case '\n': result << "\\n"; break;
default:
if(c < 32 || c >= 127)
result << "\\x" << std::hex << std::setfill('0') << std::setw(2)
<< static_cast<int>(c);
else
result << c;
}
}
return result.str();
}
// quote so the result can be used inside '...' in the bourne shell
// quotes not included in the result
inline std::string shq(const std::string &s)
{
std::ostringstream result;
bool pend = false;
for(std::size_t i = 0; i < s.size(); ++i)
{
char c = s[i];
switch(c) {
case '\0': DUNE_THROW(Dune::NotImplemented,
"Can't pass \\0 through the shell");
case '\'': result << (pend ? "" : "'") << "\\'"; pend = true; break;
default: result << (pend ? "'" : "") << c; pend = false; break;
}
}
if(pend) result << "'";
return result.str();
}
inline int runShell(const std::string &code)
{
int result = std::system(code.c_str());
// Avoid returning anything that is a multiple of 256, unless the return
// value was 0. This way the return value can be directly used as an
// argument to exit(), which usually interprets its argument modulo 256.
if(WIFEXITED(result))
return WEXITSTATUS(result);
if(WIFSIGNALED(result))
return WTERMSIG(result) + 256;
else
return 513;
}
inline int runPython(const std::string &code)
{
return runShell("python -c '"+shq(code)+"'");
}
inline bool is_suffix(const std::string &s, const std::string &suffix)
{
return s.size() >= suffix.size() &&
s.compare(s.size() - suffix.size(), suffix.size(), suffix) == 0;
}
inline bool havePythonVTK()
{
static const bool result = [] {
// This check is invoked only once, even in a multithreading environment,
// since it is invoked in the initializer of a static variable.
if(runPython("from vtk import *") == 0)
return true;
std::cerr << "warning: python or python vtk module not available. This "
<< "will result in skipped tests, since we cannot check that "
<< "vtk can read the files we wrote." << std::endl;
return false;
} ();
return result;
}
inline std::string pythonVTKReader(const std::string& filename)
{
if (is_suffix(filename, ".vtu")) return "vtkXMLUnstructuredGridReader";
else if(is_suffix(filename, ".pvtu")) return "vtkXMLPUnstructuredGridReader";
else if(is_suffix(filename, ".vts")) return "vtkXMLStructuredGridReader";
else if(is_suffix(filename, ".pvts")) return "vtkXMLPStructuredGridReader";
else if(is_suffix(filename, ".vtr")) return "vtkXMLRectilinearGridReader";
else if(is_suffix(filename, ".pvtr")) return "vtkXMLPRectilinearGridReader";
else if(is_suffix(filename, ".vti")) return "vtkXMLImageDataReader";
else if(is_suffix(filename, ".pvti")) return "vtkXMLPImageDataReader";
else DUNE_THROW(Dune::NotImplemented,
"Unknown vtk file extension: " << filename);
}
} /* namespace Impl */
class VTKChecker
{
public:
void push(const std::string& file)
{
auto res = files_.insert(file);
if (not res.second) {
testSuite_.check(false, "VTKChecker")
<< "'" << file << "' was added multiple times";
}
}
int check()
{
if (not Impl::havePythonVTK()) {
return 77;
}
else if (not files_.empty()) {
const int result = Impl::runPython(generatePythonCode());
testSuite_.check(result == 0);
}
return testSuite_.exit();
}
const TestSuite& testSuite() const
{
return testSuite_;
}
private:
std::string generatePythonCode() const
{
std::stringstream code;
code << "from vtk import *\n"
<< "import sys\n"
<< "passed = True\n";
for (const auto& file : files_) {
code << "reader = " << Impl::pythonVTKReader(file) << "()\n"
<< "reader.SetFileName('" << Impl::pyq(file) << "')\n"
<< "reader.Update()\n"
<< "if (not (reader.GetOutput().GetNumberOfCells() > 0)):\n"
<< " print('ERROR in {}'.format('" << Impl::pyq(file) << "'))\n"
<< " passed = False\n";
}
code << "sys.exit(0 if passed else 1)\n";
return code.str();
}
std::set< std::string > files_;
TestSuite testSuite_;
};
} /* namespace Dune */
#endif // DUNE_VTK_TEST_CHECKVTKFILE_HH
// -*- tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*-
// vi: set et ts=4 sw=2 sts=2:
#if HAVE_CONFIG_H
#include "config.h" // autoconf defines, needed by the dune headers
#endif
#include <algorithm>
#include <iostream>
#include <ostream>
#include <sstream>
#include <string>
#include <vector>
#include <dune/common/fvector.hh>
#include <dune/common/parallel/mpihelper.hh>
#include <dune/functions/gridfunctions/analyticgridviewfunction.hh>
#include <dune/grid/yaspgrid.hh>
#if HAVE_UG
#include <dune/grid/uggrid.hh>
#endif
#include <dune/vtk/vtkwriter.hh>
#include "checkvtkfile.hh"
// accumulate exit status
void acc (int &accresult, int result)
{
if (accresult == 0 || (accresult == 77 && result != 0))
accresult = result;
}
struct Acc
{
int operator() (int v1, int v2) const
{
acc(v1, v2);
return v1;
}
};
template <class GridView>
int testGridView (std::string prefix, Dune::VTKChecker& vtkChecker, GridView const& gridView)
{
enum { dim = GridView :: dimension };
Dune::VtkWriter<GridView> vtk(gridView);
auto f1 = Dune::Functions::makeAnalyticGridViewFunction([](const auto& x) { return std::sin(x.two_norm()); },gridView);
vtk.addCellData(f1, "scalar-valued lambda");
auto f2 = Dune::Functions::makeAnalyticGridViewFunction([](const auto& x) { return x; },gridView);
vtk.addPointData(f2, "vector-valued lambda");
int result = 0;
// ASCII files
{
vtk.setFormat(Dune::Vtk::ASCII);
std::string name = vtk.write(prefix + "_ascii");
if (gridView.comm().rank() == 0) vtkChecker.push(name);
}
// BINARY files
{
vtk.setFormat(Dune::Vtk::BINARY);
std::string name = vtk.write(prefix + "_binary");
if (gridView.comm().rank() == 0) vtkChecker.push(name);
}
// COMPRESSED files
{
vtk.setFormat(Dune::Vtk::COMPRESSED);
std::string name = vtk.write(prefix + "_compressed");
if (gridView.comm().rank() == 0) vtkChecker.push(name);
}
return result;
}
template <class Grid>
int testGrid (std::string prefix, Dune::VTKChecker& vtkChecker, Grid& grid)
{
if (grid.comm().rank() == 0)
std::cout << "vtkCheck(" << prefix << ")" << std::endl;
grid.globalRefine(1);
int result = 0;
acc(result, testGridView( prefix + "_leaf", vtkChecker, grid.leafGridView() ));
acc(result, testGridView( prefix + "_level0", vtkChecker, grid.levelGridView(0) ));
acc(result, testGridView( prefix + "_level1", vtkChecker, grid.levelGridView(grid.maxLevel()) ));
return result;
}
int main (int argc, char** argv)
{
using namespace Dune;
const MPIHelper& mpiHelper = MPIHelper::instance(argc, argv);
if (mpiHelper.rank() == 0)
std::cout << "vtktest: MPI_Comm_size == " << mpiHelper.size() << std::endl;
int result = 0; // pass by default
VTKChecker vtkChecker;
YaspGrid<1> yaspGrid1({1.0}, {8});
YaspGrid<2> yaspGrid2({1.0,2.0}, {8,4});
YaspGrid<3> yaspGrid3({1.0,2.0,3.0}, {8,4,4});
acc(result, testGrid("yaspgrid_1d", vtkChecker, yaspGrid1));
acc(result, testGrid("yaspgrid_2d", vtkChecker, yaspGrid2));
acc(result, testGrid("yaspgrid_3d", vtkChecker, yaspGrid3));
#if HAVE_UG
auto ugGrid2a = StructuredGridFactory<UGGrid<2>>::createSimplexGrid({0.0,0.0}, {1.0,2.0}, {2u,4u});
auto ugGrid3a = StructuredGridFactory<UGGrid<3>>::createSimplexGrid({0.0,0.0,0.0}, {1.0,2.0,3.0}, {2u,4u,6u});
acc(result, testGrid("uggrid_simplex_2d", vtkChecker, *ugGrid2a));
acc(result, testGrid("uggrid_simplex_3d", vtkChecker, *ugGrid3a));
auto ugGrid2b = StructuredGridFactory<UGGrid<2>>::createCubeGrid({0.0,0.0}, {1.0,2.0}, {2u,4u});
auto ugGrid3b = StructuredGridFactory<UGGrid<3>>::createCubeGrid({0.0,0.0,0.0}, {1.0,2.0,3.0}, {2u,4u,6u});
acc(result, testGrid("uggrid_cube_2d", vtkChecker, *ugGrid2b));
acc(result, testGrid("uggrid_cube_3d", vtkChecker, *ugGrid3b));
#endif
acc(result, vtkChecker.check());
mpiHelper.getCollectiveCommunication().allreduce<Acc>(&result, 1);
return result;
}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment