Commit f669423d authored by David Thompson's avatar David Thompson

Add particle rendering to Catalyst analysis.

parent 46cb8ccb
<sensei>
<!--
Catalyst particle rendering example
This XML configures a ParaView/Catalyst pipeline
to render points using the point-Gaussian representation.
This representation accepts a particle-style parameter
that should be one of:
+ "Gaussian Blur"
+ "Sphere"
+ "Black-edged circle"
+ "Plain circle"
+ "Triangle"
+ "Square Outline"
You may set the size of the point-Gaussian circles
using the particle-radius parameter (which defaults to 1.0).
The camera will default to looking at the origin from the
point (1,1,1) but will have its position reset along this line
at each timestep so that the entire dataset is in view.
You may override these camera settings with the camera-position
and camera-focus parameters.
For now, you must include an image-filename parameter
indicating where to save the image from each timestep.
-->
<analysis
enabled="1"
type="catalyst" pipeline="particle"
mesh="particles"
particle-style="Black-edged circle" particle-radius="0.5"
array="uniqueGlobalId" association="point"
color-range="0.0,1024.0" color-log="0"
camera-position="150,150,100" camera-focus="0,0,0"
image-filename="/tmp/catalyst-particles-%ts.png"
image-width="1920" image-height="1080"
/>
</sensei>
......@@ -3,7 +3,7 @@
type="vtkmaar" - we should use Haar wavelets to reduce the dataset size
mesh="mesh" - that this analysis will run on the oscillator's image data
field="data" - the scalar field we wish to reduce
association="points" - that the scalar field is defined on points of the image
association="cell" - that the scalar field is defined on cells of the image
reduction="1" - we should perform a single (factor of 8) reduction step
working-directory="/tmp" - where the result files should be stored.
The results include an "index.json" summary and on directory
......@@ -15,7 +15,7 @@
type="vtkmhaar"
mesh="mesh"
field="data"
association="points"
association="cell"
reduction="1"
working-directory="/tmp"
/>
......
......@@ -399,7 +399,7 @@ int DataAdaptor::AddArray(vtkDataObject* mesh, const std::string &meshName,
vtkNew<vtkIdTypeArray> array;
array->SetName(arrayName.c_str());
array->SetNumberOfComponents(1);
array->SetNumberOfTuples(block->GetNumberOfPoints());
array->SetNumberOfTuples(particles.size());
for (vtkIdType i = 0; i < array->GetNumberOfTuples(); ++i)
{
vtkIdType ugid = particles[i].id;
......@@ -412,7 +412,7 @@ int DataAdaptor::AddArray(vtkDataObject* mesh, const std::string &meshName,
vtkNew<vtkFloatArray> array;
array->SetName(arrayName.c_str());
array->SetNumberOfComponents(3);
array->SetNumberOfTuples(block->GetNumberOfPoints());
array->SetNumberOfTuples(particles.size());
float vel[3] = { 0, 0, 0 };
for (vtkIdType i = 0; i < array->GetNumberOfTuples(); ++i)
{
......@@ -429,7 +429,7 @@ int DataAdaptor::AddArray(vtkDataObject* mesh, const std::string &meshName,
vtkNew<vtkFloatArray> array;
array->SetName(arrayName.c_str());
array->SetNumberOfComponents(1);
array->SetNumberOfTuples(block->GetNumberOfPoints());
array->SetNumberOfTuples(particles.size());
for (vtkIdType i = 0; i < array->GetNumberOfTuples(); ++i)
{
float magnitude = std::sqrt(particles[i].velocity.norm());
......
......@@ -45,8 +45,9 @@ inline bool IsVertexInsideBounds(const Vertex& v, const Bounds& b)
struct Block
{
Block(int gid_, const Bounds& bounds_, const Bounds& domain_, const Oscillators& oscillators_):
Block(int gid_, const Bounds& bounds_, const Bounds& domain_, const Oscillators& oscillators_, float velocity_scale_):
gid(gid_),
velocity_scale(velocity_scale_),
bounds(bounds_),
domain(domain_),
grid(Vertex(bounds.max) - Vertex(bounds.min) + Vertex::one()),
......@@ -73,8 +74,8 @@ struct Block
{
particle.velocity += o.evaluateGradient(particle.position, t);
}
// normalize
particle.velocity /= std::sqrt(particle.velocity.norm());
// scale the gradient to get "units" right for velocity
particle.velocity *= velocity_scale;
}
}
......@@ -158,6 +159,7 @@ struct Block
static void destroy(void* b) { delete static_cast<Block*>(b); }
int gid;
float velocity_scale;
Bounds bounds;
Bounds domain;
Grid grid;
......@@ -165,7 +167,9 @@ struct Block
Oscillators oscillators;
private:
Block() {} // for create; to let Master manage the blocks
Block() // for create; to let Master manage the blocks
: gid(-1), velocity_scale(1.0f)
{}
};
......@@ -180,6 +184,7 @@ int main(int argc, char** argv)
int nblocks = world.size();
float t_end = 10;
float dt = .01;
float velocity_scale = 50.0f;
size_t window = 10;
size_t k_max = 3;
int threads = 1;
......@@ -204,6 +209,7 @@ int main(int argc, char** argv)
>> Option('o', "output", out_prefix, "prefix to save output")
>> Option('g', "ghost levels", ghostLevels, "number of ghost levels")
>> Option('p', "particles", numberOfParticles, "number of random particles to generate")
>> Option('v', "velocity scale", velocity_scale, "scale factor to convert function gradient to velocity")
>> Option( "seed", seed, "specify a random seed")
;
bool sync = ops >> Present("sync", "synchronize after each time step");
......@@ -292,7 +298,7 @@ int main(int argc, char** argv)
diy::decompose(3, world.rank(), domain, assigner,
[&](int gid, const Bounds&, const Bounds& bounds, const Bounds& domain, const Link& link)
{
auto b = new Block(gid, bounds, domain, oscillators);
auto b = new Block(gid, bounds, domain, oscillators, velocity_scale);
// generate particles
int start = particlesPerBlock * gid;
......
......@@ -17,7 +17,7 @@ if (ENABLE_SENSEI)
if (ENABLE_CATALYST)
list(APPEND senseiCore_sources CatalystAnalysisAdaptor.cxx
CatalystSlice.cxx CatalystUtilities.cxx)
CatalystParticle.cxx CatalystSlice.cxx CatalystUtilities.cxx)
endif()
if (ENABLE_VTKM)
......
This diff is collapsed.
#ifndef sensei_CatalystParticle_h
#define sensei_CatalystParticle_h
#include "vtkCPPipeline.h"
namespace sensei
{
class CatalystParticle : public vtkCPPipeline
{
public:
static CatalystParticle* New();
vtkTypeMacro(CatalystParticle, vtkCPPipeline);
void PrintSelf(ostream& os, vtkIndent indent) override;
/// @brief Set the mesh on which the slice should operate.
void SetInputMesh(const std::string& meshName);
/// @brief Set particle glyph type.
void SetParticleGlyph(const std::string& glyphType);
/// @brief Set parameters for saving rendering result.
///
/// If not specified, the pipeline will not do any rendering.
void SetImageParameters(const std::string& filename, int width, int height);
/// @brief Choose how particles are rendered.
///
/// This must be one of the following strings:
/// + "Gaussian Blur"
/// + "Sphere"
/// + "Black-edged circle"
/// + "Plain circle"
/// + "Triangle"
/// + "Square Outline"
///
/// The default is "Sphere".
void SetParticleStyle(const std::string& style);
/// @brief Set a scale factor to apply to each particle's Gaussian radius.
///
/// The default is 1.0.
void SetParticleRadius(float radius);
/// @brief Set the camera location
///
/// If unset, the camera will be placed at (1,1,1) and reset at each time-step.
/// Note that resetting the camera may cause discontinuities in the camera
/// motion if the bounds of the data change abruptly with time.
void SetCameraPosition(const double posn[3]);
/// @brief Set the camera's focal point.
///
/// If unset, the camera will focus on the origin, (0,0,0).
void SetCameraFocus(const double focus[3]);
/// @brief Set array to color with.
///
/// Set array to color with. If arrayname is NULL, coloring will be disabled.
void ColorBy(int association, const std::string& arrayname);
/// @brief Set whether to automatically determine color range per time iteration.
///
/// When set to true, this analysis adaptor will compute and set the color range per
/// timestep. Otherwise, it will use the value specified using SetColorRange().
void SetAutoColorRange(bool val);
bool GetAutoColorRange() const;
/// @brief Set the color range to use when AutoColorRange is false.
///
/// Set the color range to use when AutoColorRange is false.
void SetColorRange(double min, double max);
const double* GetColorRange() const;
/// @brief Set whether to use log scale for coloring.
void SetUseLogScale(bool val);
bool GetUseLogScale() const;
int RequestDataDescription(vtkCPDataDescription* dataDesc) override;
int CoProcess(vtkCPDataDescription* dataDesc) override;
int Finalize() override;
protected:
CatalystParticle();
~CatalystParticle();
private:
CatalystParticle(const CatalystParticle&); // Not implemented.
void operator=(const CatalystParticle&); // Not implemented.
class vtkInternals;
vtkInternals* Internals;
};
}
#endif
......@@ -24,6 +24,7 @@
#endif
#ifdef ENABLE_CATALYST
#include "CatalystAnalysisAdaptor.h"
#include "CatalystParticle.h"
#include "CatalystSlice.h"
#endif
#ifdef ENABLE_LIBSIM
......@@ -433,6 +434,70 @@ int ConfigurableAnalysis::InternalsType::AddCatalyst(pugi::xml_node node)
this->CatalystAdaptor->AddPipeline(slice.GetPointer());
}
else if (strcmp(node.attribute("pipeline").value(), "particle") == 0)
{
vtkNew<CatalystParticle> particle;
double tmp[3];
if (node.attribute("mesh"))
{
particle->SetInputMesh(node.attribute("mesh").value());
}
if (node.attribute("particle-style"))
{
particle->SetParticleStyle(node.attribute("particle-style").value());
}
if (node.attribute("particle-radius") &&
(std::sscanf(node.attribute("particle-radius").value(),
"%lg", &tmp[0]) == 1))
{
particle->SetParticleRadius(tmp[0]);
}
if (node.attribute("camera-position") &&
(std::sscanf(node.attribute("camera-position").value(),
"%lg,%lg,%lg", &tmp[0], &tmp[1], &tmp[2]) == 3))
{
particle->SetCameraPosition(tmp);
}
if (node.attribute("camera-focus") &&
(std::sscanf(node.attribute("camera-focus").value(),
"%lg,%lg,%lg", &tmp[0], &tmp[1], &tmp[2]) == 3))
{
particle->SetCameraFocus(tmp);
}
int association = 0;
std::string assocStr = node.attribute("association").as_string("point");
if (VTKUtils::GetAssociation(assocStr, association))
{
SENSEI_ERROR("Failed to initialize Catalyst")
return -1;
}
particle->ColorBy(association, node.attribute("array").value());
if (node.attribute("color-range") &&
(std::sscanf(node.attribute("color-range").value(), "%lg,%lg", &tmp[0], &tmp[1]) == 2))
{
particle->SetAutoColorRange(false);
particle->SetColorRange(tmp[0], tmp[1]);
}
else
{
particle->SetAutoColorRange(true);
}
particle->SetUseLogScale(node.attribute("color-log").as_int(0) == 1);
if (node.attribute("image-filename"))
{
particle->SetImageParameters(
node.attribute("image-filename").value(),
node.attribute("image-width").as_int(800),
node.attribute("image-height").as_int(800));
}
this->CatalystAdaptor->AddPipeline(particle.GetPointer());
}
else if (strcmp(node.attribute("pipeline").value(), "pythonscript") == 0)
{
#ifndef ENABLE_CATALYST_PYTHON
......
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