Skip to content
Snippets Groups Projects
Commit 24b9b299 authored by Praetorius, Simon's avatar Praetorius, Simon
Browse files

Filewriter abstraction

parent 02a175fc
No related branches found
No related tags found
No related merge requests found
#pragma once
#include <string>
#include <memory>
#include <utility>
#include <dune/grid/common/backuprestore.hh>
#include <dune/grid/common/capabilities.hh>
#include <amdis/BackupRestore.hpp>
#include <amdis/Initfile.hpp>
#include <amdis/Output.hpp>
#include <amdis/common/Filesystem.hpp>
#include <amdis/io/FileWriterBase.hpp>
namespace AMDiS
{
template <class SystemVector>
class BackupWriter
: public FileWriterBase
{
using GlobalBasis = typename SystemVector::GlobalBasis;
using GridView = typename GlobalBasis::GridView;
using Grid = typename GridView::Grid;
public:
/// Constructor.
BackupWriter(std::string const& name, std::shared_ptr<SystemVector> systemVector)
: FileWriterBase(name)
, systemVector_(std::move(systemVector))
{
Parameters::get(name + "->animation", animation_);
}
/// Implements \ref FileWriterBase::write
void write(AdaptInfo& adaptInfo, bool force) override
{
std::string filename = filesystem::path({this->dir(), this->filename()}).string();
if (animation_)
filename += "_" + std::to_string(adaptInfo.timestepNumber());
if (force || this->doWrite(adaptInfo)) {
GridView const& gridView = systemVector_->basis()->gridView();
backupGrid(gridView, filename + ".grid",
bool_t<Dune::Capabilities::hasBackupRestoreFacilities<Grid>::v>{});
systemVector_->backup(filename + ".solution");
msg("Backup written to files '{}' and '{}'.", filename + ".grid", filename + ".solution");
}
}
private:
template <class G = Grid,
class = decltype(Dune::BackupRestoreFacility<G>::backup(std::declval<G>(), std::string("")))>
void backupGrid(GridView const& gv, std::string filename, std::true_type)
{
Dune::BackupRestoreFacility<Grid>::backup(gv.grid(), filename);
}
template <bool B, class G = Grid, class GV = GridView,
class = decltype(BackupRestoreByGridFactory<G>::backup(std::declval<GV>(), std::string("")))>
void backupGrid(GridView const& gv, std::string filename, bool_t<B>)
{
warning("Falling back to backup of gridview.");
BackupRestoreByGridFactory<Grid>::backup(gv, filename);
}
private:
std::shared_ptr<SystemVector> systemVector_;
/// append timestepnumber if animation=true, otherwise always override
bool animation_ = false;
};
} // end namespace AMDiS
dune_library_add_sources(amdis SOURCES
FileWriterBase.cpp
)
install(FILES install(FILES
BackupWriter.hpp
DuneVtkWriter.hpp
FileWriterBase.hpp
FileWriterCreator.hpp
GmshWriter.hpp
VTKSequenceWriter.hpp VTKSequenceWriter.hpp
VTKWriter.hpp
DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amdis/io) DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/amdis/io)
#pragma once
#include <string>
#include <memory>
#if HAVE_DUNE_VTK
#include <dune/vtk/pvdwriter.hh>
#include <dune/vtk/vtkwriter.hh>
#include <amdis/Initfile.hpp>
#include <amdis/common/Filesystem.hpp>
#include <amdis/io/FileWriterBase.hpp>
namespace AMDiS
{
/// Adapter for the dune-vtk writer
template <class GB, class VT, class TP>
class DuneVtkWriter
: public FileWriterBase
{
using GridView = typename GB::GridView;
using Writer = Dune::VtkWriter<GridView>;
using SeqWriter = Dune::PvdWriter<Writer>;
public:
/// Constructor.
DuneVtkWriter(std::string const& name, DiscreteFunction<GB,VT,TP> const& discreteFct)
: FileWriterBase(name)
, discreteFct_(discreteFct)
{
int m = 0, p = 0;
Parameters::get(name + "->animation", animation_);
Parameters::get(name + "->mode", m);
Parameters::get(name + "->precision", p);
Dune::Vtk::FormatTypes mode =
m == 0 ? Dune::Vtk::ASCII :
m == 1 ? Dune::Vtk::BINARY :
Dune::Vtk::COMPRESSED;
Dune::Vtk::DataTypes precision =
p == 0 ? Dune::Vtk::FLOAT32 :
Dune::Vtk::FLOAT64;
if (animation_) {
vtkSeqWriter_ = std::make_shared<SeqWriter>(gridView(), mode, precision);
vtkSeqWriter_->addPointData(discreteFct_, this->name());
} else {
vtkWriter_ = std::make_shared<Writer>(gridView(), mode, precision);
vtkWriter_->addPointData(discreteFct_, this->name());
}
}
/// Implements \ref FileWriterBase::write
void write(AdaptInfo& adaptInfo, bool force) override
{
std::string data_dir = this->dir() + "/_piecefiles";
filesystem::create_directories(data_dir);
std::string filename = filesystem::path({this->dir(), this->filename() + ".vtk"}).string();
if (force || this->doWrite(adaptInfo)) {
if (animation_)
vtkSeqWriter_->writeTimestep(adaptInfo.time(), filename, data_dir, true);
else
vtkWriter_->write(filename, data_dir);
}
}
private:
/// The Gridview this writer lives on. Given by the discrete function.
GridView const& gridView() const
{
return discreteFct_.basis()->gridView();
}
private:
DiscreteFunction<GB,VT,TP> discreteFct_;
std::shared_ptr<Writer> vtkWriter_;
std::shared_ptr<SeqWriter> vtkSeqWriter_;
/// write .pvd if animation=true, otherwise write only .vtu
bool animation_ = false;
};
} // end namespace AMDiS
#endif // HAVE_DUNE_VTK
#include "FileWriterBase.hpp"
#include <amdis/AdaptInfo.hpp>
#include <amdis/Initfile.hpp>
#include <amdis/common/Filesystem.hpp>
namespace AMDiS
{
FileWriterBase::FileWriterBase(std::string const& base)
{
Parameters::get(base + "->filename", filename_);
Parameters::get(base + "->output directory", dir_);
Parameters::get(base + "->name", name_);
Parameters::get(base + "->write every i-th timestep", tsModulo_);
Parameters::get(base + "->write after timestep", timeModulo_);
test_exit(dir_ == "." || filesystem::exists(dir_), "Output directory '{}' does not exist!", dir_);
}
bool FileWriterBase::doWrite(AdaptInfo& adaptInfo) const
{
if (timeModulo_ > 0.0 &&
lastWriteTime_ > adaptInfo.startTime() &&
lastWriteTime_ + timeModulo_ > adaptInfo.time() + 0.5*adaptInfo.minTimestep())
return false;
else if (tsModulo_ > 0 && adaptInfo.timestepNumber() % tsModulo_ != 0)
return false;
lastWriteTime_ = adaptInfo.time();
return true;
}
} // end namespace AMDiS
#pragma once
#include <limits>
#include <string>
namespace AMDiS
{
// forward declaration
class AdaptInfo;
/// Interface class for filewriters
class FileWriterInterface
{
public:
/// Virtual destructor
virtual ~FileWriterInterface() = default;
/// Pure virtual method to be implemented by derived classes
virtual void write(AdaptInfo& adaptInfo, bool force) = 0;
};
/// \brief Base class for filewriters
/**
* Read parameters common for all filewriters, where `BASE` is given by the
* constructor parameter `base`:
* - `[BASE]->filename`: Base name of the filename, not including the file extension
* - `[BASE]->output directory`: Directory where to put the files
* - `[BASE]->name`: Name of the data vector in the output file
* - `[BASE]->write every i-th timestep`: Timestep number interval.
* - `[BASE]->write after timestep`: Time interval.
*
* Some other parameters found in some filewriter implementations:
* - `[BASE]->mode`: File mode, either ASCII=0, BINARY=1, or COMPRESSED=2
* - `[BASE]->precision`: Output precision, either FLOAT=0, DOUBLE=1
* - `[BASE]->animation`: Write animation files, or append the timestep to the filename.
**/
class FileWriterBase
: public FileWriterInterface
{
public:
/// Constructor. Reads common parameters.
FileWriterBase(std::string const& base);
/// Return whether to write the current timestep or not
bool doWrite(AdaptInfo& adaptInfo) const;
public:
std::string const& filename() const
{
return filename_;
}
std::string const& dir() const
{
return dir_;
}
std::string const& name() const
{
return name_;
}
protected:
/// Base part of output filename
std::string filename_ = "solution";
/// Output directory
std::string dir_ = ".";
/// Name of the data
std::string name_ = "solution";
/// Write every i'th timestep
int tsModulo_ = 0;
/// Write after every time interval
double timeModulo_ = 0.0;
private:
// time counter for the interval output
mutable double lastWriteTime_ = std::numeric_limits<double>::lowest();
};
} // end namespace AMDiS
#pragma once
#include <memory>
#include <string>
#include <amdis/BoundaryManager.hpp>
#include <amdis/LinearAlgebra.hpp>
#include <amdis/Output.hpp>
#include <amdis/gridfunctions/DiscreteFunction.hpp>
#include <amdis/io/BackupWriter.hpp>
#include <amdis/io/DuneVtkWriter.hpp>
#include <amdis/io/FileWriterBase.hpp>
#include <amdis/io/GmshWriter.hpp>
#include <amdis/io/VTKWriter.hpp>
namespace AMDiS
{
/// \brief Creator class for filewriters depending on a given type name
/**
* \tparam SystemVector Type of the DOFVector to write with the filewriter
**/
template <class SystemVector>
class FileWriterCreator
{
using GlobalBasis = typename SystemVector::GlobalBasis;
using GridView = typename GlobalBasis::GridView;
using Grid = typename GridView::Grid;
public:
/// Constructor. Stores the pointer to the systemVector and to the (optional) boundaryManager.
FileWriterCreator(std::shared_ptr<SystemVector> systemVector,
std::shared_ptr<BoundaryManager<Grid>> boundaryManager = nullptr)
: systemVector_(std::move(systemVector))
, boundaryManager_(std::move(boundaryManager))
{}
/// Create a new FileWriter of type `type`
/**
* \param type String representing the type of the writer. One of {vtk, dune-vtk, gmsh, backup}
* \param prefix The initfile prefix tp configure the filewriter
* \param treePath Treepath to the component of the systemVector to be handled by the fileWriter.
**/
template <class TreePath = RootTreePath>
std::unique_ptr<FileWriterInterface>
create(std::string type, std::string prefix, TreePath treePath = {}) const
{
auto data = makeDiscreteFunction(*systemVector_, treePath);
using Range = typename TYPEOF(data)::Range;
return create_impl(std::move(type), std::move(prefix), data, ValueCategory_t<Range>{});
}
private:
template <class GB, class VT, class TP, class ValCat>
std::unique_ptr<FileWriterInterface>
create_impl(std::string type, std::string prefix, DiscreteFunction<GB,VT,TP> const& data, ValCat) const
{
// ParaView VTK format, writer from dune-grid
if (type == "vtk")
{
return std::make_unique<VTKWriter<GB,VT,TP>>(prefix, data);
}
#if HAVE_DUNE_VTK
// ParaView VTK format, writer from dune-vtk
else if (type == "dune-vtk")
{
return std::make_unique<DuneVtkWriter<GB,VT,TP>>(prefix, data);
}
#endif
// GMsh file format, writing just the grid and optionally boundary ids
else if (type == "gmsh")
{
GridView const& gridView = systemVector_->basis()->gridView();
if (!!boundaryManager_)
return std::make_unique<GmshWriter<typename GB::GridView>>(prefix, gridView,
std::vector<int>{}, boundaryManager_->boundaryIds());
else
return std::make_unique<GmshWriter<typename GB::GridView>>(prefix, gridView);
}
// Backup writer, writing the grid and the solution vector
else if (type == "backup")
{
return std::make_unique<BackupWriter<SystemVector>>(prefix, systemVector_);
}
error_exit("Unknown filewriter type '{}' given for '{}'. Use one of "
"(vtk, gmsh, backup, [dune-vtk]), where the last one is only available "
"if the dune module dune-vtk is found.", type, prefix);
return {};
}
// The value-category is unknown, like a composite/hierarchic vector or any unknown type.
template <class GB, class VT, class TP>
std::unique_ptr<FileWriterInterface>
create_impl(std::string type, std::string prefix, DiscreteFunction<GB,VT,TP> const& /*data*/, tag::unknown) const
{
// Backup writer, writing the grid and the solution vector
if (type == "backup")
{
return std::make_unique<BackupWriter<SystemVector>>(prefix, systemVector_);
}
error_exit("Filewriter '{}' cannot be applied to this component in the tree. "
"Either use the writer 'backup' to write the whole tree, or add a treepath "
"to the output prefix '{}'.", type, prefix);
return {};
}
private:
std::shared_ptr<SystemVector> systemVector_;
std::shared_ptr<BoundaryManager<Grid>> boundaryManager_ = nullptr;
};
} // end namespace AMDiS
#pragma once
#include <string>
#include <limits>
#include <memory>
#include <utility>
#include <dune/grid/io/file/gmshwriter.hh>
#include <amdis/Initfile.hpp>
#include <amdis/Output.hpp>
#include <amdis/common/Filesystem.hpp>
#include <amdis/io/FileWriterBase.hpp>
namespace AMDiS
{
/// The GmshWriter just writes the grid of a given gridView to a gmsh compatible .msh file
template <class GV>
class GmshWriter
: public FileWriterBase
{
using GridView = GV;
using Writer = Dune::GmshWriter<GridView>;
public:
/// Constructor.
GmshWriter(std::string const& name, GridView const& gridView,
std::vector<int> const& physicalEntities = std::vector<int>(),
std::vector<int> const& physicalBoundaries = std::vector<int>())
: FileWriterBase(name)
, gridView_(gridView)
, physicalEntities_(physicalEntities)
, physicalBoundaries_(physicalBoundaries)
{
int p = 0;
Parameters::get(name + "->animation", animation_);
Parameters::get(name + "->precision", p);
int precision = (p == 0)
? std::numeric_limits<float>::max_digits10
: std::numeric_limits<double>::max_digits10;
writer_ = std::make_shared<Writer>(gridView_, precision);
}
/// Implements \ref FileWriterBase::write
void write(AdaptInfo& adaptInfo, bool force) override
{
std::string filename = this->filename();
if (animation_)
filename += "_" + std::to_string(adaptInfo.time());
if (force || this->doWrite(adaptInfo)) {
writer_->write(filesystem::path({this->dir(), filename + ".msh"}).string(),
physicalEntities_, physicalBoundaries_);
msg("Grid written to file '{}'.", filesystem::path({this->dir(), filename + ".msh"}).string());
}
}
private:
GridView gridView_;
std::vector<int> const& physicalEntities_;
std::vector<int> const& physicalBoundaries_;
std::shared_ptr<Writer> writer_;
/// write .pvd if animation=true, otherwise write only .vtu
bool animation_ = false;
};
} // end namespace AMDiS
...@@ -12,6 +12,8 @@ ...@@ -12,6 +12,8 @@
#include <dune/grid/io/file/vtk/vtkwriter.hh> #include <dune/grid/io/file/vtk/vtkwriter.hh>
#include <amdis/Environment.hpp>
namespace AMDiS namespace AMDiS
{ {
/// \brief class to write pvd-files which contains a list of all collected vtk-files /// \brief class to write pvd-files which contains a list of all collected vtk-files
......
...@@ -3,17 +3,14 @@ ...@@ -3,17 +3,14 @@
#include <string> #include <string>
#include <memory> #include <memory>
#include <dune/functions/functionspacebases/lagrangebasis.hh>
#include <dune/grid/io/file/vtk/vtkwriter.hh>
#include <dune/grid/io/file/vtk/subsamplingvtkwriter.hh> #include <dune/grid/io/file/vtk/subsamplingvtkwriter.hh>
#include <dune/typetree/childextraction.hh> #include <dune/grid/io/file/vtk/vtkwriter.hh>
#include <amdis/FileWriterInterface.hpp>
#include <amdis/Initfile.hpp> #include <amdis/Initfile.hpp>
#include <amdis/common/Filesystem.hpp> #include <amdis/common/Filesystem.hpp>
#include <amdis/common/StaticSize.hpp> #include <amdis/common/StaticSize.hpp>
#include <amdis/common/ValueCategory.hpp> #include <amdis/common/ValueCategory.hpp>
#include <amdis/gridfunctions/DiscreteFunction.hpp> #include <amdis/io/FileWriterBase.hpp>
#include <amdis/io/VTKSequenceWriter.hpp> #include <amdis/io/VTKSequenceWriter.hpp>
namespace AMDiS namespace AMDiS
...@@ -36,76 +33,70 @@ namespace AMDiS ...@@ -36,76 +33,70 @@ namespace AMDiS
} // end namespace Impl } // end namespace Impl
template <class Range>
constexpr Dune::VTK::FieldInfo::Type VTKFieldType = Impl::VTKFieldTypeImpl<ValueCategory_t<Range>>::value;
template <class Range>
constexpr std::size_t VTKFieldSize = Size_v<Range>;
/// Adapter for the dune-grid VTKWriter
template <class GB, class VT, class TP> template <class GB, class VT, class TP>
class FileWriter class VTKWriter
: public FileWriterInterface : public FileWriterBase
{ {
using GlobalBasis = GB; using GridView = typename GB::GridView;
using ValueType = VT; using Writer = Dune::VTKWriter<GridView>;
using TreePath = TP; using SeqWriter = VTKSequenceWriter<GridView>;
private:
using GridView = typename GlobalBasis::GridView;
using Range = typename DiscreteFunction<GB,VT,TP>::Range; using Range = typename DiscreteFunction<GB,VT,TP>::Range;
/// Dimension of the mesh template <class R>
static constexpr int dim = GridView::dimension; static constexpr Dune::VTK::FieldInfo::Type VTKFieldType() {
return Impl::VTKFieldTypeImpl<ValueCategory_t<R>>::value;
}
/// Dimension of the world template <class R>
static constexpr int dow = GridView::dimensionworld; static constexpr std::size_t VTKFieldSize() {
return Size_v<R>;
}
public: public:
/// Constructor. /// Constructor.
FileWriter(std::string const& name, DiscreteFunction<GB,VT,TP> const& discreteFct) VTKWriter(std::string const& name, DiscreteFunction<GB,VT,TP> const& discreteFct)
: FileWriterInterface(name) : FileWriterBase(name)
, discreteFct_(discreteFct) , discreteFct_(discreteFct)
, animation_(false)
{ {
Parameters::get(name + "->ParaView animation", animation_); int m = 0;
Parameters::get(name + "->animation", animation_);
int m = Parameters::get<int>(name + "->ParaView mode").value_or(0); Parameters::get(name + "->mode", m);
mode_ = (m == 0 ? Dune::VTK::ascii : Dune::VTK::appendedraw);
mode_ = (m == 0)
init(name, ValueCategory_t<Range>{}); ? Dune::VTK::ascii
} : Dune::VTK::appendedraw;
template <class ValueCat> auto subSampling = Parameters::get<int>(name + "->subsampling");
void init(std::string const& baseName, ValueCat) if (subSampling) {
{ using SubsamplingWriter = Dune::SubsamplingVTKWriter<GridView>;
auto subSampling = Parameters::get<int>(baseName + "->subsampling"); vtkWriter_ = std::make_shared<SubsamplingWriter>(gridView(), subSampling.value());
if (subSampling) } else {
vtkWriter_ = std::make_shared<Dune::SubsamplingVTKWriter<GridView>>(gridView(), subSampling.value()); vtkWriter_ = std::make_shared<Writer>(gridView());
else }
vtkWriter_ = std::make_shared<Dune::VTKWriter<GridView>>(gridView());
if (animation_) if (animation_)
vtkSeqWriter_ = std::make_shared<VTKSequenceWriter<GridView>>(vtkWriter_); vtkSeqWriter_ = std::make_shared<SeqWriter>(vtkWriter_);
vtkWriter_->addVertexData(discreteFct_, Dune::VTK::FieldInfo(name_, VTKFieldType<Range>, VTKFieldSize<Range>));
test_exit(dir_ == "." || filesystem::exists(dir_), "Output directory '{}' does not exist!",dir_); vtkWriter_->addVertexData(discreteFct_,
Dune::VTK::FieldInfo(name_, VTKFieldType<Range>(), VTKFieldSize<Range>()));
} }
void init(std::string const&, tag::unknown) {} /// Implements \ref FileWriterInterface::write
void write(AdaptInfo& adaptInfo, bool force) override
/// Implements \ref FileWriterInterface::writeFiles
void writeFiles(AdaptInfo& adaptInfo, bool force) override
{ {
filesystem::create_directories(dir_ + "/_piecefiles"); filesystem::create_directories(this->dir() + "/_piecefiles");
if (vtkSeqWriter_) if (force || this->doWrite(adaptInfo)) {
vtkSeqWriter_->pwrite(adaptInfo.time(), filename_, dir_, "_piecefiles", mode_); if (animation_)
else if (vtkWriter_) vtkSeqWriter_->pwrite(adaptInfo.time(), this->filename(), this->dir(), "_piecefiles", mode_);
vtkWriter_->pwrite(filename_, dir_, "_piecefiles", mode_); else
vtkWriter_->pwrite(this->filename(), this->dir(), "_piecefiles", mode_);
}
} }
protected: private:
/// The Gridview this writer lives on. Given by the discrete function.
GridView const& gridView() const GridView const& gridView() const
{ {
return discreteFct_.basis()->gridView(); return discreteFct_.basis()->gridView();
...@@ -113,29 +104,14 @@ namespace AMDiS ...@@ -113,29 +104,14 @@ namespace AMDiS
private: private:
DiscreteFunction<GB,VT,TP> discreteFct_; DiscreteFunction<GB,VT,TP> discreteFct_;
std::shared_ptr<Writer> vtkWriter_;
std::shared_ptr<Dune::VTKWriter<GridView>> vtkWriter_; std::shared_ptr<SeqWriter> vtkSeqWriter_;
std::shared_ptr<VTKSequenceWriter<GridView>> vtkSeqWriter_;
// write .pvd if animation=true, otherwise write only .vtu // write .pvd if animation=true, otherwise write only .vtu
bool animation_; bool animation_ = false;
// represents VTK::OutputType: ascii, appendedraw // represents VTK::OutputType: ascii, appendedraw
Dune::VTK::OutputType mode_; Dune::VTK::OutputType mode_ = Dune::VTK::ascii;
}; };
template <class GlobalBasis, class ValueType, class TreePath>
auto makeFileWriter(std::string const& name,
DiscreteFunction<GlobalBasis,ValueType,TreePath> const& discreteFct)
{
return FileWriter<GlobalBasis,ValueType,TreePath>{name, discreteFct};
}
template <class GlobalBasis, class ValueType, class TreePath>
auto makeFileWriterPtr(std::string const& name,
DiscreteFunction<GlobalBasis,ValueType,TreePath> const& discreteFct)
{
return std::make_unique<FileWriter<GlobalBasis,ValueType,TreePath>>(name, discreteFct);
}
} // end namespace AMDiS } // end namespace AMDiS
...@@ -47,7 +47,7 @@ void test(Factory factory) ...@@ -47,7 +47,7 @@ void test(Factory factory)
num_vertices = prob.grid()->size(Grid::dimension); num_vertices = prob.grid()->size(Grid::dimension);
AdaptInfo adaptInfo("adapt"); AdaptInfo adaptInfo("adapt");
prob.backup(adaptInfo); prob.writeFiles(adaptInfo, true);
} }
// restore // restore
...@@ -97,11 +97,12 @@ int main(int argc, char** argv) ...@@ -97,11 +97,12 @@ int main(int argc, char** argv)
Environment env(argc, argv); Environment env(argc, argv);
std::string filename = "test"; std::string filename = "test";
Parameters::set("test->backup->grid", filename + "_" + std::to_string(GRID_ID) + ".grid");
Parameters::set("test->restore->grid", filename + "_" + std::to_string(GRID_ID) + ".grid"); Parameters::set("test->restore->grid", filename + "_" + std::to_string(GRID_ID) + ".grid");
Parameters::set("test->backup->solution", filename + "_" + std::to_string(GRID_ID) + ".solution");
Parameters::set("test->restore->solution", filename + "_" + std::to_string(GRID_ID) + ".solution"); Parameters::set("test->restore->solution", filename + "_" + std::to_string(GRID_ID) + ".solution");
Parameters::set("test->output->format", "backup");
Parameters::set("test->output->filename", filename + "_" + std::to_string(GRID_ID));
#if GRID_ID == 0 #if GRID_ID == 0
test_cube<Dune::YaspGrid<2>>(); test_cube<Dune::YaspGrid<2>>();
test_cube<Dune::YaspGrid<3>>(); test_cube<Dune::YaspGrid<3>>();
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment