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

add data requirements

a helper class to handle XML parsing and track mesh name,
and point and cell data array names to be processed during
analysis.
parent 8cda178f
......@@ -13,6 +13,7 @@
#include "senseiConfig.h"
#include "senseiPyDataAdaptor.h"
#include "LibsimImageProperties.h"
#include "DataRequirements.h"
%}
%init %{
......@@ -24,8 +25,8 @@ import_array();
%include <std_vector.i>
%template(vector_string) std::vector<std::string>;
%include <mpi4py/mpi4py.i>
%include <vtk.i>
%include <senseiTypeMaps.i>
%include "vtk.i"
%include "senseiTypeMaps.i"
%mpi4py_typemap(Comm, MPI_Comm);
......@@ -38,6 +39,47 @@ VTK_SWIG_INTEROP(vtkObjectBase)
VTK_SWIG_INTEROP(vtkDataObject)
VTK_SWIG_INTEROP(vtkInformation)
/****************************************************************************
* DataRequirements
***************************************************************************/
%ignore sensei::MeshRequirementsIterator::operator++;
%ignore sensei::MeshRequirementsIterator::operator bool() const;
%extend sensei::MeshRequirementsIterator
{
// ------------------------------------------------------------------------
int __bool__()
{
return static_cast<bool>(*self);
}
// ------------------------------------------------------------------------
sensei::MeshRequirementsIterator &__iadd__(int n)
{
for (int i = 0; (i < n) && *self; ++i)
self->operator++();
return *self;
}
}
%ignore sensei::ArrayRequirementsIterator::operator++;
%ignore sensei::ArrayRequirementsIterator::operator bool() const;
%extend sensei::ArrayRequirementsIterator
{
// ------------------------------------------------------------------------
int __bool__()
{
return static_cast<bool>(*self);
}
// ------------------------------------------------------------------------
sensei::ArrayRequirementsIterator &__iadd__(int n)
{
for (int i = 0; i < n; ++i)
self->operator++();
return *self;
}
}
%include "DataRequirements.h"
/****************************************************************************
* DataAdaptor
***************************************************************************/
......
if (ENABLE_SENSEI)
message(STATUS "Enabled: sensei library")
set(sensei_sources Error.cxx AnalysisAdaptor.cxx Autocorrelation.cxx
ConfigurableAnalysis.cxx DataAdaptor.cxx ProgrammableDataAdaptor.cxx
PosthocIO.cxx Histogram.cxx VTKHistogram.cxx VTKDataAdaptor.cxx)
set(sensei_sources AnalysisAdaptor.cxx Autocorrelation.cxx
ConfigurableAnalysis.cxx DataAdaptor.cxx DataRequirements.cxx
Histogram.cxx Error.cxx ProgrammableDataAdaptor.cxx VTKHistogram.cxx
VTKDataAdaptor.cxx)
set(sensei_libs mpi pugixml vtk thread ArrayIO timer diy grid)
......
#include "DataRequirements.h"
#include "DataAdaptor.h"
#include "VTKUtils.h"
#include "Error.h"
#include <vtkDataObject.h>
#include <sstream>
namespace sensei
{
static
unsigned int getArrayNames(pugi::xml_node node, std::vector<std::string> &arrays)
{
if (!node || !node.text())
return -1;
std::string text = node.text().as_string();
// replace ',' with ' '
size_t n = text.size();
for (size_t i = 0; i < n; ++i)
{
if (text[i] == ',')
text[i] = ' ';
}
std::istringstream iss(text);
while (iss.good())
{
std::string array;
iss >> array >> std::ws;
arrays.push_back(array);
}
return arrays.size();
}
// --------------------------------------------------------------------------
DataRequirements::DataRequirements()
{
}
// --------------------------------------------------------------------------
DataRequirements::~DataRequirements()
{
}
// --------------------------------------------------------------------------
void DataRequirements::Clear()
{
this->MeshNames.clear();
this->MeshArrayMap.clear();
}
// --------------------------------------------------------------------------
int DataRequirements::Initialize(pugi::xml_node parent)
{
this->Clear();
int retVal = 0;
int meshId = 0;
// walk the children look for elements named mesh
for (pugi::xml_node node = parent.child("mesh");
node; node = node.next_sibling("mesh"))
{
// get the mesh name,, it is required
if (!node.attribute("name"))
{
SENSEI_ERROR("Mesh " << meshId
<< " element is missing required attribute name")
retVal = -1;
continue;
}
std::string meshName = node.attribute("name").as_string();
bool structureOnly = node.attribute("structure_only").as_int(0);
this->MeshNames.insert(std::make_pair(meshName, structureOnly));
// get cell data arrays, optional
std::vector<std::string> arrays;
if (getArrayNames(node.child("cell_arrays"), arrays))
this->MeshArrayMap[meshName][vtkDataObject::CELL] = arrays;
// get point data arrays, optional
arrays.clear();
if (getArrayNames(node.child("point_arrays"), arrays))
this->MeshArrayMap[meshName][vtkDataObject::POINT] = arrays;
meshId += 1;
}
return retVal;
}
//----------------------------------------------------------------------------
int DataRequirements::Initialize(DataAdaptor *adaptor)
{
this->Clear();
std::vector<std::string> meshNames;
if (adaptor->GetMeshNames(meshNames))
{
SENSEI_ERROR("Failed to get mesh names")
return -1;
}
unsigned int nMeshes = meshNames.size();
for (unsigned int i = 0; i < nMeshes; ++i)
{
const std::string &meshName = meshNames[i];
this->AddRequirement(meshName, false);
int associations[] = {vtkDataObject::POINT, vtkDataObject::CELL};
for (int j = 0; j < 2; ++j)
{
int association = associations[j];
std::vector<std::string> arrays;
if (adaptor->GetArrayNames(meshName, association, arrays))
{
SENSEI_ERROR("Failed to get "
<< VTKUtils::GetAttributesName(association)
<< " adaptor arrays on mesh \"" << meshName << "\"")
return -1;
}
if (arrays.size())
this->AddRequirement(meshName, association, arrays);
}
}
return 0;
}
// --------------------------------------------------------------------------
int DataRequirements::AddRequirement(const std::string &meshName,
bool structureOnly)
{
this->MeshNames.insert(std::make_pair(meshName, structureOnly));
return 0;
}
// --------------------------------------------------------------------------
int DataRequirements::AddRequirement(const std::string &meshName,
int association, const std::vector<std::string> &arrays)
{
if (meshName.empty())
{
SENSEI_ERROR("A mesh name is required")
return -1;
}
// always add the mesh, mesh geometry can be used without
// any arrays
this->MeshNames.insert(std::make_pair(meshName, false));
// only add arrays if there are any
if (!arrays.empty())
this->MeshArrayMap[meshName][association] = arrays;
return 0;
}
// --------------------------------------------------------------------------
int DataRequirements::GetRequiredMesh(unsigned int id, std::string &mesh) const
{
mesh = "";
unsigned int nMeshes = this->MeshNames.size();
if (id >= nMeshes)
{
SENSEI_ERROR("Index " << id << " is out of bounds, only "
<< nMeshes << " meshes")
return -1;
}
MeshNamesType::const_iterator it = this->MeshNames.begin();
for (unsigned int i = 0; i < id; ++i)
++it;
mesh = it->first;
return 0;
}
// --------------------------------------------------------------------------
int DataRequirements::GetRequiredMeshes(std::vector<std::string> &meshes) const
{
meshes.clear();
MeshNamesType::const_iterator it = this->MeshNames.begin();
MeshNamesType::const_iterator end = this->MeshNames.end();
for (; it != end; ++it)
meshes.push_back(it->first);
return meshes.size();
}
// --------------------------------------------------------------------------
unsigned int DataRequirements::GetNumberOfRequiredMeshes() const
{
return this->MeshNames.size();
}
// --------------------------------------------------------------------------
int DataRequirements::GetRequiredArrays(const std::string &meshName,
int association, std::vector<std::string> &arrays) const
{
arrays.clear();
MeshArrayMapType::const_iterator it = this->MeshArrayMap.find(meshName);
if (it != this->MeshArrayMap.end())
{
AssocArrayMapType::const_iterator ait = it->second.find(association);
if (ait != it->second.end())
arrays = ait->second;
}
return 0;
}
// --------------------------------------------------------------------------
int DataRequirements::GetNumberOfRequiredArrays(const std::string &meshName,
int association, unsigned int &nArrays) const
{
nArrays = 0;
MeshArrayMapType::const_iterator it = this->MeshArrayMap.find(meshName);
if (it != this->MeshArrayMap.end())
{
AssocArrayMapType::const_iterator ait = it->second.find(association);
if (ait != it->second.end())
nArrays = ait->second.size();
}
return 0;
}
// --------------------------------------------------------------------------
MeshRequirementsIterator DataRequirements::GetMeshRequirementsIterator() const
{
MeshRequirementsIterator it(this->MeshNames);
return it;
}
// --------------------------------------------------------------------------
ArrayRequirementsIterator DataRequirements::GetArrayRequirementsIterator(
const std::string &meshName) const
{
MeshArrayMapType::const_iterator it = this->MeshArrayMap.find(meshName);
if (it != this->MeshArrayMap.end())
{
ArrayRequirementsIterator ait(it->second);
return ait;
}
SENSEI_ERROR("No mesh named \"" << meshName << "\"")
return ArrayRequirementsIterator();
}
}
#ifndef DataRequirements_h
#define DataRequirements_h
#include <string>
#include <vector>
#include <map>
#include <set>
#include <pugixml.hpp>
namespace sensei
{
class DataAdaptor;
class MeshRequirementsIterator;
class ArrayRequirementsIterator;
/// This is a helper class that handles the common
/// task of specifying the set of meshes and arrays
/// rqeuired to perform a specific analysis. An analysis
/// would typically intialize the data requirement from
/// XML and then probe the requirements during execution
/// to get the minimal set of data required to complete
/// the analysis.
class DataRequirements
{
public:
DataRequirements();
~DataRequirements();
/// Returns true if the object is empty
bool Empty() const { return this->MeshArrayMap.empty(); }
/// initialize from XML. the XML should contain one or more
/// mesh elements each with zero or more array groups
///
/// <parent>
/// <mesh name="mesh_1" structure_only="1">
/// <cell_arrays> array_1, ... array_n </cell_arrays>
/// <point_arrays> array_1, ... array_n </point_arrays>
/// </mesh>
/// .
/// .
/// .
/// <mesh name="mesh_n" structure_only="0">
/// <cell_arrays> array_1, ... array_n </cell_arrays>
/// <point_arrays> array_1, ... array_n </point_arrays>
/// </mesh>
/// </parent>
///
/// @param[in] parent XML node which contains mesh elements
/// @returns the number of mesh elements processed.
int Initialize(pugi::xml_node parent);
/// @breif Get a description of what data is available
///
/// This is a convenience method that fills in
/// all of data that the data adaptor makes avaialable.
///
/// @param[in] adaptor a DataAdaptor instance
/// @returns zero if successful, non zero if an error occurred
int Initialize(DataAdaptor *adaptor);
/// Adds a mesh
/// The requirement consists of a mesh name and weather it is structure only
/// @param[in] meshName name of the mesh
/// @param[in] structureOnly flag indicating if mesh geometry is needed
/// @returns zero if successful
int AddRequirement(const std::string &meshName, bool structureOnly);
/// Adds a set of arrays on a specific mesh
/// The requirement consists of a mesh name and a list of arrays
/// @param[in] meshName name of the mesh
/// @param[in] association type of arrays
/// @param[in] arrays a list of the required arrays
/// @returns zero if successful
int AddRequirement(const std::string &meshName, int association,
const std::vector<std::string> &arrays);
/// Get the list of meshes
/// @param[out] meshes a vector where mesh names will be stored
/// @returns zero if successful
int GetRequiredMeshes(std::vector<std::string> &meshes) const;
unsigned int GetNumberOfRequiredMeshes() const;
int GetRequiredMesh(unsigned int id, std::string &mesh) const;
/// For the named mesh, gets the list of required arrays
/// @param[in] meshName the name of the mesh
/// @param[in] association vtkDataObject::POINT, vtkDataObject::CELL, etc
/// @param[out] arrays a vector where the arrays will be stored
/// @returns zero if successful
int GetRequiredArrays(const std::string &meshName, int association,
std::vector<std::string> &arrays) const;
/// For the named mesh, and association, gets the number of required arrays
/// @param[in] meshName the name of the mesh
/// @param[in] association vtkDataObject::POINT, vtkDataObject::CELL, etc
/// @param[out] number of arrays
/// @returns zero if successful
int GetNumberOfRequiredArrays(const std::string &meshName,
int association, unsigned int &nArrays) const;
/// Clear the contents of the container
void Clear();
/// Get an iterator for the named mesh
MeshRequirementsIterator GetMeshRequirementsIterator() const;
/// Get an iterator for the associated arrays
ArrayRequirementsIterator GetArrayRequirementsIterator(
const std::string &meshName) const;
public:
using AssocArrayMapType = std::map<int, std::vector<std::string>>;
using MeshArrayMapType = std::map<std::string, AssocArrayMapType>;
using MeshNamesType = std::map<std::string, bool>;
private:
friend class ArrayRequirementsIterator;
friend class MeshRequirementsIterator;
MeshNamesType MeshNames;
MeshArrayMapType MeshArrayMap;
};
// iterate over the meshes
class MeshRequirementsIterator
{
public:
MeshRequirementsIterator() : Valid(false) {}
MeshRequirementsIterator(const DataRequirements::MeshNamesType &meshNames)
: Valid(true), It(meshNames.cbegin()), End(meshNames.cend()) {}
/// advance to the next requirement
MeshRequirementsIterator &operator++(){ ++this->It; return *this; }
/// Test if the iterator is finished
operator bool() const { return Valid && (this->It != this->End); }
/// returns the mesh name
const std::string &MeshName(){ return this->It->first; }
bool StructureOnly(){ return this->It->second; }
private:
bool Valid;
DataRequirements::MeshNamesType::const_iterator It;
DataRequirements::MeshNamesType::const_iterator End;
};
/// Iterate over the mesh's arrays. One can iterate either over
/// associations and access all of an associations arrays or
/// one by one over the arrays.
class ArrayRequirementsIterator
{
public:
enum {MODE_ARRAY, MODE_ASSOCIATION};
ArrayRequirementsIterator() : Valid(false), Mode(MODE_ARRAY) {}
ArrayRequirementsIterator(const DataRequirements::AssocArrayMapType &aa)
: Valid(true), Mode(MODE_ARRAY), It(aa.cbegin()), End(aa.cend())
{ this->UpdateArrayIterator(); }
/// Set the mode of operator++
void SetMode(int mode){ this->Mode = mode; }
/// Test if the iterator is finished
operator bool() const
{
if (Valid)
{
if (this->Mode == MODE_ASSOCIATION)
{
if (this->It == this->End)
return false;
else
return true;
}
else if (this->Mode == MODE_ARRAY)
{
if ((this->It == this->End) && (this->AIt == this->AEnd))
return false;
else
return true;
}
}
return true;
}
/// returns the type of requirement (point,cell, etc)
int Association(){ return this->It->first; }
/// Get the next group of requied arrays
const std::vector<std::string> &Arrays(){ return this->It->second; }
/// Get the next requied array
const std::string &Array(){ return *this->AIt; }
/// Advance the iterator
ArrayRequirementsIterator &operator++()
{
if (this->Mode == MODE_ARRAY)
return this->NextArray();
return this->NextAssociation();
}
/// move to the next group od arrays
ArrayRequirementsIterator &NextAssociation()
{
if (this->It != this->End)
{
++this->It;
this->UpdateArrayIterator();
// the current association is empty
if (this->AIt == this->AEnd)
return this->NextAssociation();
}
return *this;
}
/// advance to the next array
ArrayRequirementsIterator &NextArray()
{
++this->AIt;
// at the end of this association's arrays
if (this->AIt == this->AEnd)
this->NextAssociation();
return *this;
}
private:
// move array iterator to the next group of arrays
void UpdateArrayIterator()
{
if (this->It != this->End)
{
this->AIt = this->It->second.cbegin();
this->AEnd = this->It->second.cend();
// the current association is empty
if (this->AIt == this->AEnd)
this->NextAssociation();
}
}
bool Valid;
int Mode;
DataRequirements::AssocArrayMapType::const_iterator It;
DataRequirements::AssocArrayMapType::const_iterator End;
std::vector<std::string>::const_iterator AIt;
std::vector<std::string>::const_iterator AEnd;
};
}
#endif
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