Commit effeef7d authored by Burlen Loring's avatar Burlen Loring
Browse files

add a VTK writer for AMR data

parent 9a469012
......@@ -320,3 +320,10 @@ SENSEI_DATA_ADAPTOR(ADIOSDataAdaptor)
#ifdef ENABLE_VTK_XMLP
VTK_DERIVED(VTKPosthocIO)
#endif
/****************************************************************************
* VTKAmrWriter
***************************************************************************/
#ifdef ENABLE_VTK_XMLP
VTK_DERIVED(VTKAmrWriter)
#endif
......@@ -32,7 +32,7 @@ if (ENABLE_SENSEI)
endif()
if(ENABLE_VTK_XMLP)
list(APPEND sensei_sources VTKPosthocIO.cxx)
list(APPEND sensei_sources VTKPosthocIO.cxx VTKAmrWriter.cxx)
endif()
add_library(sensei ${sensei_sources})
......
......@@ -5,10 +5,13 @@
#include "DataRequirements.h"
#include "Autocorrelation.h"
#include "VTKPosthocIO.h"
#include "Histogram.h"
#ifdef ENABLE_VTK_XMLP
#include "VTKPosthocIO.h"
#include "VTKAmrWriter.h"
#endif
#ifdef ENABLE_VTK_M
# include "VTKmContourAnalysis.h"
#include "VTKmContourAnalysis.h"
#endif
#ifdef ENABLE_ADIOS
#include "ADIOSAnalysisAdaptor.h"
......@@ -138,6 +141,9 @@ public:
// adds the post hoc I/O analysis
int AddPosthocIO(MPI_Comm comm, pugi::xml_node node);
// adds the VTK AMR writer
int AddVTKAmrWriter(MPI_Comm comm, pugi::xml_node node);
public:
AnalysisAdaptorVector Analyses;
#ifdef ENABLE_LIBSIM
......@@ -533,6 +539,49 @@ int ConfigurableAnalysis::InternalsType::AddPosthocIO(MPI_Comm comm,
#endif
}
// --------------------------------------------------------------------------
int ConfigurableAnalysis::InternalsType::AddVTKAmrWriter(MPI_Comm comm,
pugi::xml_node node)
{
#ifndef ENABLE_VTK_XMLP
(void)comm;
(void)node;
SENSEI_ERROR("VTK AMR Writer was requested but is disabled in this build")
return -1;
#else
DataRequirements req;
if (req.Initialize(node) ||
(req.GetNumberOfRequiredMeshes() < 1))
{
SENSEI_ERROR("Failed to initialize VTKAmrWriter. "
"At least one mesh is required")
return -1;
}
std::string outputDir = node.attribute("output_dir").as_string("./");
std::string fileName = node.attribute("file_name").as_string("data");
std::string mode = node.attribute("mode").as_string("visit");
vtkNew<VTKAmrWriter> adapter;
if (adapter->SetCommunicator(comm) ||
adapter->SetOutputDir(outputDir) ||
adapter->SetMode(mode) ||
adapter->SetDataRequirements(req) ||
adapter->Initialize())
{
SENSEI_ERROR("Failed to initialize the VTKAmrWriter analysis")
return -1;
}
this->Analyses.push_back(adapter.GetPointer());
SENSEI_STATUS("Configured VTKAmrWriter")
return 0;
#endif
}
......@@ -578,6 +627,7 @@ bool ConfigurableAnalysis::Initialize(MPI_Comm comm, const std::string& filename
|| ((type == "catalyst") && !this->Internals->AddCatalyst(comm, node))
|| ((type == "libsim") && !this->Internals->AddLibsim(comm, node))
|| ((type == "PosthocIO") && !this->Internals->AddPosthocIO(comm, node))
|| ((type == "VTKAmrWriter") && !this->Internals->AddVTKAmrWriter(comm, node))
|| ((type == "vtkmcontour") && !this->Internals->AddVTKmContour(comm, node))))
{
if (rank == 0)
......
#include "VTKAmrWriter.h"
#include "senseiConfig.h"
#include "DataAdaptor.h"
#include "VTKUtils.h"
#include "Error.h"
#include <vtkCompositeDataIterator.h>
#include <vtkDataObject.h>
#include <vtkObjectFactory.h>
#include <vtkSmartPointer.h>
#include <vtkOverlappingAMR.h>
#include <vtkCompositeDataPipeline.h>
#include <vtkXMLPUniformGridAMRWriter.h>
#include <vtkAlgorithm.h>
#include <vtkMultiProcessController.h>
#include <vtkMPIController.h>
#include <algorithm>
#include <sstream>
#include <fstream>
#include <cassert>
#include <mpi.h>
static
std::string getFileName(const std::string &outputDir,
const std::string &meshName, unsigned long fileId,
const std::string &blockExt)
{
std::ostringstream fss;
fss << outputDir << "/" << meshName << "_"
<< std::setw(6) << std::setfill('0') << fileId << blockExt;
return fss.str();
}
namespace sensei
{
//-----------------------------------------------------------------------------
senseiNewMacro(VTKAmrWriter);
//-----------------------------------------------------------------------------
VTKAmrWriter::VTKAmrWriter() : Comm(MPI_COMM_WORLD),
OutputDir("./"), Mode(MODE_PARAVIEW)
{}
//-----------------------------------------------------------------------------
VTKAmrWriter::~VTKAmrWriter()
{}
//-----------------------------------------------------------------------------
int VTKAmrWriter::SetCommunicator(MPI_Comm comm)
{
this->Comm = comm;
return 0;
}
//-----------------------------------------------------------------------------
int VTKAmrWriter::Initialize()
{
vtkMPIController *controller=vtkMPIController::New();
controller->Initialize(0,0,1);
vtkMultiProcessController::SetGlobalController(controller);
vtkCompositeDataPipeline* cexec=vtkCompositeDataPipeline::New();
vtkAlgorithm::SetDefaultExecutivePrototype(cexec);
cexec->Delete();
return 0;
}
//-----------------------------------------------------------------------------
int VTKAmrWriter::SetOutputDir(const std::string &outputDir)
{
this->OutputDir = outputDir;
return 0;
}
//-----------------------------------------------------------------------------
int VTKAmrWriter::SetMode(int mode)
{
if (!(mode == VTKAmrWriter::MODE_VISIT) ||
(mode == VTKAmrWriter::MODE_PARAVIEW))
{
SENSEI_ERROR("Invalid mode " << mode)
return -1;
}
this->Mode = mode;
return 0;
}
//-----------------------------------------------------------------------------
int VTKAmrWriter::SetMode(std::string modeStr)
{
unsigned int n = modeStr.size();
for (unsigned int i = 0; i < n; ++i)
modeStr[i] = tolower(modeStr[i]);
int mode = 0;
if (modeStr == "visit")
{
mode = VTKAmrWriter::MODE_VISIT;
}
else if (modeStr == "paraview")
{
mode = VTKAmrWriter::MODE_PARAVIEW;
}
else
{
SENSEI_ERROR("invalid mode \"" << modeStr << "\"")
return -1;
}
this->Mode = mode;
return 0;
}
//-----------------------------------------------------------------------------
int VTKAmrWriter::SetDataRequirements(const DataRequirements &reqs)
{
this->Requirements = reqs;
return 0;
}
//-----------------------------------------------------------------------------
int VTKAmrWriter::AddDataRequirement(const std::string &meshName,
int association, const std::vector<std::string> &arrays)
{
this->Requirements.AddRequirement(meshName, association, arrays);
return 0;
}
//-----------------------------------------------------------------------------
bool VTKAmrWriter::Execute(DataAdaptor* dataAdaptor)
{
int rank = 0;
MPI_Comm_rank(this->Comm, &rank);
// if no dataAdaptor requirements are given, push all the data
// fill in the requirements with every thing
if (this->Requirements.Empty())
{
if (this->Requirements.Initialize(dataAdaptor))
{
SENSEI_ERROR("Failed to initialze dataAdaptor description")
return false;
}
SENSEI_WARNING("No subset specified. Writing all available data")
}
MeshRequirementsIterator mit =
this->Requirements.GetMeshRequirementsIterator();
for (; mit; ++mit)
{
// get the mesh
vtkDataObject* dobj = nullptr;
std::string meshName = mit.MeshName();
if (dataAdaptor->GetMesh(meshName, mit.StructureOnly(), dobj))
{
SENSEI_ERROR("Failed to get mesh \"" << meshName << "\"")
return false;
}
// make sure we have amr dataset
if (!dynamic_cast<vtkOverlappingAMR*>(dobj))
{
SENSEI_ERROR("Data \"" << dobj->GetClassName() << "\" is not an AMR data set")
return false;
}
// get ghost cell/node metadata always provide this information as
// it is essential to process the data objects
int nGhostCellLayers = 0;
int nGhostNodeLayers = 0;
if (dataAdaptor->GetMeshHasGhostCells(mit.MeshName(), nGhostCellLayers) ||
dataAdaptor->GetMeshHasGhostNodes(mit.MeshName(), nGhostNodeLayers))
{
SENSEI_ERROR("Failed to get ghost layer info for mesh \"" << mit.MeshName() << "\"")
return false;
}
// add the ghost cell arrays to the mesh
if ((nGhostCellLayers > 0) && dataAdaptor->AddGhostCellsArray(dobj, mit.MeshName()))
{
SENSEI_ERROR("Failed to get ghost cells for mesh \"" << mit.MeshName() << "\"")
return false;
}
// add the ghost node arrays to the mesh
if ((nGhostNodeLayers > 0) && dataAdaptor->AddGhostNodesArray(dobj, mit.MeshName()))
{
SENSEI_ERROR("Failed to get ghost nodes for mesh \"" << mit.MeshName() << "\"")
return false;
}
// add the required arrays
ArrayRequirementsIterator ait =
this->Requirements.GetArrayRequirementsIterator(meshName);
for (; ait; ++ait)
{
if (dataAdaptor->AddArray(dobj, mit.MeshName(),
ait.Association(), ait.Array()))
{
SENSEI_ERROR("Failed to add "
<< VTKUtils::GetAttributesName(ait.Association())
<< " data array \"" << ait.Array() << "\" to mesh \""
<< meshName << "\"")
return false;
}
}
// initialize file id and time and step records
if (this->HaveBlockInfo.count(meshName) == 0)
{
this->HaveBlockInfo[meshName] = 1;
this->FileId[meshName] = 0;
}
// write to disk
std::string fileName =
getFileName(this->OutputDir, meshName, this->FileId[meshName], ".vth");
vtkXMLPUniformGridAMRWriter *w = vtkXMLPUniformGridAMRWriter::New();
w->SetInputData(dobj);
w->SetFileName(fileName.c_str());
w->Write();
w->Delete();
// update file id
this->FileId[meshName] += 1;
// record time and step info for meta files
if (rank == 0)
{
double time = dataAdaptor->GetDataTime();
this->Time[meshName].push_back(time);
long step = dataAdaptor->GetDataTimeStep();
this->TimeStep[meshName].push_back(step);
}
}
return true;
}
//-----------------------------------------------------------------------------
int VTKAmrWriter::Finalize()
{
int rank = 0;
MPI_Comm_rank(this->Comm, &rank);
// clean up VTK
vtkMultiProcessController *controller =
vtkMultiProcessController::GetGlobalController();
vtkMultiProcessController::SetGlobalController(nullptr);
vtkAlgorithm::SetDefaultExecutivePrototype(nullptr);
controller->Finalize(1);
controller->Delete();
// rank 0 will write meta files
if (rank != 0)
return 0;
std::vector<std::string> meshNames;
this->Requirements.GetRequiredMeshes(meshNames);
unsigned int nMeshes = meshNames.size();
for (unsigned int i = 0; i < nMeshes; ++i)
{
const std::string &meshName = meshNames[i];
if (this->HaveBlockInfo.find(meshName) == this->HaveBlockInfo.end())
{
SENSEI_ERROR("No blocks have been written for a mesh named \""
<< meshName << "\"")
return -1;
}
std::vector<double> &times = this->Time[meshName];
long nSteps = times.size();
if (this->Mode == VTKAmrWriter::MODE_PARAVIEW)
{
std::string pvdFileName = this->OutputDir + "/" + meshName + ".pvd";
ofstream pvdFile(pvdFileName);
if (!pvdFile)
{
SENSEI_ERROR("Failed to open " << pvdFileName << " for writing")
return -1;
}
pvdFile << "<?xml version=\"1.0\"?>" << endl
<< "<VTKFile type=\"Collection\" version=\"0.1\"" << endl
<< " byte_order=\"LittleEndian\" compressor=\"\">" << endl
<< "<Collection>" << endl;
for (long i = 0; i < nSteps; ++i)
{
std::string fileName =
getFileName(this->OutputDir, meshName, i, ".vth");
pvdFile << "<DataSet timestep=\"" << times[i]
<< "\" group=\"\" part=\"\" file=\"" << fileName
<< "\"/>" << endl;
}
pvdFile << "</Collection>" << endl
<< "</VTKFile>" << endl;
}
else if (this->Mode == VTKAmrWriter::MODE_VISIT)
{
std::string visitFileName = this->OutputDir + "/" + meshName + ".visit";
ofstream visitFile(visitFileName);
if (!visitFile)
{
SENSEI_ERROR("Failed to open " << visitFileName << " for writing")
return -1;
}
visitFile << "!NBLOCKS " << 1 << endl;
for (long i = 0; i < nSteps; ++i)
{
visitFile << "!TIME " << times[i] << endl;
}
for (long i = 0; i < nSteps; ++i)
{
std::string fileName =
getFileName(this->OutputDir, meshName, i, ".vth");
visitFile << fileName << endl;
}
}
else
{
SENSEI_ERROR("Invalid mode \"" << this->Mode << "\"")
return -1;
}
}
return 0;
}
}
#ifndef sensei_VTKAmrWriter_h
#define sensei_VTKAmrWriter_h
#include "AnalysisAdaptor.h"
#include "DataRequirements.h"
#include <mpi.h>
#include <vector>
#include <string>
class vtkInformation;
class vtkCompositeDataSet;
namespace sensei
{
/// @class VTKAmrWriter
/// brief sensei::VTKAmrWriter is a AnalysisAdaptor that writes
/// AMR data to disk. This can be useful for generating preview datasets
/// that allow configuration of Catalyst and/or Libsim scripts, or
/// for staging data on resources such as burst buffers. This adaptor
/// supports writing to a VTK(PXML), VisIt(.visit) or ParaView(.pvd)
/// compatible format. One must provide a set of data requirments,
/// consisting of a list of meshes and the arrays to write from
/// each mesh. File names are derived using the output directory,
/// the mesh name, and the mode.
class VTKAmrWriter : public AnalysisAdaptor
{
public:
static VTKAmrWriter* New();
senseiTypeMacro(VTKAmrWriter, AnalysisAdaptor);
// Run time configuration
int SetCommunicator(MPI_Comm comm);
int SetOutputDir(const std::string &outputDir);
enum {MODE_PARAVIEW=0, MODE_VISIT=1};
int SetMode(int mode);
int SetMode(std::string mode);
/// data requirements tell the adaptor what to push
/// if none are given then all data is pushed.
int SetDataRequirements(const DataRequirements &reqs);
int AddDataRequirement(const std::string &meshName,
int association, const std::vector<std::string> &arrays);
int Initialize();
// SENSEI API
bool Execute(DataAdaptor* data) override;
int Finalize() override;
protected:
VTKAmrWriter();
~VTKAmrWriter();
private:
MPI_Comm Comm;
std::string OutputDir;
DataRequirements Requirements;
int Mode;
template<typename T>
using NameMap = std::map<std::string, T>;
NameMap<std::vector<double>> Time;
NameMap<std::vector<long>> TimeStep;
NameMap<long> FileId;
NameMap<int> HaveBlockInfo;
private:
VTKAmrWriter(const VTKAmrWriter&) = delete;
void operator=(const VTKAmrWriter&) = delete;
};
}
#endif
......@@ -53,10 +53,6 @@ protected:
VTKPosthocIO();
~VTKPosthocIO();
private:
int WriteXMLP(vtkCompositeDataSet *cd,
vtkInformation *info, int timeStep);
private:
MPI_Comm Comm;
std::string OutputDir;
......
Markdown is supported
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