ParaView-5.0.1: Added the source-tree to ThirdParty-dev and patched as described in the README file

Resolves bug-report http://bugs.openfoam.org/view.php?id=2098
This commit is contained in:
Henry Weller
2016-05-30 21:20:56 +01:00
parent 1cce60aa78
commit eba760a6d6
24640 changed files with 6366069 additions and 0 deletions

View File

@ -0,0 +1,75 @@
#------------------------------------------------------------------------------
# Builds the examples as a separate project using a custom target.
# This is included in ParaView/CMakeLists.txt to build examples as a separate
# project.
#------------------------------------------------------------------------------
# Make sure it uses the same build configuration as ParaView.
if (CMAKE_CONFIGURATION_TYPES)
set(build_config_arg -C "${CMAKE_CFG_INTDIR}")
else()
set(build_config_arg)
endif()
set (extra_params)
foreach (flag CMAKE_C_FLAGS_DEBUG
CMAKE_C_FLAGS_RELEASE
CMAKE_C_FLAGS_MINSIZEREL
CMAKE_C_FLAGS_RELWITHDEBINFO
CMAKE_CXX_FLAGS_DEBUG
CMAKE_CXX_FLAGS_RELEASE
CMAKE_CXX_FLAGS_MINSIZEREL
CMAKE_CXX_FLAGS_RELWITHDEBINFO)
if (${${flag}})
set (extra_params ${extra_params}
-D${flag}:STRING=${${flag}})
endif()
endforeach()
set (examples_dependencies
vtkPVServerManagerApplication
vtkPVServerManagerApplicationCS)
if (PARAVIEW_BUILD_QT_GUI)
list (APPEND examples_dependencies pqApplicationComponents)
endif()
set(ENABLE_CATALYST OFF)
if (PARAVIEW_ENABLE_PYTHON AND PARAVIEW_USE_MPI AND PARAVIEW_ENABLE_CATALYST AND NOT WIN32)
list (APPEND examples_dependencies vtkPVPythonCatalyst)
set (ENABLE_CATALYST ON)
endif()
add_custom_command(
OUTPUT "${ParaView_BINARY_DIR}/ParaViewExamples.done"
COMMAND ${CMAKE_CTEST_COMMAND}
ARGS ${build_config_arg}
--build-and-test
${ParaView_SOURCE_DIR}/Examples
${ParaView_BINARY_DIR}/Examples/All
--build-noclean
--build-two-config
--build-project ParaViewExamples
--build-generator ${CMAKE_GENERATOR}
--build-makeprogram ${CMAKE_MAKE_PROGRAM}
--build-options -DParaView_DIR:PATH=${ParaView_BINARY_DIR}
-DPARAVIEW_QT_VERSION:STRING=${PARAVIEW_QT_VERSION}
-DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE}
-DCMAKE_C_COMPILER:FILEPATH=${CMAKE_C_COMPILER}
-DCMAKE_C_FLAGS:STRING=${CMAKE_C_FLAGS}
-DCMAKE_CXX_COMPILER:FILEPATH=${CMAKE_CXX_COMPILER}
-DCMAKE_CXX_FLAGS:STRING=${CMAKE_CXX_FLAGS}
-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=${CMAKE_LIBRARY_OUTPUT_DIRECTORY}
-DCMAKE_RUNTIME_OUTPUT_DIRECTORY:PATH=${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
-DBUILD_TESTING:BOOL=${BUILD_TESTING}
-DPARAVIEW_TEST_OUTPUT_DIR:PATH=${PARAVIEW_TEST_OUTPUT_DIR}
-DENABLE_CATALYST:BOOL=${ENABLE_CATALYST}
${extra_params}
--no-warn-unused-cli
COMMAND ${CMAKE_COMMAND} -E touch
"${ParaView_BINARY_DIR}/ParaViewExamples.done"
COMMENT "Build examples as a separate project"
DEPENDS ${examples_dependencies}
)
# Add custom target to ensure that the examples get built.
add_custom_target(ParaViewExamples ALL DEPENDS
"${ParaView_BINARY_DIR}/ParaViewExamples.done")

View File

@ -0,0 +1,51 @@
cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR)
project(ParaViewExamples)
option(ENABLE_CATALYST "Enable Catalyst Examples" OFF)
set(paraview_components)
if (ENABLE_CATALYST)
list(APPEND paraview_components vtkPVPythonCatalyst)
endif ()
if (paraview_components)
list(INSERT paraview_components 0 COMPONENTS)
endif ()
find_package(ParaView REQUIRED ${paraview_components})
include(${PARAVIEW_USE_FILE})
add_subdirectory(Plugins/Filter)
add_subdirectory(Plugins/ParametricSource)
add_subdirectory(Plugins/SMProxy)
add_subdirectory(Plugins/Representation)
add_subdirectory(Plugins/HiddenLinesRemoval)
if (ENABLE_CATALYST)
add_subdirectory(Catalyst)
endif ()
if (PARAVIEW_BUILD_QT_GUI)
add_subdirectory(Plugins/Autostart)
add_subdirectory(Plugins/DockWidget)
add_subdirectory(Plugins/GUIToolBar)
add_subdirectory(Plugins/PropertyWidgets)
add_subdirectory(Plugins/Reader)
add_subdirectory(Plugins/ReaderXMLOnly)
add_subdirectory(Plugins/RenderPassViews)
# TODO: update this plugin to use the pipeline controller instead.
#add_subdirectory(Plugins/RepresentationBehavior)
add_subdirectory(Plugins/SourceToolbar)
add_subdirectory(Plugins/Writer)
add_subdirectory(CustomApplications/Clone1)
add_subdirectory(CustomApplications/Clone2)
add_subdirectory(CustomApplications/Demo0)
add_subdirectory(CustomApplications/Demo1)
add_subdirectory(CustomApplications/MultiServerClient)
add_subdirectory(CustomApplications/Spreadsheet)
add_subdirectory(CustomApplications/ParticlesViewer)
endif()
if (TARGET vtkIOVisItBridge)
# add_subdirectory(Plugins/VisItReader)
endif ()

View File

@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystCFullExample C)
set(USE_CATALYST ON CACHE BOOL "Link the simulator with Catalyst")
if(USE_CATALYST)
# we only need C++ compilers if we're building the Adaptor
enable_language(CXX)
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
set(Adaptor_SRCS
FEAdaptor.cxx
)
add_library(CFullExampleAdaptor ${Adaptor_SRCS})
target_link_libraries(CFullExampleAdaptor vtkPVPythonCatalyst)
add_definitions("-DUSE_CATALYST")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
else()
find_package(MPI REQUIRED)
include_directories(${MPI_C_INCLUDE_PATH})
endif()
add_executable(CFullExample FEDriver.c FEDataStructures.c)
if(USE_CATALYST)
target_link_libraries(CFullExample LINK_PRIVATE CFullExampleAdaptor)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(CFullExample)
else()
target_link_libraries(CFullExample LINK_PRIVATE ${MPI_LIBRARIES})
endif()
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
add_test(NAME CFullExampleTest COMMAND CFullExample ${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/feslicescript.py)
set_tests_properties(CFullExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,152 @@
#include <iostream>
#include "FEAdaptor.h"
#include <vtkCellData.h>
#include <vtkCellType.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkCPPythonScriptPipeline.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkPointData.h>
#include <vtkUnstructuredGrid.h>
namespace
{
vtkCPProcessor* Processor = NULL;
vtkUnstructuredGrid* VTKGrid;
void BuildVTKGrid(unsigned int numberOfPoints, double* pointsData,
unsigned int numberOfCells, unsigned int* cellsData)
{
// create the points information
vtkNew<vtkDoubleArray> pointArray;
pointArray->SetNumberOfComponents(3);
pointArray->SetArray(pointsData, static_cast<vtkIdType>(numberOfPoints*3), 1);
vtkNew<vtkPoints> points;
points->SetData(pointArray.GetPointer());
VTKGrid->SetPoints(points.GetPointer());
// create the cells
VTKGrid->Allocate(static_cast<vtkIdType>(numberOfCells*9));
for(unsigned int cell=0;cell<numberOfCells;cell++)
{
unsigned int* cellPoints = cellsData+8*cell;
vtkIdType tmp[8] = {cellPoints[0], cellPoints[1], cellPoints[2], cellPoints[3],
cellPoints[4], cellPoints[5], cellPoints[6], cellPoints[7]};
VTKGrid->InsertNextCell(VTK_HEXAHEDRON, 8, tmp);
}
}
void UpdateVTKAttributes(unsigned int numberOfPoints, double* velocityData,
unsigned int numberOfCells, float* pressureData)
{
if(VTKGrid->GetPointData()->GetNumberOfArrays() == 0)
{
// velocity array
vtkNew<vtkDoubleArray> velocity;
velocity->SetName("velocity");
velocity->SetNumberOfComponents(3);
velocity->SetNumberOfTuples(static_cast<vtkIdType>(numberOfPoints));
VTKGrid->GetPointData()->AddArray(velocity.GetPointer());
}
if(VTKGrid->GetCellData()->GetNumberOfArrays() == 0)
{
// pressure array
vtkNew<vtkFloatArray> pressure;
pressure->SetName("pressure");
pressure->SetNumberOfComponents(1);
VTKGrid->GetCellData()->AddArray(pressure.GetPointer());
}
vtkDoubleArray* velocity = vtkDoubleArray::SafeDownCast(
VTKGrid->GetPointData()->GetArray("velocity"));
// The velocity array is ordered as vx0,vx1,vx2,..,vy0,vy1,vy2,..,vz0,vz1,vz2,..
// so we need to create a full copy of it with VTK's ordering of
// vx0,vy0,vz0,vx1,vy1,vz1,..
for(unsigned int i=0;i<numberOfPoints;i++)
{
double values[3] = {velocityData[i], velocityData[i+numberOfPoints],
velocityData[i+2*numberOfPoints]};
velocity->SetTupleValue(i, values);
}
vtkFloatArray* pressure = vtkFloatArray::SafeDownCast(
VTKGrid->GetCellData()->GetArray("pressure"));
// The pressure array is a scalar array so we can reuse
// memory as long as we ordered the points properly.
pressure->SetArray(pressureData, static_cast<vtkIdType>(numberOfCells), 1);
}
void BuildVTKDataStructures(unsigned int numberOfPoints, double* points, double* velocity,
unsigned int numberOfCells, unsigned int* cells, float* pressure)
{
if(VTKGrid == NULL)
{
// The grid structure isn't changing so we only build it
// the first time it's needed. If we needed the memory
// we could delete it and rebuild as necessary.
VTKGrid = vtkUnstructuredGrid::New();
BuildVTKGrid(numberOfPoints, points, numberOfCells, cells);
}
UpdateVTKAttributes(numberOfPoints, velocity, numberOfCells, pressure);
}
}
void CatalystInitialize(int numScripts, char* scripts[])
{
if(Processor == NULL)
{
Processor = vtkCPProcessor::New();
Processor->Initialize();
}
else
{
Processor->RemoveAllPipelines();
}
for(int i=1;i<numScripts;i++)
{
vtkNew<vtkCPPythonScriptPipeline> pipeline;
pipeline->Initialize(scripts[i]);
Processor->AddPipeline(pipeline.GetPointer());
}
}
void CatalystFinalize()
{
if(Processor)
{
Processor->Delete();
Processor = NULL;
}
if(VTKGrid)
{
VTKGrid->Delete();
VTKGrid = NULL;
}
}
void CatalystCoProcess(unsigned int numberOfPoints, double* pointsData,
unsigned int numberOfCells, unsigned int* cellsData,
double* velocityData, float* pressureData, double time,
unsigned int timeStep, int lastTimeStep)
{
vtkNew<vtkCPDataDescription> dataDescription;
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(Processor->RequestDataDescription(dataDescription.GetPointer()) != 0)
{
BuildVTKDataStructures(numberOfPoints, pointsData, velocityData,
numberOfCells, cellsData, pressureData);
dataDescription->GetInputDescriptionByName("input")->SetGrid(VTKGrid);
Processor->CoProcess(dataDescription.GetPointer());
}
}

View File

@ -0,0 +1,19 @@
#ifndef FEADAPTOR_HEADER
#define FEADAPTOR_HEADER
#ifdef __cplusplus
extern "C"
{
#endif
void CatalystInitialize(int numScripts, char* scripts[]);
void CatalystFinalize();
void CatalystCoProcess(unsigned int numberOfPoints, double* pointsData,
unsigned int numberOfCells, unsigned int* cellsDAta,
double* velocityData, float* pressureData, double time,
unsigned int timeStep, int lastTimeStep);
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,137 @@
#include "FEDataStructures.h"
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
void InitializeGrid(Grid* grid, const unsigned int numPoints[3], const double spacing[3] )
{
grid->NumberOfPoints = 0;
grid->Points = 0;
grid->NumberOfCells = 0;
grid->Cells = 0;
if(numPoints[0] == 0 || numPoints[1] == 0 || numPoints[2] == 0)
{
printf("Must have a non-zero amount of points in each direction.\n");
}
// in parallel, we do a simple partitioning in the x-direction.
int mpiSize = 1;
int mpiRank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
unsigned int startXPoint = mpiRank*numPoints[0]/mpiSize;
unsigned int endXPoint = (mpiRank+1)*numPoints[0]/mpiSize;
if(mpiSize != mpiRank+1)
{
endXPoint++;
}
// create the points -- slowest in the x and fastest in the z directions
if(grid->Points != 0)
{
free(grid->Points);
}
unsigned int numXPoints = endXPoint - startXPoint;
grid->Points = (double*) malloc(3*sizeof(double)*numPoints[1]*numPoints[2]*numXPoints);
unsigned int counter = 0;
unsigned int i, j, k;
for(i=startXPoint;i<endXPoint;i++)
{
for(j=0;j<numPoints[1];j++)
{
for(k=0;k<numPoints[2];k++)
{
grid->Points[counter] = i*spacing[0];
grid->Points[counter+1] = j*spacing[1];
grid->Points[counter+2] = k*spacing[2];
counter += 3;
}
}
}
grid->NumberOfPoints = numPoints[1]*numPoints[2]*numXPoints;
// create the hex cells
if(grid->Cells != 0)
{
free(grid->Cells);
}
grid->Cells = (unsigned int*) malloc(8*sizeof(unsigned int)*(numPoints[1]-1)*(numPoints[2]-1)*(numXPoints-1));
counter = 0;
for(i=0;i<numXPoints-1;i++)
{
for(j=0;j<numPoints[1]-1;j++)
{
for(k=0;k<numPoints[2]-1;k++)
{
grid->Cells[counter] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
grid->Cells[counter+1] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
grid->Cells[counter+2] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
grid->Cells[counter+3] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
grid->Cells[counter+4] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
grid->Cells[counter+5] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
grid->Cells[counter+6] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
grid->Cells[counter+7] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
counter += 8;
}
}
}
grid->NumberOfCells = (numPoints[1]-1)*(numPoints[2]-1)*(numXPoints-1);
}
void FinalizeGrid(Grid* grid)
{
if(grid->Points)
{
free(grid->Points);
grid->Points = 0;
}
if(grid->Cells)
{
free(grid->Cells);
grid->Cells = 0;
}
grid->NumberOfPoints = 0;
grid->NumberOfCells = 0;
}
void InitializeAttributes(Attributes* attributes, Grid* grid)
{
attributes->GridPtr = grid;
attributes->Velocity = 0;
attributes->Pressure = 0;
}
void UpdateFields(Attributes* attributes, double time)
{
unsigned int numPoints = attributes->GridPtr->NumberOfPoints;
if(attributes->Velocity != 0)
{
free(attributes->Velocity);
}
attributes->Velocity = (double*) malloc(sizeof(double)*numPoints*3);
unsigned int i;
for(i=0;i<numPoints;i++)
{
attributes->Velocity[i] = 0;
attributes->Velocity[i+numPoints] = attributes->GridPtr->Points[i*3+1]*time;
attributes->Velocity[i+2*numPoints] = 0;
}
unsigned int numCells = attributes->GridPtr->NumberOfCells;
if(attributes->Pressure != 0)
{
free(attributes->Pressure);
}
attributes->Pressure = (float*) malloc(sizeof(float)*numCells);
for(i=0;i<numCells;i++)
{
attributes->Pressure[i] = 0;
}
}

View File

@ -0,0 +1,30 @@
#ifndef FEDATASTRUCTURES_HEADER
#define FEDATASTRUCTURES_HEADER
typedef struct Grid
{
unsigned int NumberOfPoints;
double* Points;
unsigned int NumberOfCells;
unsigned int* Cells;
} Grid;
void InitializeGrid(Grid* grid, const unsigned int numPoints[3], const double spacing[3]);
void FinalizeGrid(Grid*);
typedef struct Attributes
{
// A structure for generating and storing point and cell fields.
// Velocity is stored at the points and pressure is stored
// for the cells. The current velocity profile is for a
// shearing flow with U(y,t) = y*t, V = 0 and W = 0.
// Pressure is constant through the domain.
double* Velocity;
float* Pressure;
Grid* GridPtr;
} Attributes;
void InitializeAttributes(Attributes* attributes, Grid* grid);
void FinalizeAttributes(Attributes* attributes);
void UpdateFields(Attributes* attributes, double time);
#endif

View File

@ -0,0 +1,61 @@
#include "FEDataStructures.h"
#include <mpi.h>
#ifdef USE_CATALYST
#include "FEAdaptor.h"
#endif
// Example of a C adaptor for a simulation code
// where the simulation code has a fixed topology
// grid. We treat the grid as an unstructured
// grid even though in the example provided it
// would be best described as a vtkImageData.
// Also, the points are stored in an inconsistent
// manner with respect to the velocity vector.
// This is purposefully done to demonstrate
// the different approaches for getting data
// into Catalyst. In this example we don't
// use any of the Fortran/C API provided in
// Catalyst. That is in CFullExample2.
// Note that through configuration
// that the driver can be run without linking
// to Catalyst.
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
Grid grid = (Grid) { .NumberOfPoints = 0, .Points = 0, .NumberOfCells = 0, .Cells = 0};
unsigned int numPoints[3] = {70, 60, 44};
double spacing[3] = {1, 1.1, 1.3};
InitializeGrid(&grid, numPoints, spacing);
Attributes attributes;
InitializeAttributes(&attributes, &grid);
#ifdef USE_CATALYST
CatalystInitialize(argc, argv);
#endif
unsigned int numberOfTimeSteps = 100;
unsigned int timeStep;
for(timeStep=0;timeStep<numberOfTimeSteps;timeStep++)
{
// use a time step length of 0.1
double time = timeStep * 0.1;
UpdateFields(&attributes, time);
#ifdef USE_CATALYST
int lastTimeStep = 0;
if(timeStep == numberOfTimeSteps-1)
{
lastTimeStep = 1;
}
CatalystCoProcess(grid.NumberOfPoints, grid.Points, grid.NumberOfCells, grid.Cells,
attributes.Velocity, attributes.Pressure, time, timeStep, lastTimeStep);
#endif
}
#ifdef USE_CATALYST
CatalystFinalize();
#endif
MPI_Finalize();
return 0;
}

View File

@ -0,0 +1,87 @@
try: paraview.simple
except: from paraview.simple import *
from paraview import coprocessing
#--------------------------------------------------------------
# Code generated from cpstate.py to create the CoProcessor.
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
filename_3_pvtu = coprocessor.CreateProducer( datadescription, "input" )
Slice1 = Slice( guiName="Slice1", Crinkleslice=0, SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane" )
Slice1.SliceType.Offset = 0.0
Slice1.SliceType.Origin = [34.5, 32.45, 27.95]
Slice1.SliceType.Normal = [1.0, 0.0, 0.0]
SetActiveSource(filename_3_pvtu)
ParallelUnstructuredGridWriter1 = coprocessor.CreateWriter( XMLPUnstructuredGridWriter, "fullgrid_%t.pvtu", 100 )
SetActiveSource(Slice1)
ParallelPolyDataWriter1 = coprocessor.CreateWriter( XMLPPolyDataWriter, "slice_%t.pvtp", 10 )
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [10, 100]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(True)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystCFullExample2 C)
set(USE_CATALYST ON CACHE BOOL "Link the simulator with Catalyst")
if(USE_CATALYST)
# we only need C++ compilers if we're building the Adaptor
enable_language(CXX)
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
set(Adaptor_SRCS
FEAdaptor.cxx
)
add_library(CFullExample2Adaptor ${Adaptor_SRCS})
target_link_libraries(CFullExample2Adaptor vtkPVPythonCatalyst)
add_definitions("-DUSE_CATALYST")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
else()
find_package(MPI REQUIRED)
include_directories(${MPI_C_INCLUDE_PATH})
endif()
add_executable(CFullExample2 FEDriver.c FEDataStructures.c)
if(USE_CATALYST)
target_link_libraries(CFullExample2 LINK_PRIVATE CFullExample2Adaptor)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(CFullExample2)
else()
target_link_libraries(CFullExample2 LINK_PRIVATE ${MPI_LIBRARIES})
endif()
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
add_test(NAME CFullExample2Test COMMAND CFullExample2 ${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/feslicescript.py)
set_tests_properties(CFullExample2Test PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,135 @@
#include <iostream>
#include "FEAdaptor.h"
#include <vtkCellData.h>
#include <vtkCellType.h>
#include <vtkCPAdaptorAPI.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkCPPythonScriptPipeline.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkPointData.h>
#include <vtkSmartPointer.h>
#include <vtkUnstructuredGrid.h>
namespace
{
vtkSmartPointer<vtkUnstructuredGrid> VTKGrid;
void BuildVTKGrid(unsigned int numberOfPoints, double* pointsData,
unsigned int numberOfCells, unsigned int* cellsData)
{
// create the points information
vtkNew<vtkDoubleArray> pointArray;
pointArray->SetNumberOfComponents(3);
pointArray->SetArray(pointsData, static_cast<vtkIdType>(numberOfPoints*3), 1);
vtkNew<vtkPoints> points;
points->SetData(pointArray.GetPointer());
VTKGrid->SetPoints(points.GetPointer());
// create the cells
VTKGrid->Allocate(static_cast<vtkIdType>(numberOfCells*9));
for(unsigned int cell=0;cell<numberOfCells;cell++)
{
unsigned int* cellPoints = cellsData+8*cell;
vtkIdType tmp[8] = {cellPoints[0], cellPoints[1], cellPoints[2], cellPoints[3],
cellPoints[4], cellPoints[5], cellPoints[6], cellPoints[7]};
VTKGrid->InsertNextCell(VTK_HEXAHEDRON, 8, tmp);
}
}
void UpdateVTKAttributes(unsigned int numberOfPoints, double* velocityData,
unsigned int numberOfCells, float* pressureData)
{
if(VTKGrid->GetPointData()->GetNumberOfArrays() == 0)
{
// velocity array
vtkNew<vtkDoubleArray> velocity;
velocity->SetName("velocity");
velocity->SetNumberOfComponents(3);
velocity->SetNumberOfTuples(static_cast<vtkIdType>(numberOfPoints));
VTKGrid->GetPointData()->AddArray(velocity.GetPointer());
}
if(VTKGrid->GetCellData()->GetNumberOfArrays() == 0)
{
// pressure array
vtkNew<vtkFloatArray> pressure;
pressure->SetName("pressure");
pressure->SetNumberOfComponents(1);
VTKGrid->GetCellData()->AddArray(pressure.GetPointer());
}
vtkDoubleArray* velocity = vtkDoubleArray::SafeDownCast(
VTKGrid->GetPointData()->GetArray("velocity"));
// The velocity array is ordered as vx0,vx1,vx2,..,vy0,vy1,vy2,..,vz0,vz1,vz2,..
// so we need to create a full copy of it with VTK's ordering of
// vx0,vy0,vz0,vx1,vy1,vz1,..
for(unsigned int i=0;i<numberOfPoints;i++)
{
double values[3] = {velocityData[i], velocityData[i+numberOfPoints],
velocityData[i+2*numberOfPoints]};
velocity->SetTupleValue(i, values);
}
vtkFloatArray* pressure = vtkFloatArray::SafeDownCast(
VTKGrid->GetCellData()->GetArray("pressure"));
// The pressure array is a scalar array so we can reuse
// memory as long as we ordered the points properly.
pressure->SetArray(pressureData, static_cast<vtkIdType>(numberOfCells), 1);
}
void BuildVTKDataStructures(unsigned int numberOfPoints, double* points, double* velocity,
unsigned int numberOfCells, unsigned int* cells, float* pressure)
{
if(VTKGrid == NULL)
{
// The grid structure isn't changing so we only build it
// the first time it's needed. If we needed the memory
// we could delete it and rebuild as necessary.
VTKGrid = vtkSmartPointer<vtkUnstructuredGrid>::New();
BuildVTKGrid(numberOfPoints, points, numberOfCells, cells);
}
UpdateVTKAttributes(numberOfPoints, velocity, numberOfCells, pressure);
}
}
void CatalystCoProcess(unsigned int numberOfPoints, double* pointsData,
unsigned int numberOfCells, unsigned int* cellsData,
double* velocityData, float* pressureData, double time,
unsigned int timeStep, int lastTimeStep)
{
vtkCPProcessor* processor = vtkCPAdaptorAPI::GetCoProcessor();
vtkCPDataDescription* dataDescription = vtkCPAdaptorAPI::GetCoProcessorData();
if(processor == NULL || dataDescription == NULL)
{
cerr << "ERROR: Catalyst not properly initialized.\n";
return;
}
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(processor->RequestDataDescription(dataDescription) != 0)
{
BuildVTKDataStructures(numberOfPoints, pointsData, velocityData,
numberOfCells, cellsData, pressureData);
dataDescription->GetInputDescriptionByName("input")->SetGrid(VTKGrid);
processor->CoProcess(dataDescription);
}
}
void CatalystFinalize()
{ // Used to free grid if it was used
if(VTKGrid)
{
VTKGrid = NULL;
}
}

View File

@ -0,0 +1,16 @@
#ifndef FEADAPTOR_HEADER
#define FEADAPTOR_HEADER
#ifdef __cplusplus
extern "C"
{
#endif
void CatalystCoProcess(unsigned int numberOfPoints, double* pointsData,
unsigned int numberOfCells, unsigned int* cellsDAta,
double* velocityData, float* pressureData, double time,
unsigned int timeStep, int lastTimeStep);
void CatalystFinalize();
#ifdef __cplusplus
}
#endif
#endif

View File

@ -0,0 +1,137 @@
#include "FEDataStructures.h"
#include <mpi.h>
#include <stdio.h>
#include <stdlib.h>
void InitializeGrid(Grid* grid, const unsigned int numPoints[3], const double spacing[3] )
{
grid->NumberOfPoints = 0;
grid->Points = 0;
grid->NumberOfCells = 0;
grid->Cells = 0;
if(numPoints[0] == 0 || numPoints[1] == 0 || numPoints[2] == 0)
{
printf("Must have a non-zero amount of points in each direction.\n");
}
// in parallel, we do a simple partitioning in the x-direction.
int mpiSize = 1;
int mpiRank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
unsigned int startXPoint = mpiRank*numPoints[0]/mpiSize;
unsigned int endXPoint = (mpiRank+1)*numPoints[0]/mpiSize;
if(mpiSize != mpiRank+1)
{
endXPoint++;
}
// create the points -- slowest in the x and fastest in the z directions
if(grid->Points != 0)
{
free(grid->Points);
}
unsigned int numXPoints = endXPoint - startXPoint;
grid->Points = (double*) malloc(3*sizeof(double)*numPoints[1]*numPoints[2]*numXPoints);
unsigned int counter = 0;
unsigned int i, j, k;
for(i=startXPoint;i<endXPoint;i++)
{
for(j=0;j<numPoints[1];j++)
{
for(k=0;k<numPoints[2];k++)
{
grid->Points[counter] = i*spacing[0];
grid->Points[counter+1] = j*spacing[1];
grid->Points[counter+2] = k*spacing[2];
counter += 3;
}
}
}
grid->NumberOfPoints = numPoints[1]*numPoints[2]*numXPoints;
// create the hex cells
if(grid->Cells != 0)
{
free(grid->Cells);
}
grid->Cells = (unsigned int*) malloc(8*sizeof(unsigned int)*(numPoints[1]-1)*(numPoints[2]-1)*(numXPoints-1));
counter = 0;
for(i=0;i<numXPoints-1;i++)
{
for(j=0;j<numPoints[1]-1;j++)
{
for(k=0;k<numPoints[2]-1;k++)
{
grid->Cells[counter] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
grid->Cells[counter+1] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
grid->Cells[counter+2] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
grid->Cells[counter+3] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
grid->Cells[counter+4] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
grid->Cells[counter+5] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
grid->Cells[counter+6] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
grid->Cells[counter+7] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
counter += 8;
}
}
}
grid->NumberOfCells = (numPoints[1]-1)*(numPoints[2]-1)*(numXPoints-1);
}
void FinalizeGrid(Grid* grid)
{
if(grid->Points)
{
free(grid->Points);
grid->Points = 0;
}
if(grid->Cells)
{
free(grid->Cells);
grid->Cells = 0;
}
grid->NumberOfPoints = 0;
grid->NumberOfCells = 0;
}
void InitializeAttributes(Attributes* attributes, Grid* grid)
{
attributes->GridPtr = grid;
attributes->Velocity = 0;
attributes->Pressure = 0;
}
void UpdateFields(Attributes* attributes, double time)
{
unsigned int numPoints = attributes->GridPtr->NumberOfPoints;
if(attributes->Velocity != 0)
{
free(attributes->Velocity);
}
attributes->Velocity = (double*) malloc(sizeof(double)*numPoints*3);
unsigned int i;
for(i=0;i<numPoints;i++)
{
attributes->Velocity[i] = 0;
attributes->Velocity[i+numPoints] = attributes->GridPtr->Points[i*3+1]*time;
attributes->Velocity[i+2*numPoints] = 0;
}
unsigned int numCells = attributes->GridPtr->NumberOfCells;
if(attributes->Pressure != 0)
{
free(attributes->Pressure);
}
attributes->Pressure = (float*) malloc(sizeof(float)*numCells);
for(i=0;i<numCells;i++)
{
attributes->Pressure[i] = 0;
}
}

View File

@ -0,0 +1,30 @@
#ifndef FEDATASTRUCTURES_HEADER
#define FEDATASTRUCTURES_HEADER
typedef struct Grid
{
unsigned int NumberOfPoints;
double* Points;
unsigned int NumberOfCells;
unsigned int* Cells;
} Grid;
void InitializeGrid(Grid* grid, const unsigned int numPoints[3], const double spacing[3]);
void FinalizeGrid(Grid*);
typedef struct Attributes
{
// A structure for generating and storing point and cell fields.
// Velocity is stored at the points and pressure is stored
// for the cells. The current velocity profile is for a
// shearing flow with U(y,t) = y*t, V = 0 and W = 0.
// Pressure is constant through the domain.
double* Velocity;
float* Pressure;
Grid* GridPtr;
} Attributes;
void InitializeAttributes(Attributes* attributes, Grid* grid);
void FinalizeAttributes(Attributes* attributes);
void UpdateFields(Attributes* attributes, double time);
#endif

View File

@ -0,0 +1,82 @@
#include "FEDataStructures.h"
#include <mpi.h>
#ifdef USE_CATALYST
#include "FEAdaptor.h"
#include <CPythonAdaptorAPI.h>
#include <stdio.h>
#include <string.h>
#endif
// Example of a C adaptor for a simulation code
// where the simulation code has a fixed topology
// grid. We treat the grid as an unstructured
// grid even though in the example provided it
// would be best described as a vtkImageData.
// Also, the points are stored in an inconsistent
// manner with respect to the velocity vector.
// This is purposefully done to demonstrate
// the different approaches for getting data
// into Catalyst. In this example we use
// some of the API in CPythonAdaptorAPI.h
// to assist in setting the problem up.
// CFullExample does essentially the same
// thing but without using the existing
// helper API. Note that through configuration
// that the driver can be run without linking
// to Catalyst.
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
Grid grid = (Grid) { .NumberOfPoints = 0, .Points = 0, .NumberOfCells = 0, .Cells = 0};
unsigned int numPoints[3] = {70, 60, 44};
double spacing[3] = {1, 1.1, 1.3};
InitializeGrid(&grid, numPoints, spacing);
Attributes attributes;
InitializeAttributes(&attributes, &grid);
#ifdef USE_CATALYST
int fileNameLength = 0;
if(argc < 2)
{
printf("Must pass in a Catalyst script\n");
MPI_Finalize();
return 1;
}
fileNameLength = strlen(argv[1]);
coprocessorinitializewithpython(argv[1], &fileNameLength);
int i;
for(i=2;i<argc;i++)
{
// Add in any other Python script pipelines that are passed in.
fileNameLength = strlen(argv[i]);
coprocessoraddpythonscript(argv[i], &fileNameLength);
}
#endif
unsigned int numberOfTimeSteps = 100;
unsigned int timeStep;
for(timeStep=0;timeStep<numberOfTimeSteps;timeStep++)
{
// use a time step length of 0.1
double time = timeStep * 0.1;
UpdateFields(&attributes, time);
#ifdef USE_CATALYST
int lastTimeStep = 0;
if(timeStep == numberOfTimeSteps-1)
{
lastTimeStep = 1;
}
CatalystCoProcess(grid.NumberOfPoints, grid.Points, grid.NumberOfCells, grid.Cells,
attributes.Velocity, attributes.Pressure, time, timeStep, lastTimeStep);
#endif
}
#ifdef USE_CATALYST
CatalystFinalize();
coprocessorfinalize();
#endif
MPI_Finalize();
return 0;
}

View File

@ -0,0 +1,87 @@
try: paraview.simple
except: from paraview.simple import *
from paraview import coprocessing
#--------------------------------------------------------------
# Code generated from cpstate.py to create the CoProcessor.
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
filename_3_pvtu = coprocessor.CreateProducer( datadescription, "input" )
Slice1 = Slice( guiName="Slice1", Crinkleslice=0, SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane" )
Slice1.SliceType.Offset = 0.0
Slice1.SliceType.Origin = [34.5, 32.45, 27.95]
Slice1.SliceType.Normal = [1.0, 0.0, 0.0]
SetActiveSource(filename_3_pvtu)
ParallelUnstructuredGridWriter1 = coprocessor.CreateWriter( XMLPUnstructuredGridWriter, "fullgrid_%t.pvtu", 100 )
SetActiveSource(Slice1)
ParallelPolyDataWriter1 = coprocessor.CreateWriter( XMLPPolyDataWriter, "slice_%t.pvtp", 10 )
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [10, 100]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(True)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,50 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystExamples CXX C)
set(USE_CATALYST ON CACHE BOOL "Link the simulator with Catalyst")
if(USE_CATALYST)
enable_language(CXX)
find_package(ParaView 4.2 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
add_definitions("-DUSE_CATALYST")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
else()
find_package(MPI REQUIRED)
include_directories(${MPI_C_INCLUDE_PATH})
endif()
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
endif()
add_subdirectory(CxxOverlappingAMRExample)
add_subdirectory(CxxNonOverlappingAMRExample)
add_subdirectory(CFullExample2)
add_subdirectory(CxxFullExample)
add_subdirectory(CFullExample)
add_subdirectory(CxxVTKPipelineExample)
add_subdirectory(CxxImageDataExample)
add_subdirectory(CxxMultiPieceExample)
add_subdirectory(CxxPVSMPipelineExample)
add_subdirectory(CxxMappedDataArrayExample)
add_subdirectory(MPISubCommunicatorExample)
add_subdirectory(PythonDolfinExample)
add_subdirectory(CxxParticlePathExample)
set(BUILD_FORTRAN_EXAMPLES OFF CACHE BOOL "Build Fortran Catalyst Examples")
if(BUILD_FORTRAN_EXAMPLES)
# Theoretically, CheckFortran should not be needed, but
# enable_language(OPTIONAL) fails with Ninja generator.
include(CheckFortran)
if(CMAKE_Fortran_COMPILER)
enable_language(Fortran OPTIONAL)
endif()
if(CMAKE_Fortran_COMPILER_WORKS)
add_subdirectory(Fortran90FullExample)
add_subdirectory(FortranPoissonSolver)
endif()
endif()

View File

@ -0,0 +1,40 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystCxxFullExample)
set(USE_CATALYST ON CACHE BOOL "Link the simulator with Catalyst")
if(USE_CATALYST)
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
set(Adaptor_SRCS
FEAdaptor.cxx
)
add_library(CxxFullExampleAdaptor ${Adaptor_SRCS})
target_link_libraries(CxxFullExampleAdaptor vtkPVPythonCatalyst vtkParallelMPI)
add_definitions("-DUSE_CATALYST")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
else()
find_package(MPI REQUIRED)
include_directories(${MPI_C_INCLUDE_PATH})
endif()
add_executable(CxxFullExample FEDriver.cxx FEDataStructures.cxx)
if(USE_CATALYST)
target_link_libraries(CxxFullExample LINK_PRIVATE CxxFullExampleAdaptor)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(CxxFullExample)
else()
target_link_libraries(CxxFullExample LINK_PRIVATE ${MPI_LIBRARIES})
endif()
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
add_test(NAME CxxFullExampleTest COMMAND CxxFullExample ${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/feslicescript.py)
set_tests_properties(CxxFullExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
add_test(NAME CxxFullExampleCinemaTest COMMAND CxxFullExample ${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/feslicecinema.py)
set_tests_properties(CxxFullExampleCinemaTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,155 @@
#include <iostream>
#include "FEAdaptor.h"
#include "FEDataStructures.h"
#include <vtkCellData.h>
#include <vtkCellType.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkCPPythonScriptPipeline.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkPointData.h>
#include <vtkUnstructuredGrid.h>
namespace
{
vtkCPProcessor* Processor = NULL;
vtkUnstructuredGrid* VTKGrid;
void BuildVTKGrid(Grid& grid)
{
// create the points information
vtkNew<vtkDoubleArray> pointArray;
pointArray->SetNumberOfComponents(3);
pointArray->SetArray(grid.GetPointsArray(), static_cast<vtkIdType>(grid.GetNumberOfPoints()*3), 1);
vtkNew<vtkPoints> points;
points->SetData(pointArray.GetPointer());
VTKGrid->SetPoints(points.GetPointer());
// create the cells
size_t numCells = grid.GetNumberOfCells();
VTKGrid->Allocate(static_cast<vtkIdType>(numCells*9));
for(size_t cell=0;cell<numCells;cell++)
{
unsigned int* cellPoints = grid.GetCellPoints(cell);
vtkIdType tmp[8] = {cellPoints[0], cellPoints[1], cellPoints[2], cellPoints[3],
cellPoints[4], cellPoints[5], cellPoints[6], cellPoints[7]};
VTKGrid->InsertNextCell(VTK_HEXAHEDRON, 8, tmp);
}
}
void UpdateVTKAttributes(Grid& grid, Attributes& attributes)
{
if(VTKGrid->GetPointData()->GetNumberOfArrays() == 0)
{
// velocity array
vtkNew<vtkDoubleArray> velocity;
velocity->SetName("velocity");
velocity->SetNumberOfComponents(3);
velocity->SetNumberOfTuples(static_cast<vtkIdType>(grid.GetNumberOfPoints()));
VTKGrid->GetPointData()->AddArray(velocity.GetPointer());
}
if(VTKGrid->GetCellData()->GetNumberOfArrays() == 0)
{
// pressure array
vtkNew<vtkFloatArray> pressure;
pressure->SetName("pressure");
pressure->SetNumberOfComponents(1);
VTKGrid->GetCellData()->AddArray(pressure.GetPointer());
}
vtkDoubleArray* velocity = vtkDoubleArray::SafeDownCast(
VTKGrid->GetPointData()->GetArray("velocity"));
// The velocity array is ordered as vx0,vx1,vx2,..,vy0,vy1,vy2,..,vz0,vz1,vz2,..
// so we need to create a full copy of it with VTK's ordering of
// vx0,vy0,vz0,vx1,vy1,vz1,..
double* velocityData = attributes.GetVelocityArray();
vtkIdType numTuples = velocity->GetNumberOfTuples();
for(vtkIdType i=0;i<numTuples;i++)
{
double values[3] = {velocityData[i], velocityData[i+numTuples],
velocityData[i+2*numTuples]};
velocity->SetTupleValue(i, values);
}
vtkFloatArray* pressure = vtkFloatArray::SafeDownCast(
VTKGrid->GetCellData()->GetArray("pressure"));
// The pressure array is a scalar array so we can reuse
// memory as long as we ordered the points properly.
float* pressureData = attributes.GetPressureArray();
pressure->SetArray(pressureData, static_cast<vtkIdType>(grid.GetNumberOfCells()), 1);
}
void BuildVTKDataStructures(Grid& grid, Attributes& attributes)
{
if(VTKGrid == NULL)
{
// The grid structure isn't changing so we only build it
// the first time it's needed. If we needed the memory
// we could delete it and rebuild as necessary.
VTKGrid = vtkUnstructuredGrid::New();
BuildVTKGrid(grid);
}
UpdateVTKAttributes(grid, attributes);
}
}
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[])
{
if(Processor == NULL)
{
Processor = vtkCPProcessor::New();
Processor->Initialize();
}
else
{
Processor->RemoveAllPipelines();
}
for(int i=1;i<numScripts;i++)
{
vtkNew<vtkCPPythonScriptPipeline> pipeline;
pipeline->Initialize(scripts[i]);
Processor->AddPipeline(pipeline.GetPointer());
}
}
void Finalize()
{
if(Processor)
{
Processor->Delete();
Processor = NULL;
}
if(VTKGrid)
{
VTKGrid->Delete();
VTKGrid = NULL;
}
}
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep)
{
vtkNew<vtkCPDataDescription> dataDescription;
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(Processor->RequestDataDescription(dataDescription.GetPointer()) != 0)
{
BuildVTKDataStructures(grid, attributes);
dataDescription->GetInputDescriptionByName("input")->SetGrid(VTKGrid);
Processor->CoProcess(dataDescription.GetPointer());
}
}
} // end of Catalyst namespace

View File

@ -0,0 +1,17 @@
#ifndef FEADAPTOR_HEADER
#define FEADAPTOR_HEADER
class Attributes;
class Grid;
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[]);
void Finalize();
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep);
}
#endif

View File

@ -0,0 +1,153 @@
#include "FEDataStructures.h"
#include <mpi.h>
#include <iostream>
Grid::Grid()
{}
void Grid::Initialize(const unsigned int numPoints[3], const double spacing[3] )
{
if(numPoints[0] == 0 || numPoints[1] == 0 || numPoints[2] == 0)
{
std::cerr << "Must have a non-zero amount of points in each direction.\n";
}
// in parallel, we do a simple partitioning in the x-direction.
int mpiSize = 1;
int mpiRank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
unsigned int startXPoint = mpiRank*numPoints[0]/mpiSize;
unsigned int endXPoint = (mpiRank+1)*numPoints[0]/mpiSize;
if(mpiSize != mpiRank+1)
{
endXPoint++;
}
// create the points -- slowest in the x and fastest in the z directions
double coord[3] = {0,0,0};
for(unsigned int i=startXPoint;i<endXPoint;i++)
{
coord[0] = i*spacing[0];
for(unsigned int j=0;j<numPoints[1];j++)
{
coord[1] = j*spacing[1];
for(unsigned int k=0;k<numPoints[2];k++)
{
coord[2] = k*spacing[2];
// add the coordinate to the end of the vector
std::copy(coord, coord+3, std::back_inserter(this->Points));
}
}
}
// create the hex cells
unsigned int cellPoints[8];
unsigned int numXPoints = endXPoint - startXPoint;
for(unsigned int i=0;i<numXPoints-1;i++)
{
for(unsigned int j=0;j<numPoints[1]-1;j++)
{
for(unsigned int k=0;k<numPoints[2]-1;k++)
{
cellPoints[0] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
cellPoints[1] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
cellPoints[2] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
cellPoints[3] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
cellPoints[4] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
cellPoints[5] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
cellPoints[6] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
cellPoints[7] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
std::copy(cellPoints, cellPoints+8, std::back_inserter(this->Cells));
}
}
}
}
size_t Grid::GetNumberOfPoints()
{
return this->Points.size()/3;
}
size_t Grid::GetNumberOfCells()
{
return this->Cells.size()/8;
}
double* Grid::GetPointsArray()
{
if(this->Points.empty())
{
return NULL;
}
return &(this->Points[0]);
}
double* Grid::GetPoint(size_t pointId)
{
if(pointId >= this->Points.size())
{
return NULL;
}
return &(this->Points[pointId*3]);
}
unsigned int* Grid::GetCellPoints(size_t cellId)
{
if(cellId >= this->Cells.size())
{
return NULL;
}
return &(this->Cells[cellId*8]);
}
Attributes::Attributes()
{
this->GridPtr = NULL;
}
void Attributes::Initialize(Grid* grid)
{
this->GridPtr = grid;
}
void Attributes::UpdateFields(double time)
{
size_t numPoints = this->GridPtr->GetNumberOfPoints();
this->Velocity.resize(numPoints*3);
for(size_t pt=0;pt<numPoints;pt++)
{
double* coord = this->GridPtr->GetPoint(pt);
this->Velocity[pt] = coord[1]*time;
}
std::fill(this->Velocity.begin()+numPoints, this->Velocity.end(), 0.);
size_t numCells = this->GridPtr->GetNumberOfCells();
this->Pressure.resize(numCells);
std::fill(this->Pressure.begin(), this->Pressure.end(), 1.);
}
double* Attributes::GetVelocityArray()
{
if(this->Velocity.empty())
{
return NULL;
}
return &this->Velocity[0];
}
float* Attributes::GetPressureArray()
{
if(this->Pressure.empty())
{
return NULL;
}
return &this->Pressure[0];
}

View File

@ -0,0 +1,41 @@
#ifndef FEDATASTRUCTURES_HEADER
#define FEDATASTRUCTURES_HEADER
#include <cstddef>
#include <vector>
class Grid
{
public:
Grid();
void Initialize(const unsigned int numPoints[3], const double spacing[3]);
size_t GetNumberOfPoints();
size_t GetNumberOfCells();
double* GetPointsArray();
double* GetPoint(size_t pointId);
unsigned int* GetCellPoints(size_t cellId);
private:
std::vector<double> Points;
std::vector<unsigned int> Cells;
};
class Attributes
{
// A class for generating and storing point and cell fields.
// Velocity is stored at the points and pressure is stored
// for the cells. The current velocity profile is for a
// shearing flow with U(y,t) = y*t, V = 0 and W = 0.
// Pressure is constant through the domain.
public:
Attributes();
void Initialize(Grid* grid);
void UpdateFields(double time);
double* GetVelocityArray();
float* GetPressureArray();
private:
std::vector<double> Velocity;
std::vector<float> Pressure;
Grid* GridPtr;
};
#endif

View File

@ -0,0 +1,51 @@
#include "FEDataStructures.h"
#include <mpi.h>
#ifdef USE_CATALYST
#include "FEAdaptor.h"
#endif
// Example of a C++ adaptor for a simulation code
// where the simulation code has a fixed topology
// grid. We treat the grid as an unstructured
// grid even though in the example provided it
// would be best described as a vtkImageData.
// Also, the points are stored in an inconsistent
// manner with respect to the velocity vector.
// This is purposefully done to demonstrate
// the different approaches for getting data
// into Catalyst. Note that through configuration
// that the driver can be run without linking
// to Catalyst.
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
Grid grid;
unsigned int numPoints[3] = {70, 60, 44};
double spacing[3] = {1, 1.1, 1.3};
grid.Initialize(numPoints, spacing);
Attributes attributes;
attributes.Initialize(&grid);
#ifdef USE_CATALYST
FEAdaptor::Initialize(argc, argv);
#endif
unsigned int numberOfTimeSteps = 100;
for(unsigned int timeStep=0;timeStep<numberOfTimeSteps;timeStep++)
{
// use a time step length of 0.1
double time = timeStep * 0.1;
attributes.UpdateFields(time);
#ifdef USE_CATALYST
FEAdaptor::CoProcess(grid, attributes, time, timeStep, timeStep == numberOfTimeSteps-1);
#endif
}
#ifdef USE_CATALYST
FEAdaptor::Finalize();
#endif
MPI_Finalize();
return 0;
}

View File

@ -0,0 +1,189 @@
from paraview.simple import *
from paraview import coprocessing
# --------------------- Cinema exporter definition ---------------------
def CreateCinemaExporter(input, frequency):
from paraview import simple
from paraview import data_exploration as cinema
class FullAnalysis(object):
def __init__(self):
self.center_of_rotation = [34.5, 32.45, 27.95]
self.rotation_axis = [0.0, 0.0, 1.0]
self.distance = 500.0
self.exporters = []
self.analysis = cinema.AnalysisManager( 'cinema', "Cinema", "Test various cinema exporter.")
self.analysis.begin()
min = 0.0
max = 642.0
self.lut = simple.GetColorTransferFunction(
"velocity",
RGBPoints=[min, 0.23, 0.299, 0.754, (min+max)*0.5, 0.865, 0.865, 0.865, max, 0.706, 0.016, 0.15])
# ==
self.createSliceExporter()
self.createComposite()
self.createImageResampler()
self.simple360()
def createSliceExporter(self):
self.analysis.register_analysis(
"slice", # id
"Slice exploration", # title
"Perform 10 slice along X", # description
"{time}/{sliceColor}_{slicePosition}.jpg", # data structure
cinema.SliceExplorer.get_data_type())
nb_slices = 5
colorByArray = { "velocity": { "lut": self.lut , "type": 'POINT_DATA'} }
view = simple.CreateRenderView()
fng = self.analysis.get_file_name_generator("slice")
exporter = cinema.SliceExplorer(fng, view, input, colorByArray, nb_slices)
exporter.set_analysis(self.analysis)
self.exporters.append(exporter)
def createComposite(self):
try:
simple.LoadDistributedPlugin("RGBZView", ns=globals())
self.analysis.register_analysis(
"composite",
"Composite rendering",
"Performing composite on contour",
'{time}/{theta}/{phi}/{filename}', cinema.CompositeImageExporter.get_data_type())
fng = self.analysis.get_file_name_generator("composite")
# Create pipeline to compose
color_type = [('POINT_DATA', "velocity")]
luts = { "velocity": self.lut }
filters = [ input ]
filters_description = [ {'name': 'catalyst'} ]
color_by = [ color_type ]
# Data exploration ------------------------------------------------------------
camera_handler = cinema.ThreeSixtyCameraHandler(fng, None, [ float(r) for r in range(0, 360, 72)], [ float(r) for r in range(-60, 61, 45)], self.center_of_rotation, self.rotation_axis, self.distance)
exporter = cinema.CompositeImageExporter(fng, filters, color_by, luts, camera_handler, [400,400], filters_description, 0, 0)
exporter.set_analysis(self.analysis)
self.exporters.append(exporter)
except:
print "Skip RGBZView exporter"
def createImageResampler(self):
self.analysis.register_analysis(
"interactive-prober", # id
"Interactive prober", # title
"Sample data in image stack for line probing", # description
"{time}/{field}/{slice}.{format}", # data structure
cinema.ImageResampler.get_data_type())
fng = self.analysis.get_file_name_generator("interactive-prober")
arrays = { "velocity" : self.lut }
exporter = cinema.ImageResampler(fng, input, [50,50,50], arrays)
self.exporters.append(exporter)
def simple360(self):
self.analysis.register_analysis(
"360", # id
"rotation", # title
"Perform 15 contour", # description
"{time}/{theta}_{phi}.jpg", # data structure
cinema.ThreeSixtyImageStackExporter.get_data_type())
fng = self.analysis.get_file_name_generator("360")
arrayName = ('POINT_DATA', 'velocity')
view = simple.CreateRenderView()
rep = simple.Show(input, view)
rep.LookupTable = self.lut
rep.ColorArrayName = arrayName
exporter = cinema.ThreeSixtyImageStackExporter(fng, view, self.center_of_rotation, self.distance, self.rotation_axis, [20,45])
self.exporters.append(exporter)
def UpdatePipeline(self, time):
if time % frequency != 0:
return
# Do the exploration work
for exporter in self.exporters:
exporter.UpdatePipeline(time)
def Finalize(self):
self.analysis.end()
return FullAnalysis()
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
filename_3_pvtu = coprocessor.CreateProducer( datadescription, "input" )
Slice1 = Slice( guiName="Slice1", Crinkleslice=0, SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane" )
Slice1.SliceType.Offset = 0.0
Slice1.SliceType.Origin = [34.5, 32.45, 27.95]
Slice1.SliceType.Normal = [1.0, 0.0, 0.0]
ParallelPolyDataWriter1 = coprocessor.CreateWriter( XMLPPolyDataWriter, "slice_%t.pvtp", 10 )
# Add cinema exploration
# coprocessor.RegisterExporter(CreateCinemaExporter(filename_3_pvtu, 2)) acbauer -- this should be the one to use
CreateCinemaExporter(filename_3_pvtu, 2)
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [10, 100]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(False)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,86 @@
try: paraview.simple
except: from paraview.simple import *
from paraview import coprocessing
#--------------------------------------------------------------
# Code generated from cpstate.py to create the CoProcessor.
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
filename_3_pvtu = coprocessor.CreateProducer( datadescription, "input" )
Slice1 = Slice( guiName="Slice1", Crinkleslice=0, SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane" )
Slice1.SliceType.Offset = 0.0
Slice1.SliceType.Origin = [34.5, 32.45, 27.95]
Slice1.SliceType.Normal = [1.0, 0.0, 0.0]
ParallelPolyDataWriter1 = coprocessor.CreateWriter( XMLPPolyDataWriter, "slice_%t.pvtp", 10 )
SetActiveSource(filename_3_pvtu)
ParallelUnstructuredGridWriter1 = coprocessor.CreateWriter( XMLPUnstructuredGridWriter, "fullgrid_%t.pvtu", 100 )
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [10, 100]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(False)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,38 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystCxxImageDataExample)
set(USE_CATALYST ON CACHE BOOL "Link the simulator with Catalyst")
if(USE_CATALYST)
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
set(Adaptor_SRCS
FEAdaptor.cxx
)
add_library(CxxImageDataExampleAdaptor ${Adaptor_SRCS})
target_link_libraries(CxxImageDataExampleAdaptor vtkPVPythonCatalyst)
add_definitions("-DUSE_CATALYST")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
else()
find_package(MPI REQUIRED)
include_directories(${MPI_CXX_INCLUDE_PATH})
endif()
add_executable(CxxImageDataExample FEDriver.cxx FEDataStructures.cxx)
if(USE_CATALYST)
target_link_libraries(CxxImageDataExample LINK_PRIVATE CxxImageDataExampleAdaptor)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(CxxImageDataExample)
else()
target_link_libraries(CxxImageDataExample LINK_PRIVATE ${MPI_LIBRARIES})
endif()
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
add_test(NAME CxxImageDataExampleTest COMMAND CxxImageDataExample ${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/feslicescript.py)
set_tests_properties(CxxImageDataExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,151 @@
#include <iostream>
#include "FEAdaptor.h"
#include "FEDataStructures.h"
#include <vtkCellData.h>
#include <vtkCellType.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkCPPythonScriptPipeline.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkImageData.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkPointData.h>
namespace
{
vtkCPProcessor* Processor = NULL;
vtkImageData* VTKGrid = NULL;
void BuildVTKGrid(Grid& grid)
{
// The grid structure isn't changing so we only build it
// the first time it's needed. If we needed the memory
// we could delete it and rebuild as necessary.
if(VTKGrid == NULL)
{
VTKGrid = vtkImageData::New();
int extent[6];
for(int i=0;i<6;i++)
{
extent[i] = grid.GetExtent()[i];
}
VTKGrid->SetExtent(extent);
VTKGrid->SetSpacing(grid.GetSpacing());
}
}
void UpdateVTKAttributes(Grid& grid, Attributes& attributes)
{
if(VTKGrid->GetPointData()->GetNumberOfArrays() == 0)
{
// velocity array
vtkNew<vtkDoubleArray> velocity;
velocity->SetName("velocity");
velocity->SetNumberOfComponents(3);
velocity->SetNumberOfTuples(static_cast<vtkIdType>(grid.GetNumberOfLocalPoints()));
VTKGrid->GetPointData()->AddArray(velocity.GetPointer());
}
if(VTKGrid->GetCellData()->GetNumberOfArrays() == 0)
{
// pressure array
vtkNew<vtkFloatArray> pressure;
pressure->SetName("pressure");
pressure->SetNumberOfComponents(1);
VTKGrid->GetCellData()->AddArray(pressure.GetPointer());
}
vtkDoubleArray* velocity = vtkDoubleArray::SafeDownCast(
VTKGrid->GetPointData()->GetArray("velocity"));
// The velocity array is ordered as vx0,vx1,vx2,..,vy0,vy1,vy2,..,vz0,vz1,vz2,..
// so we need to create a full copy of it with VTK's ordering of
// vx0,vy0,vz0,vx1,vy1,vz1,..
double* velocityData = attributes.GetVelocityArray();
vtkIdType numTuples = velocity->GetNumberOfTuples();
for(vtkIdType i=0;i<numTuples;i++)
{
double values[3] = {velocityData[i], velocityData[i+numTuples],
velocityData[i+2*numTuples]};
velocity->SetTupleValue(i, values);
}
vtkFloatArray* pressure = vtkFloatArray::SafeDownCast(
VTKGrid->GetCellData()->GetArray("pressure"));
// The pressure array is a scalar array so we can reuse
// memory as long as we ordered the points properly.
float* pressureData = attributes.GetPressureArray();
pressure->SetArray(pressureData, static_cast<vtkIdType>(grid.GetNumberOfLocalCells()), 1);
}
void BuildVTKDataStructures(Grid& grid, Attributes& attributes)
{
BuildVTKGrid(grid);
UpdateVTKAttributes(grid, attributes);
}
}
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[])
{
if(Processor == NULL)
{
Processor = vtkCPProcessor::New();
Processor->Initialize();
}
else
{
Processor->RemoveAllPipelines();
}
for(int i=1;i<numScripts;i++)
{
vtkNew<vtkCPPythonScriptPipeline> pipeline;
pipeline->Initialize(scripts[i]);
Processor->AddPipeline(pipeline.GetPointer());
}
}
void Finalize()
{
if(Processor)
{
Processor->Delete();
Processor = NULL;
}
if(VTKGrid)
{
VTKGrid->Delete();
VTKGrid = NULL;
}
}
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep)
{
vtkNew<vtkCPDataDescription> dataDescription;
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(Processor->RequestDataDescription(dataDescription.GetPointer()) != 0)
{
BuildVTKDataStructures(grid, attributes);
dataDescription->GetInputDescriptionByName("input")->SetGrid(VTKGrid);
int wholeExtent[6];
for(int i=0;i<6;i++)
{
wholeExtent[i] = grid.GetNumPoints()[i];
}
dataDescription->GetInputDescriptionByName("input")->SetWholeExtent(wholeExtent);
Processor->CoProcess(dataDescription.GetPointer());
}
}
} // end of Catalyst namespace

View File

@ -0,0 +1,17 @@
#ifndef FEADAPTOR_HEADER
#define FEADAPTOR_HEADER
class Attributes;
class Grid;
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[]);
void Finalize();
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep);
}
#endif

View File

@ -0,0 +1,122 @@
#include "FEDataStructures.h"
#include <iostream>
#include <mpi.h>
#include <assert.h>
Grid::Grid()
{
this->NumPoints[0] = this->NumPoints[1] = this->NumPoints[2] = 0;
this->Spacing[0] = this->Spacing[1] = this->Spacing[2] = 0;
}
void Grid::Initialize(const unsigned int numPoints[3], const double spacing[3] )
{
if(numPoints[0] == 0 || numPoints[1] == 0 || numPoints[2] == 0)
{
std::cerr << "Must have a non-zero amount of points in each direction.\n";
}
for(int i=0;i<3;i++)
{
this->NumPoints[i] = numPoints[i];
this->Spacing[i] = spacing[i];
}
int mpiRank = 0, mpiSize = 1;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
this->Extent[0] = mpiRank*numPoints[0]/mpiSize;
this->Extent[1] = (mpiRank+1)*numPoints[0]/mpiSize;
if(mpiSize != mpiRank+1)
{
this->Extent[1]++;
}
this->Extent[2] = this->Extent[4] = 0;
this->Extent[3] = numPoints[1];
this->Extent[5] = numPoints[2];
}
unsigned int Grid::GetNumberOfLocalPoints()
{
return (this->Extent[1]-this->Extent[0]+1)*(this->Extent[3]-this->Extent[2]+1)*
(this->Extent[5]-this->Extent[4]+1);
}
unsigned int Grid::GetNumberOfLocalCells()
{
return (this->Extent[1]-this->Extent[0])*(this->Extent[3]-this->Extent[2])*
(this->Extent[5]-this->Extent[4]);
}
void Grid::GetLocalPoint(unsigned int pointId, double* point)
{
unsigned int logicalX = pointId%(this->Extent[1]-this->Extent[0]+1);
assert(logicalX <= this->Extent[1]);
point[0] = this->Spacing[0]*logicalX;
unsigned int logicalY = pointId%((this->Extent[1]-this->Extent[0]+1)*(this->Extent[3]-this->Extent[2]+1));
logicalY /= this->Extent[1]-this->Extent[0]+1;
assert(logicalY <= this->Extent[3]);
point[1] = this->Spacing[1]*logicalY;
unsigned int logicalZ = pointId/((this->Extent[1]-this->Extent[0]+1)*
(this->Extent[3]-this->Extent[2]+1));
assert(logicalZ <= this->Extent[5]);
point[2] = this->Spacing[2]*logicalZ;
}
unsigned int* Grid::GetNumPoints()
{
return this->NumPoints;
}
unsigned int* Grid::GetExtent()
{
return this->Extent;
}
double* Grid::GetSpacing()
{
return this->Spacing;
}
Attributes::Attributes()
{
this->GridPtr = NULL;
}
void Attributes::Initialize(Grid* grid)
{
this->GridPtr = grid;
}
void Attributes::UpdateFields(double time)
{
unsigned int numPoints = this->GridPtr->GetNumberOfLocalPoints();
this->Velocity.resize(numPoints*3);
for(unsigned int pt=0;pt<numPoints;pt++)
{
double coord[3];
this->GridPtr->GetLocalPoint(pt, coord);
this->Velocity[pt] = coord[1]*time;
}
std::fill(this->Velocity.begin()+numPoints, this->Velocity.end(), 0.);
unsigned int numCells = this->GridPtr->GetNumberOfLocalCells();
this->Pressure.resize(numCells);
std::fill(this->Pressure.begin(), this->Pressure.end(), 1.);
}
double* Attributes::GetVelocityArray()
{
if(this->Velocity.empty())
{
return NULL;
}
return &this->Velocity[0];
}
float* Attributes::GetPressureArray()
{
if(this->Pressure.empty())
{
return NULL;
}
return &this->Pressure[0];
}

View File

@ -0,0 +1,43 @@
#ifndef FEDATASTRUCTURES_HEADER
#define FEDATASTRUCTURES_HEADER
#include <cstddef>
#include <vector>
class Grid
{
public:
Grid();
void Initialize(const unsigned int numPoints[3], const double spacing[3]);
unsigned int GetNumberOfLocalPoints();
unsigned int GetNumberOfLocalCells();
void GetLocalPoint(unsigned int pointId, double* point);
unsigned int* GetNumPoints();
unsigned int* GetExtent();
double* GetSpacing();
private:
unsigned int NumPoints[3];
unsigned int Extent[6];
double Spacing[3];
};
class Attributes
{
// A class for generating and storing point and cell fields.
// Velocity is stored at the points and pressure is stored
// for the cells. The current velocity profile is for a
// shearing flow with U(y,t) = y*t, V = 0 and W = 0.
// Pressure is constant through the domain.
public:
Attributes();
void Initialize(Grid* grid);
void UpdateFields(double time);
double* GetVelocityArray();
float* GetPressureArray();
private:
std::vector<double> Velocity;
std::vector<float> Pressure;
Grid* GridPtr;
};
#endif

View File

@ -0,0 +1,51 @@
#include "FEDataStructures.h"
#include <mpi.h>
#ifdef USE_CATALYST
#include "FEAdaptor.h"
#endif
// Example of a C++ adaptor for a simulation code
// where the simulation code has a fixed topology
// grid. We treat the grid as an unstructured
// grid even though in the example provided it
// would be best described as a vtkImageData.
// Also, the points are stored in an inconsistent
// manner with respect to the velocity vector.
// This is purposefully done to demonstrate
// the different approaches for getting data
// into Catalyst. Note that through configuration
// that the driver can be run without linking
// to Catalyst.
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
Grid grid;
unsigned int numPoints[3] = {70, 60, 44};
double spacing[3] = {1, 1.1, 1.3};
grid.Initialize(numPoints, spacing);
Attributes attributes;
attributes.Initialize(&grid);
#ifdef USE_CATALYST
FEAdaptor::Initialize(argc, argv);
#endif
unsigned int numberOfTimeSteps = 100;
for(unsigned int timeStep=0;timeStep<numberOfTimeSteps;timeStep++)
{
// use a time step length of 0.1
double time = timeStep * 0.1;
attributes.UpdateFields(time);
#ifdef USE_CATALYST
FEAdaptor::CoProcess(grid, attributes, time, timeStep, timeStep == numberOfTimeSteps-1);
#endif
}
#ifdef USE_CATALYST
FEAdaptor::Finalize();
#endif
MPI_Finalize();
return 0;
}

View File

@ -0,0 +1,85 @@
try: paraview.simple
except: from paraview.simple import *
from paraview import coprocessing
#--------------------------------------------------------------
# Code generated from cpstate.py to create the CoProcessor.
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
filename_2_pvti = coprocessor.CreateProducer( datadescription, "input" )
ParallelImageDataWriter1 = coprocessor.CreateWriter( XMLPImageDataWriter, "fullgrid_%t.pvti", 100 )
SetActiveSource(filename_2_pvti)
Slice1 = Slice( guiName="Slice1", Crinkleslice=0, SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane" )
Slice1.SliceType.Offset = 0.0
Slice1.SliceType.Origin = [9.0, 33.0, 28.6]
Slice1.SliceType.Normal = [1.0, 0.0, 0.0]
ParallelPolyDataWriter1 = coprocessor.CreateWriter( XMLPPolyDataWriter, "slice_%t.pvtp", 10 )
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [10, 100]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(False)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,38 @@
cmake_minimum_required(VERSION 2.8.8)
project(CxxMappedDataArrayExample)
set(USE_CATALYST ON CACHE BOOL "Link the simulator with Catalyst")
if(USE_CATALYST)
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
set(Adaptor_SRCS
FEAdaptor.cxx
)
add_library(CxxMappedDataArrayExampleAdaptor ${Adaptor_SRCS})
target_link_libraries(CxxMappedDataArrayExampleAdaptor vtkPVPythonCatalyst vtkParallelMPI)
add_definitions("-DUSE_CATALYST")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
else()
find_package(MPI REQUIRED)
include_directories(${MPI_C_INCLUDE_PATH})
endif()
add_executable(CxxMappedDataArrayExample FEDriver.cxx FEDataStructures.cxx)
if(USE_CATALYST)
target_link_libraries(CxxMappedDataArrayExample LINK_PRIVATE CxxMappedDataArrayExampleAdaptor)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(CxxMappedDataArrayExample)
else()
target_link_libraries(CxxMappedDataArrayExample LINK_PRIVATE ${MPI_LIBRARIES})
endif()
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
add_test(NAME CxxMappedDataArrayExampleTest COMMAND CxxMappedDataArrayExample ${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/feslicescript.py)
set_tests_properties(CxxMappedDataArrayExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,151 @@
#include <iostream>
#include "FEAdaptor.h"
#include "FEDataStructures.h"
#include <vtkCellData.h>
#include <vtkCellType.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkCPPythonScriptPipeline.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkPointData.h>
#include <vtkUnstructuredGrid.h>
#include "vtkCPMappedVectorArrayTemplate.h"
namespace
{
vtkCPProcessor* Processor = NULL;
vtkUnstructuredGrid* VTKGrid;
void BuildVTKGrid(Grid& grid)
{
// create the points information
vtkCPMappedVectorArrayTemplate<double>* pointArray =
vtkCPMappedVectorArrayTemplate<double>::New();
pointArray->SetVectorArray(grid.GetPointsArray(),static_cast<vtkIdType>(grid.GetNumberOfPoints()));
vtkNew<vtkPoints> points;
points->SetData(pointArray);
pointArray->Delete();
VTKGrid->SetPoints(points.GetPointer());
// create the cells
size_t numCells = grid.GetNumberOfCells();
VTKGrid->Allocate(static_cast<vtkIdType>(numCells*9));
for(size_t cell=0;cell<numCells;cell++)
{
unsigned int* cellPoints = grid.GetCellPoints(cell);
vtkIdType tmp[8] = {cellPoints[0], cellPoints[1], cellPoints[2], cellPoints[3],
cellPoints[4], cellPoints[5], cellPoints[6], cellPoints[7]};
VTKGrid->InsertNextCell(VTK_HEXAHEDRON, 8, tmp);
}
}
void UpdateVTKAttributes(Grid& grid, Attributes& attributes)
{
if(VTKGrid->GetPointData()->GetNumberOfArrays() == 0)
{
// velocity array
vtkCPMappedVectorArrayTemplate<double>* velocity =
vtkCPMappedVectorArrayTemplate<double>::New();
velocity->SetName("velocity");
VTKGrid->GetPointData()->AddArray(velocity);
velocity->Delete();
}
vtkCPMappedVectorArrayTemplate<double>* velocity =
vtkCPMappedVectorArrayTemplate<double>::SafeDownCast(
VTKGrid->GetPointData()->GetArray("velocity"));
velocity->SetVectorArray(attributes.GetVelocityArray(),
VTKGrid->GetNumberOfPoints());
if(VTKGrid->GetCellData()->GetNumberOfArrays() == 0)
{
// pressure array
vtkNew<vtkFloatArray> pressure;
pressure->SetName("pressure");
pressure->SetNumberOfComponents(1);
VTKGrid->GetCellData()->AddArray(pressure.GetPointer());
}
vtkFloatArray* pressure = vtkFloatArray::SafeDownCast(
VTKGrid->GetCellData()->GetArray("pressure"));
// The pressure array is a scalar array so we can reuse
// memory as long as we ordered the points properly.
float* pressureData = attributes.GetPressureArray();
pressure->SetArray(pressureData, static_cast<vtkIdType>(grid.GetNumberOfCells()), 1);
}
void BuildVTKDataStructures(Grid& grid, Attributes& attributes)
{
if(VTKGrid == NULL)
{
// The grid structure isn't changing so we only build it
// the first time it's needed. If we needed the memory
// we could delete it and rebuild as necessary.
VTKGrid = vtkUnstructuredGrid::New();
BuildVTKGrid(grid);
}
UpdateVTKAttributes(grid, attributes);
}
}
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[])
{
if(Processor == NULL)
{
Processor = vtkCPProcessor::New();
Processor->Initialize();
}
else
{
Processor->RemoveAllPipelines();
}
for(int i=1;i<numScripts;i++)
{
vtkNew<vtkCPPythonScriptPipeline> pipeline;
pipeline->Initialize(scripts[i]);
Processor->AddPipeline(pipeline.GetPointer());
}
}
void Finalize()
{
if(Processor)
{
Processor->Delete();
Processor = NULL;
}
if(VTKGrid)
{
VTKGrid->Delete();
VTKGrid = NULL;
}
}
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep)
{
vtkNew<vtkCPDataDescription> dataDescription;
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(Processor->RequestDataDescription(dataDescription.GetPointer()) != 0)
{
BuildVTKDataStructures(grid, attributes);
dataDescription->GetInputDescriptionByName("input")->SetGrid(VTKGrid);
Processor->CoProcess(dataDescription.GetPointer());
}
}
} // end of Catalyst namespace

View File

@ -0,0 +1,17 @@
#ifndef FEADAPTOR_HEADER
#define FEADAPTOR_HEADER
class Attributes;
class Grid;
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[]);
void Finalize();
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep);
}
#endif

View File

@ -0,0 +1,162 @@
#include "FEDataStructures.h"
#include <mpi.h>
#include <iostream>
Grid::Grid()
{}
void Grid::Initialize(const unsigned int numPoints[3], const double spacing[3] )
{
if(numPoints[0] == 0 || numPoints[1] == 0 || numPoints[2] == 0)
{
std::cerr << "Must have a non-zero amount of points in each direction.\n";
}
// in parallel, we do a simple partitioning in the x-direction.
int mpiSize = 1;
int mpiRank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
unsigned int startXPoint = mpiRank*numPoints[0]/mpiSize;
unsigned int endXPoint = (mpiRank+1)*numPoints[0]/mpiSize;
if(mpiSize != mpiRank+1)
{
endXPoint++;
}
// create the points -- slowest in the x and fastest in the z directions
// all of the x coordinates are stored first, then y coordinates and
// finally z coordinates (e.g. x[0], x[1], ..., x[n-1], y[0], y[1], ...,
// y[n-1], z[0], z[1], ..., z[n-1]) which is OPPOSITE of VTK's ordering.
size_t numTotalPoints = (endXPoint-startXPoint)*numPoints[1]*numPoints[2];
this->Points.resize(3*numTotalPoints);
size_t counter = 0;
for(unsigned int i=startXPoint;i<endXPoint;i++)
{
for(unsigned int j=0;j<numPoints[1];j++)
{
for(unsigned int k=0;k<numPoints[2];k++)
{
this->Points[counter] = i*spacing[0];
this->Points[numTotalPoints+counter] = j*spacing[1];
this->Points[2*numTotalPoints+counter] = k*spacing[2];
counter++;
}
}
}
// create the hex cells
unsigned int cellPoints[8];
unsigned int numXPoints = endXPoint - startXPoint;
for(unsigned int i=0;i<numXPoints-1;i++)
{
for(unsigned int j=0;j<numPoints[1]-1;j++)
{
for(unsigned int k=0;k<numPoints[2]-1;k++)
{
cellPoints[0] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
cellPoints[1] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
cellPoints[2] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
cellPoints[3] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
cellPoints[4] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
cellPoints[5] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
cellPoints[6] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
cellPoints[7] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
std::copy(cellPoints, cellPoints+8, std::back_inserter(this->Cells));
}
}
}
}
size_t Grid::GetNumberOfPoints()
{
return this->Points.size()/3;
}
size_t Grid::GetNumberOfCells()
{
return this->Cells.size()/8;
}
double* Grid::GetPointsArray()
{
if(this->Points.empty())
{
return NULL;
}
return &(this->Points[0]);
}
bool Grid::GetPoint(size_t pointId, double coord[3])
{
if(pointId >= this->Points.size()/3)
{
return false;
}
coord[0] = this->Points[pointId];
coord[1] = this->Points[pointId+this->GetNumberOfPoints()];
coord[2] = this->Points[pointId+2*this->GetNumberOfPoints()];
return true;
}
unsigned int* Grid::GetCellPoints(size_t cellId)
{
if(cellId >= this->Cells.size())
{
return NULL;
}
return &(this->Cells[cellId*8]);
}
Attributes::Attributes()
{
this->GridPtr = NULL;
}
void Attributes::Initialize(Grid* grid)
{
this->GridPtr = grid;
}
void Attributes::UpdateFields(double time)
{
size_t numPoints = this->GridPtr->GetNumberOfPoints();
this->Velocity.resize(numPoints*3);
double coord[3] = {0, 0, 0};
for(size_t pt=0;pt<numPoints;pt++)
{
this->GridPtr->GetPoint(pt, coord);
this->Velocity[pt] = coord[1]*time;
}
std::fill(this->Velocity.begin()+numPoints, this->Velocity.end(), 0.);
size_t numCells = this->GridPtr->GetNumberOfCells();
this->Pressure.resize(numCells);
std::fill(this->Pressure.begin(), this->Pressure.end(), 1.);
}
double* Attributes::GetVelocityArray()
{
if(this->Velocity.empty())
{
return NULL;
}
return &this->Velocity[0];
}
float* Attributes::GetPressureArray()
{
if(this->Pressure.empty())
{
return NULL;
}
return &this->Pressure[0];
}

View File

@ -0,0 +1,41 @@
#ifndef FEDATASTRUCTURES_HEADER
#define FEDATASTRUCTURES_HEADER
#include <cstddef>
#include <vector>
class Grid
{
public:
Grid();
void Initialize(const unsigned int numPoints[3], const double spacing[3]);
size_t GetNumberOfPoints();
size_t GetNumberOfCells();
double* GetPointsArray();
bool GetPoint(size_t pointId, double coord[3]);
unsigned int* GetCellPoints(size_t cellId);
private:
std::vector<double> Points;
std::vector<unsigned int> Cells;
};
class Attributes
{
// A class for generating and storing point and cell fields.
// Velocity is stored at the points and pressure is stored
// for the cells. The current velocity profile is for a
// shearing flow with U(y,t) = y*t, V = 0 and W = 0.
// Pressure is constant through the domain.
public:
Attributes();
void Initialize(Grid* grid);
void UpdateFields(double time);
double* GetVelocityArray();
float* GetPressureArray();
private:
std::vector<double> Velocity;
std::vector<float> Pressure;
Grid* GridPtr;
};
#endif

View File

@ -0,0 +1,51 @@
#include "FEDataStructures.h"
#include <mpi.h>
#ifdef USE_CATALYST
#include "FEAdaptor.h"
#endif
// Example of a C++ adaptor for a simulation code
// where the simulation code has a fixed topology
// grid. We treat the grid as an unstructured
// grid even though in the example provided it
// would be best described as a vtkImageData.
// Also, the points are stored in an inconsistent
// manner with respect to the velocity vector.
// This is purposefully done to demonstrate
// the different approaches for getting data
// into Catalyst. Note that through configuration
// that the driver can be run without linking
// to Catalyst.
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
Grid grid;
unsigned int numPoints[3] = {70, 60, 44};
double spacing[3] = {1, 1.1, 1.3};
grid.Initialize(numPoints, spacing);
Attributes attributes;
attributes.Initialize(&grid);
#ifdef USE_CATALYST
FEAdaptor::Initialize(argc, argv);
#endif
unsigned int numberOfTimeSteps = 100;
for(unsigned int timeStep=0;timeStep<numberOfTimeSteps;timeStep++)
{
// use a time step length of 0.1
double time = timeStep * 0.1;
attributes.UpdateFields(time);
#ifdef USE_CATALYST
FEAdaptor::CoProcess(grid, attributes, time, timeStep, timeStep == numberOfTimeSteps-1);
#endif
}
#ifdef USE_CATALYST
FEAdaptor::Finalize();
#endif
MPI_Finalize();
return 0;
}

View File

@ -0,0 +1,86 @@
try: paraview.simple
except: from paraview.simple import *
from paraview import coprocessing
#--------------------------------------------------------------
# Code generated from cpstate.py to create the CoProcessor.
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
filename_3_pvtu = coprocessor.CreateProducer( datadescription, "input" )
Slice1 = Slice( guiName="Slice1", Crinkleslice=0, SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane" )
Slice1.SliceType.Offset = 0.0
Slice1.SliceType.Origin = [34.5, 32.45, 27.95]
Slice1.SliceType.Normal = [1.0, 0.0, 0.0]
ParallelPolyDataWriter1 = coprocessor.CreateWriter( XMLPPolyDataWriter, "slice_%t.pvtp", 10 )
SetActiveSource(filename_3_pvtu)
ParallelUnstructuredGridWriter1 = coprocessor.CreateWriter( XMLPUnstructuredGridWriter, "fullgrid_%t.pvtu", 100 )
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [10, 100]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(False)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,120 @@
/*=========================================================================
Program: Visualization Toolkit
Module: vtkCPMappedVectorArrayTemplate.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
// .NAME vtkCPMappedVectorArrayTemplate - Map native data arrays into
// the vtkDataArray interface.
//
// .SECTION Description
// Map native simulation code data arrays into the vtkDataArray interface.
// This is based on the vtkCPExodusIIResultsArrayTemplate.h class in VTK.
#ifndef vtkCPMappedVectorArrayTemplate_h
#define vtkCPMappedVectorArrayTemplate_h
#include "vtkMappedDataArray.h"
#include "vtkTypeTemplate.h" // For templated vtkObject API
#include "vtkObjectFactory.h" // for vtkStandardNewMacro
template <class Scalar>
class vtkCPMappedVectorArrayTemplate:
public vtkTypeTemplate<vtkCPMappedVectorArrayTemplate<Scalar>,
vtkMappedDataArray<Scalar> >
{
public:
vtkMappedDataArrayNewInstanceMacro(
vtkCPMappedVectorArrayTemplate<Scalar>)
static vtkCPMappedVectorArrayTemplate *New();
virtual void PrintSelf(ostream &os, vtkIndent indent);
// Description:
// Set the raw scalar arrays for the coordinate set. This class takes
// ownership of the arrays and deletes them with delete[].
void SetVectorArray(Scalar *array, vtkIdType numPoints);
// Reimplemented virtuals -- see superclasses for descriptions:
void Initialize();
void GetTuples(vtkIdList *ptIds, vtkAbstractArray *output);
void GetTuples(vtkIdType p1, vtkIdType p2, vtkAbstractArray *output);
void Squeeze();
vtkArrayIterator *NewIterator();
vtkIdType LookupValue(vtkVariant value);
void LookupValue(vtkVariant value, vtkIdList *ids);
vtkVariant GetVariantValue(vtkIdType idx);
void ClearLookup();
double* GetTuple(vtkIdType i);
void GetTuple(vtkIdType i, double *tuple);
vtkIdType LookupTypedValue(Scalar value);
void LookupTypedValue(Scalar value, vtkIdList *ids);
Scalar GetValue(vtkIdType idx);
Scalar& GetValueReference(vtkIdType idx);
void GetTupleValue(vtkIdType idx, Scalar *t);
// Description:
// This container is read only -- this method does nothing but print a
// warning.
int Allocate(vtkIdType sz, vtkIdType ext);
int Resize(vtkIdType numTuples);
void SetNumberOfTuples(vtkIdType number);
void SetTuple(vtkIdType i, vtkIdType j, vtkAbstractArray *source);
void SetTuple(vtkIdType i, const float *source);
void SetTuple(vtkIdType i, const double *source);
void InsertTuple(vtkIdType i, vtkIdType j, vtkAbstractArray *source);
void InsertTuple(vtkIdType i, const float *source);
void InsertTuple(vtkIdType i, const double *source);
void InsertTuples(vtkIdList *dstIds, vtkIdList *srcIds,
vtkAbstractArray *source);
void InsertTuples(vtkIdType dstStart, vtkIdType n, vtkIdType srcStart,
vtkAbstractArray* source);
vtkIdType InsertNextTuple(vtkIdType j, vtkAbstractArray *source);
vtkIdType InsertNextTuple(const float *source);
vtkIdType InsertNextTuple(const double *source);
void DeepCopy(vtkAbstractArray *aa);
void DeepCopy(vtkDataArray *da);
void InterpolateTuple(vtkIdType i, vtkIdList *ptIndices,
vtkAbstractArray* source, double* weights);
void InterpolateTuple(vtkIdType i, vtkIdType id1, vtkAbstractArray *source1,
vtkIdType id2, vtkAbstractArray *source2, double t);
void SetVariantValue(vtkIdType idx, vtkVariant value);
void InsertVariantValue(vtkIdType idx, vtkVariant value);
void RemoveTuple(vtkIdType id);
void RemoveFirstTuple();
void RemoveLastTuple();
void SetTupleValue(vtkIdType i, const Scalar *t);
void InsertTupleValue(vtkIdType i, const Scalar *t);
vtkIdType InsertNextTupleValue(const Scalar *t);
void SetValue(vtkIdType idx, Scalar value);
vtkIdType InsertNextValue(Scalar v);
void InsertValue(vtkIdType idx, Scalar v);
protected:
vtkCPMappedVectorArrayTemplate();
~vtkCPMappedVectorArrayTemplate();
Scalar *Array;
private:
vtkCPMappedVectorArrayTemplate(
const vtkCPMappedVectorArrayTemplate &); // Not implemented.
void operator=(
const vtkCPMappedVectorArrayTemplate &); // Not implemented.
vtkIdType Lookup(const Scalar &val, vtkIdType startIndex);
double TempDoubleArray[3];
};
#include "vtkCPMappedVectorArrayTemplate.txx"
#endif //vtkCPMappedVectorArrayTemplate_h

View File

@ -0,0 +1,499 @@
/*=========================================================================
Program: Visualization Toolkit
Module: vtkCPMappedVectorArrayTemplate.txx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#include "vtkCPMappedVectorArrayTemplate.h"
#include "vtkIdList.h"
#include "vtkObjectFactory.h"
#include "vtkVariant.h"
#include "vtkVariantCast.h"
//------------------------------------------------------------------------------
// Can't use vtkStandardNewMacro with a template.
template <class Scalar> vtkCPMappedVectorArrayTemplate<Scalar> *
vtkCPMappedVectorArrayTemplate<Scalar>::New()
{
VTK_STANDARD_NEW_BODY(vtkCPMappedVectorArrayTemplate<Scalar>)
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::PrintSelf(ostream &os, vtkIndent indent)
{
this->vtkCPMappedVectorArrayTemplate<Scalar>::Superclass::PrintSelf(
os, indent);
os << indent << "Array: " << this->Array << std::endl;
os << indent << "TempDoubleArray: " << this->TempDoubleArray << std::endl;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::Initialize()
{
this->Array = NULL;
this->MaxId = -1;
this->Size = 0;
this->NumberOfComponents = 3;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::GetTuples(vtkIdList *ptIds, vtkAbstractArray *output)
{
vtkDataArray *outArray = vtkDataArray::FastDownCast(output);
if (!outArray)
{
vtkWarningMacro(<<"Input is not a vtkDataArray");
return;
}
vtkIdType numTuples = ptIds->GetNumberOfIds();
outArray->SetNumberOfComponents(this->NumberOfComponents);
outArray->SetNumberOfTuples(numTuples);
const vtkIdType numPoints = ptIds->GetNumberOfIds();
for (vtkIdType i = 0; i < numPoints; ++i)
{
outArray->SetTuple(i, this->GetTuple(ptIds->GetId(i)));
}
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::GetTuples(vtkIdType p1, vtkIdType p2, vtkAbstractArray *output)
{
vtkDataArray *da = vtkDataArray::FastDownCast(output);
if (!da)
{
vtkErrorMacro(<<"Input is not a vtkDataArray");
return;
}
if (da->GetNumberOfComponents() != this->GetNumberOfComponents())
{
vtkErrorMacro(<<"Incorrect number of components in input array.");
return;
}
for (vtkIdType daTupleId = 0; p1 <= p2; ++p1)
{
da->SetTuple(daTupleId++, this->GetTuple(p1));
}
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::Squeeze()
{
// noop
}
//------------------------------------------------------------------------------
template <class Scalar> vtkArrayIterator*
vtkCPMappedVectorArrayTemplate<Scalar>::NewIterator()
{
vtkErrorMacro(<<"Not implemented.");
return NULL;
}
//------------------------------------------------------------------------------
template <class Scalar> vtkIdType vtkCPMappedVectorArrayTemplate<Scalar>
::LookupValue(vtkVariant value)
{
bool valid = true;
Scalar val = vtkVariantCast<Scalar>(value, &valid);
if (valid)
{
return this->Lookup(val, 0);
}
return -1;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::LookupValue(vtkVariant value, vtkIdList *ids)
{
bool valid = true;
Scalar val = vtkVariantCast<Scalar>(value, &valid);
ids->Reset();
if (valid)
{
vtkIdType index = 0;
while ((index = this->Lookup(val, index)) >= 0)
{
ids->InsertNextId(index++);
}
}
}
//------------------------------------------------------------------------------
template <class Scalar> vtkVariant vtkCPMappedVectorArrayTemplate<Scalar>
::GetVariantValue(vtkIdType idx)
{
return vtkVariant(this->GetValueReference(idx));
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::ClearLookup()
{
// no-op, no fast lookup implemented.
}
//------------------------------------------------------------------------------
template <class Scalar> double* vtkCPMappedVectorArrayTemplate<Scalar>
::GetTuple(vtkIdType i)
{
this->GetTuple(i, this->TempDoubleArray);
return this->TempDoubleArray;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::GetTuple(vtkIdType i, double *tuple)
{
tuple[0] = static_cast<double>(this->Array[i]);
tuple[1] = static_cast<double>(this->Array[i+this->GetNumberOfTuples()]);
tuple[2] = static_cast<double>(this->Array[i+2*this->GetNumberOfTuples()]);
}
//------------------------------------------------------------------------------
template <class Scalar> vtkIdType vtkCPMappedVectorArrayTemplate<Scalar>
::LookupTypedValue(Scalar value)
{
return this->Lookup(value, 0);
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::LookupTypedValue(Scalar value, vtkIdList *ids)
{
ids->Reset();
vtkIdType index = 0;
while ((index = this->Lookup(value, index)) >= 0)
{
ids->InsertNextId(index++);
}
}
//------------------------------------------------------------------------------
template <class Scalar> Scalar vtkCPMappedVectorArrayTemplate<Scalar>
::GetValue(vtkIdType idx)
{
return this->GetValueReference(idx);
}
//------------------------------------------------------------------------------
template <class Scalar> Scalar& vtkCPMappedVectorArrayTemplate<Scalar>
::GetValueReference(vtkIdType idx)
{
const vtkIdType tuple = idx / this->NumberOfComponents;
const vtkIdType comp = idx % this->NumberOfComponents;
switch (comp)
{
case 0:
return this->Array[tuple];
case 1:
return this->Array[tuple+this->GetNumberOfTuples()];
case 2:
return this->Array[tuple+2*this->GetNumberOfTuples()];
default:
vtkErrorMacro(<< "Invalid number of components.");
static Scalar dummy(0);
return dummy;
}
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::GetTupleValue(vtkIdType tupleId, Scalar *tuple)
{
tuple[0] = this->Array[tupleId];
tuple[1] = this->Array[tupleId+this->GetNumberOfTuples()];
tuple[2] = this->Array[tupleId+2*this->GetNumberOfTuples()];
}
//------------------------------------------------------------------------------
template <class Scalar> int vtkCPMappedVectorArrayTemplate<Scalar>
::Allocate(vtkIdType, vtkIdType)
{
vtkErrorMacro("Read only container.")
return 0;
}
//------------------------------------------------------------------------------
template <class Scalar> int vtkCPMappedVectorArrayTemplate<Scalar>
::Resize(vtkIdType)
{
vtkErrorMacro("Read only container.")
return 0;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::SetNumberOfTuples(vtkIdType)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::SetTuple(vtkIdType, vtkIdType, vtkAbstractArray *)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::SetTuple(vtkIdType, const float *)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::SetTuple(vtkIdType, const double *)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::InsertTuple(vtkIdType, vtkIdType, vtkAbstractArray *)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::InsertTuple(vtkIdType, const float *)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::InsertTuple(vtkIdType, const double *)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::InsertTuples(vtkIdList *, vtkIdList *, vtkAbstractArray *)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::InsertTuples(vtkIdType, vtkIdType, vtkIdType, vtkAbstractArray *)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> vtkIdType vtkCPMappedVectorArrayTemplate<Scalar>
::InsertNextTuple(vtkIdType, vtkAbstractArray *)
{
vtkErrorMacro("Read only container.")
return -1;
}
//------------------------------------------------------------------------------
template <class Scalar> vtkIdType vtkCPMappedVectorArrayTemplate<Scalar>
::InsertNextTuple(const float *)
{
vtkErrorMacro("Read only container.")
return -1;
}
//------------------------------------------------------------------------------
template <class Scalar> vtkIdType vtkCPMappedVectorArrayTemplate<Scalar>
::InsertNextTuple(const double *)
{
vtkErrorMacro("Read only container.")
return -1;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::DeepCopy(vtkAbstractArray *)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::DeepCopy(vtkDataArray *)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::InterpolateTuple(vtkIdType, vtkIdList *, vtkAbstractArray *, double *)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::InterpolateTuple(vtkIdType, vtkIdType, vtkAbstractArray*, vtkIdType,
vtkAbstractArray*, double)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::SetVariantValue(vtkIdType, vtkVariant)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::InsertVariantValue(vtkIdType, vtkVariant)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::RemoveTuple(vtkIdType)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::RemoveFirstTuple()
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::RemoveLastTuple()
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::SetTupleValue(vtkIdType, const Scalar*)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::InsertTupleValue(vtkIdType, const Scalar*)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> vtkIdType vtkCPMappedVectorArrayTemplate<Scalar>
::InsertNextTupleValue(const Scalar *)
{
vtkErrorMacro("Read only container.")
return -1;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::SetValue(vtkIdType, Scalar)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> vtkIdType vtkCPMappedVectorArrayTemplate<Scalar>
::InsertNextValue(Scalar)
{
vtkErrorMacro("Read only container.")
return -1;
}
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::InsertValue(vtkIdType, Scalar)
{
vtkErrorMacro("Read only container.")
return;
}
//------------------------------------------------------------------------------
template <class Scalar> vtkCPMappedVectorArrayTemplate<Scalar>
::vtkCPMappedVectorArrayTemplate()
: Array(NULL)
{
}
//------------------------------------------------------------------------------
template <class Scalar> vtkCPMappedVectorArrayTemplate<Scalar>
::~vtkCPMappedVectorArrayTemplate()
{ }
//------------------------------------------------------------------------------
template <class Scalar> void vtkCPMappedVectorArrayTemplate<Scalar>
::SetVectorArray(Scalar *array, vtkIdType numPoints)
{
Initialize();
this->Array = array;
this->NumberOfComponents = 3;
this->Size = this->NumberOfComponents * numPoints;
this->MaxId = this->Size - 1;
this->Modified();
}
//------------------------------------------------------------------------------
template <class Scalar> vtkIdType vtkCPMappedVectorArrayTemplate<Scalar>
::Lookup(const Scalar &val, vtkIdType index)
{
while (index <= this->MaxId)
{
if (this->GetValueReference(index++) == val)
{
return index;
}
}
return -1;
}

View File

@ -0,0 +1,38 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystCxxMultiPieceExample)
set(USE_CATALYST ON CACHE BOOL "Link the simulator with Catalyst")
if(USE_CATALYST)
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
set(Adaptor_SRCS
FEAdaptor.cxx
)
add_library(CxxMultiPieceExampleAdaptor ${Adaptor_SRCS})
target_link_libraries(CxxMultiPieceExampleAdaptor vtkPVPythonCatalyst)
add_definitions("-DUSE_CATALYST")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
else()
find_package(MPI REQUIRED)
include_directories(${MPI_CXX_INCLUDE_PATH})
endif()
add_executable(CxxMultiPieceExample FEDriver.cxx FEDataStructures.cxx)
if(USE_CATALYST)
target_link_libraries(CxxMultiPieceExample LINK_PRIVATE CxxMultiPieceExampleAdaptor)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(CxxMultiPieceExample)
else()
target_link_libraries(CxxMultiPieceExample LINK_PRIVATE ${MPI_LIBRARIES})
endif()
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
add_test(NAME CxxMultiPieceExampleTest COMMAND CxxMultiPieceExample ${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/feslicescript.py)
set_tests_properties(CxxMultiPieceExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,182 @@
#include <iostream>
#include "FEAdaptor.h"
#include "FEDataStructures.h"
#include <vtkCellData.h>
#include <vtkCellType.h>
#include <vtkCommunicator.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkCPPythonScriptPipeline.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkImageData.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkMultiPieceDataSet.h>
#include <vtkMultiProcessController.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkPointData.h>
#include <mpi.h>
namespace
{
vtkCPProcessor* Processor = NULL;
vtkMultiBlockDataSet* VTKGrid;
void BuildVTKGrid(Grid& grid)
{
vtkNew<vtkImageData> imageData;
imageData->SetSpacing(grid.GetSpacing());
imageData->SetOrigin(0, 0, 0); // Not necessary for (0,0,0)
unsigned int* extents = grid.GetExtents();
int extents2[6];
for(int i=0;i<6;i++)
{
extents2[i] = static_cast<int>(extents[i]);
}
imageData->SetExtent(extents2);
int mpiSize = 1;
int mpiRank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
vtkNew<vtkMultiPieceDataSet> multiPiece;
multiPiece->SetNumberOfPieces(mpiSize);
multiPiece->SetPiece(mpiRank, imageData.GetPointer());
VTKGrid->SetNumberOfBlocks(1);
VTKGrid->SetBlock(0, multiPiece.GetPointer());
}
void UpdateVTKAttributes(Grid& grid, Attributes& attributes)
{
int mpiRank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
vtkMultiPieceDataSet* multiPiece = vtkMultiPieceDataSet::SafeDownCast(
VTKGrid->GetBlock(0));
vtkDataSet* dataSet = vtkDataSet::SafeDownCast(multiPiece->GetPiece(mpiRank));
if(dataSet->GetPointData()->GetNumberOfArrays() == 0)
{
// velocity array
vtkNew<vtkDoubleArray> velocity;
velocity->SetName("velocity");
velocity->SetNumberOfComponents(3);
velocity->SetNumberOfTuples(static_cast<vtkIdType>(grid.GetNumberOfLocalPoints()));
dataSet->GetPointData()->AddArray(velocity.GetPointer());
}
if(dataSet->GetCellData()->GetNumberOfArrays() == 0)
{
// pressure array
vtkNew<vtkFloatArray> pressure;
pressure->SetName("pressure");
pressure->SetNumberOfComponents(1);
dataSet->GetCellData()->AddArray(pressure.GetPointer());
}
vtkDoubleArray* velocity = vtkDoubleArray::SafeDownCast(
dataSet->GetPointData()->GetArray("velocity"));
// The velocity array is ordered as vx0,vx1,vx2,..,vy0,vy1,vy2,..,vz0,vz1,vz2,..
// so we need to create a full copy of it with VTK's ordering of
// vx0,vy0,vz0,vx1,vy1,vz1,..
double* velocityData = attributes.GetVelocityArray();
vtkIdType numTuples = velocity->GetNumberOfTuples();
for(vtkIdType i=0;i<numTuples;i++)
{
double values[3] = {velocityData[i], velocityData[i+numTuples],
velocityData[i+2*numTuples]};
velocity->SetTupleValue(i, values);
}
vtkFloatArray* pressure = vtkFloatArray::SafeDownCast(
dataSet->GetCellData()->GetArray("pressure"));
// The pressure array is a scalar array so we can reuse
// memory as long as we ordered the points properly.
float* pressureData = attributes.GetPressureArray();
pressure->SetArray(pressureData, static_cast<vtkIdType>(grid.GetNumberOfLocalCells()), 1);
}
void BuildVTKDataStructures(Grid& grid, Attributes& attributes)
{
if(VTKGrid == NULL)
{
// The grid structure isn't changing so we only build it
// the first time it's needed. If we needed the memory
// we could delete it and rebuild as necessary.
VTKGrid = vtkMultiBlockDataSet::New();
BuildVTKGrid(grid);
}
UpdateVTKAttributes(grid, attributes);
}
}
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[])
{
if(Processor == NULL)
{
Processor = vtkCPProcessor::New();
Processor->Initialize();
}
else
{
Processor->RemoveAllPipelines();
}
for(int i=1;i<numScripts;i++)
{
vtkNew<vtkCPPythonScriptPipeline> pipeline;
pipeline->Initialize(scripts[i]);
Processor->AddPipeline(pipeline.GetPointer());
}
}
void Finalize()
{
if(Processor)
{
Processor->Delete();
Processor = NULL;
}
if(VTKGrid)
{
VTKGrid->Delete();
VTKGrid = NULL;
}
}
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep)
{
vtkNew<vtkCPDataDescription> dataDescription;
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(Processor->RequestDataDescription(dataDescription.GetPointer()) != 0)
{
BuildVTKDataStructures(grid, attributes);
dataDescription->GetInputDescriptionByName("input")->SetGrid(VTKGrid);
// figure out the whole extent of the grid
unsigned int* extent = grid.GetExtents();
int wholeExtent[6], tmp[6];
for(int i=0;i<3;i++)
{
wholeExtent[i*2] = - static_cast<int>(extent[i*2]); // negate for parallel communication
wholeExtent[i*2+1] = static_cast<int>(extent[i*2+1]);
}
vtkMultiProcessController::GetGlobalController()->AllReduce(wholeExtent, tmp, 6, vtkCommunicator::MAX_OP);
for(int i=0;i<3;i++)
{
tmp[i*2] = -tmp[i*2];
}
dataDescription->GetInputDescriptionByName("input")->SetWholeExtent(tmp);
Processor->CoProcess(dataDescription.GetPointer());
}
}
} // end of Catalyst namespace

View File

@ -0,0 +1,17 @@
#ifndef FEADAPTOR_HEADER
#define FEADAPTOR_HEADER
class Attributes;
class Grid;
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[]);
void Finalize();
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep);
}
#endif

View File

@ -0,0 +1,118 @@
#include "FEDataStructures.h"
#include <mpi.h>
#include <iostream>
Grid::Grid()
{}
void Grid::Initialize(const unsigned int numPoints[3], const double spacing[3] )
{
if(numPoints[0] == 0 || numPoints[1] == 0 || numPoints[2] == 0)
{
std::cerr << "Must have a non-zero amount of points in each direction.\n";
}
for(int i=0;i<3;i++)
{
this->Extents[i*2] = 0;
this->Extents[i*2+1] = numPoints[i];
this->Spacing[i] = spacing[i];
}
// in parallel, we do a simple partitioning in the x-direction.
int mpiSize = 1;
int mpiRank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
this->Extents[0] = mpiRank*numPoints[0]/mpiSize;
this->Extents[1] = (mpiRank+1)*numPoints[0]/mpiSize;
}
unsigned int Grid::GetNumberOfLocalPoints()
{
return (this->Extents[1]-this->Extents[0]+1) *
(this->Extents[3]-this->Extents[2]+1) *
(this->Extents[5]-this->Extents[4]+1);
}
unsigned int Grid::GetNumberOfLocalCells()
{
return (this->Extents[1]-this->Extents[0]) *
(this->Extents[3]-this->Extents[2]) *
(this->Extents[5]-this->Extents[4]);
}
void Grid::GetPoint(unsigned int logicalLocalCoords[3], double coord[3])
{
coord[0] = (this->Extents[0]+logicalLocalCoords[0])*this->Spacing[0];
coord[1] = logicalLocalCoords[1]*this->Spacing[1];
coord[2] = logicalLocalCoords[2]*this->Spacing[2];
}
double* Grid::GetSpacing()
{
return this->Spacing;
}
unsigned int* Grid::GetExtents()
{
return this->Extents;
}
Attributes::Attributes()
{
this->GridPtr = NULL;
}
void Attributes::Initialize(Grid* grid)
{
this->GridPtr = grid;
}
void Attributes::UpdateFields(double time)
{
size_t numPoints = this->GridPtr->GetNumberOfLocalPoints();
this->Velocity.resize(numPoints*3);
unsigned int* extents = this->GridPtr->GetExtents();
unsigned int logicalLocalCoords[3];
size_t pt = 0;
for(unsigned int k=0;k<extents[5]-extents[4]+1;k++)
{
logicalLocalCoords[2] = k;
for(unsigned int j=0;j<extents[3]-extents[2]+1;j++)
{
logicalLocalCoords[1] = j;
for(unsigned int i=0;i<extents[1]-extents[0]+1;i++)
{
logicalLocalCoords[0] = i;
double coord[3];
this->GridPtr->GetPoint(logicalLocalCoords, coord);
this->Velocity[pt] = coord[1]*time;
pt++;
}
}
}
std::fill(this->Velocity.begin()+numPoints, this->Velocity.end(), 0.);
size_t numCells = this->GridPtr->GetNumberOfLocalCells();
this->Pressure.resize(numCells);
std::fill(this->Pressure.begin(), this->Pressure.end(), 1.);
}
double* Attributes::GetVelocityArray()
{
if(this->Velocity.empty())
{
return NULL;
}
return &this->Velocity[0];
}
float* Attributes::GetPressureArray()
{
if(this->Pressure.empty())
{
return NULL;
}
return &this->Pressure[0];
}

View File

@ -0,0 +1,41 @@
#ifndef FEDATASTRUCTURES_HEADER
#define FEDATASTRUCTURES_HEADER
#include <cstddef>
#include <vector>
class Grid
{
public:
Grid();
void Initialize(const unsigned int numGlobalPoints[3], const double spacing[3]);
unsigned int GetNumberOfLocalPoints();
unsigned int GetNumberOfLocalCells();
void GetPoint(unsigned int logicalLocalCoords[3], double coord[3]);
double* GetSpacing();
unsigned int* GetExtents();
private:
unsigned int Extents[6];
double Spacing[3];
};
class Attributes
{
// A class for generating and storing point and cell fields.
// Velocity is stored at the points and pressure is stored
// for the cells. The current velocity profile is for a
// shearing flow with U(y,t) = y*t, V = 0 and W = 0.
// Pressure is constant through the domain.
public:
Attributes();
void Initialize(Grid* grid);
void UpdateFields(double time);
double* GetVelocityArray();
float* GetPressureArray();
private:
std::vector<double> Velocity;
std::vector<float> Pressure;
Grid* GridPtr;
};
#endif

View File

@ -0,0 +1,54 @@
#include <algorithm>
#include "FEDataStructures.h"
#include <iostream>
#include <mpi.h>
#include <vector>
#ifdef USE_CATALYST
#include "FEAdaptor.h"
#endif
// Example of a C++ adaptor for a simulation code
// where the simulation code has a fixed topology
// grid. We treat the grid as a partitioned
// regular grid. This is stored as a set of
// vtkImageDatas in a vtkMultiPieceDataSet.
// Also, the points are stored in an inconsistent
// manner with respect to the velocity vector.
// This is purposefully done to demonstrate
// the different approaches for getting data
// into Catalyst. Note that through configuration
// that the driver can be run without linking
// to Catalyst.
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
Grid grid;
unsigned int numPoints[3] = {70, 60, 44};
double spacing[3] = {1, 1.1, 1.3};
grid.Initialize(numPoints, spacing);
Attributes attributes;
attributes.Initialize(&grid);
#ifdef USE_CATALYST
FEAdaptor::Initialize(argc, argv);
#endif
unsigned int numberOfTimeSteps = 100;
for(unsigned int timeStep=0;timeStep<numberOfTimeSteps;timeStep++)
{
// use a time step length of 0.1
double time = timeStep * 0.1;
attributes.UpdateFields(time);
#ifdef USE_CATALYST
FEAdaptor::CoProcess(grid, attributes, time, timeStep, timeStep == numberOfTimeSteps-1);
#endif
}
#ifdef USE_CATALYST
FEAdaptor::Finalize();
#endif
MPI_Finalize();
return 0;
}

View File

@ -0,0 +1,85 @@
try: paraview.simple
except: from paraview.simple import *
from paraview import coprocessing
#--------------------------------------------------------------
# Code generated from cpstate.py to create the CoProcessor.
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
filename_18_vtm = coprocessor.CreateProducer( datadescription, "input" )
ParallelMultiBlockDataSetWriter1 = coprocessor.CreateWriter( XMLMultiBlockDataWriter, "fullgrid_%t.vtm", 100 )
SetActiveSource(filename_18_vtm)
Slice1 = Slice( guiName="Slice1", Crinkleslice=0, SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane" )
Slice1.SliceType.Offset = 0.0
Slice1.SliceType.Origin = [35.0, 33.0, 28.6]
Slice1.SliceType.Normal = [1.0, 0.0, 0.0]
ParallelMultiBlockDataSetWriter2 = coprocessor.CreateWriter( XMLMultiBlockDataWriter, "slice_%t.vtm", 10 )
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [10, 100]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(False)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystCxxNonOverlappingAMRExample)
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
set(Adaptor_SRCS
FEAdaptor.cxx
)
add_library(CxxNonOverlappingAMRExampleAdaptor ${Adaptor_SRCS})
target_link_libraries(CxxNonOverlappingAMRExampleAdaptor vtkPVPythonCatalyst)
add_definitions("-DUSE_CATALYST")
add_executable(CxxNonOverlappingAMRExample FEDriver.cxx)
target_link_libraries(CxxNonOverlappingAMRExample CxxNonOverlappingAMRExampleAdaptor)
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
add_test(NAME CxxNonOverlappingAMRExampleTest COMMAND CxxNonOverlappingAMRExample ${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/feslicescript.py)
set_tests_properties(CxxNonOverlappingAMRExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,121 @@
#include <iostream>
#include "FEAdaptor.h"
#include <vtkCompositeDataIterator.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkCPPythonScriptPipeline.h>
#include <vtkNew.h>
#include <vtkNonOverlappingAMR.h>
#include <vtkUniformGrid.h>
namespace
{
vtkCPProcessor* Processor = NULL;
vtkNonOverlappingAMR* VTKGrid;
void BuildVTKGrid()
{
if(VTKGrid == NULL)
{
// The grid structure isn't changing so we only build it
// the first time it's needed. If we needed the memory
// we could delete it and rebuild as necessary.
VTKGrid = vtkNonOverlappingAMR::New();
}
// Note that all of the vtkUniformGrids in the vtkNonOverlappingAMR
// grid can use independent spacing, origin and extents. This is
// shown in the mid-level grids in that they don't share an
// origin. The highest level grid and lowest level grid do share
// the same origin of (0,0,0) and thus need to have appropriate
// extents and spacings as well.
int numberOfLevels = 3;
int blocksPerLevel[3] = {1, 2, 1};
VTKGrid->Initialize(numberOfLevels, blocksPerLevel);
// the highest level grid
vtkNew<vtkUniformGrid> level0Grid;
level0Grid->SetSpacing(4, 4, 4);
level0Grid->SetOrigin(0, 0, 0);
level0Grid->SetExtent(0, 10, 0, 20, 0, 20);
VTKGrid->SetDataSet(0, 0, level0Grid.GetPointer());
// the first mid-level grid
vtkNew<vtkUniformGrid> level1Grid0;
level1Grid0->SetSpacing(2, 2, 2);
level1Grid0->SetOrigin(40, 0, 0);
level1Grid0->SetExtent(0, 8, 0, 20, 0, 40);
VTKGrid->SetDataSet(1, 0, level1Grid0.GetPointer());
// the second mid-level grid
vtkNew<vtkUniformGrid> level1Grid1;
level1Grid1->SetSpacing(2, 2, 2);
level1Grid1->SetOrigin(40, 40, 0);
level1Grid1->SetExtent(0, 40, 0, 20, 0, 40);
VTKGrid->SetDataSet(1, 1, level1Grid1.GetPointer());
// the lowest level grid
vtkNew<vtkUniformGrid> level2Grid;
level2Grid->SetSpacing(1, 1, 2);
level2Grid->SetOrigin(0, 0, 0);
level2Grid->SetExtent(56, 120, 0, 40, 0, 40);
VTKGrid->SetDataSet(2, 0, level2Grid.GetPointer());
}
}
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[])
{
if(Processor == NULL)
{
Processor = vtkCPProcessor::New();
Processor->Initialize();
}
else
{
Processor->RemoveAllPipelines();
}
for(int i=1;i<numScripts;i++)
{
vtkNew<vtkCPPythonScriptPipeline> pipeline;
pipeline->Initialize(scripts[i]);
Processor->AddPipeline(pipeline.GetPointer());
}
}
void Finalize()
{
if(Processor)
{
Processor->Delete();
Processor = NULL;
}
if(VTKGrid)
{
VTKGrid->Delete();
VTKGrid = NULL;
}
}
void CoProcess(double time, unsigned int timeStep, bool lastTimeStep)
{
vtkNew<vtkCPDataDescription> dataDescription;
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(Processor->RequestDataDescription(dataDescription.GetPointer()) != 0)
{
BuildVTKGrid();
dataDescription->GetInputDescriptionByName("input")->SetGrid(VTKGrid);
Processor->CoProcess(dataDescription.GetPointer());
}
}
} // end of Catalyst namespace

View File

@ -0,0 +1,13 @@
#ifndef FEADAPTOR_HEADER
#define FEADAPTOR_HEADER
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[]);
void Finalize();
void CoProcess(double time, unsigned int timeStep, bool lastTimeStep);
}
#endif

View File

@ -0,0 +1,25 @@
#include <algorithm>
#include <iostream>
#include <vector>
#include "FEAdaptor.h"
// Example of a C++ adaptor for a simulation code
// where the simulation code has a fixed topology
// grid. The grid in this case is a vtkNonOverlappingAMR
// data set without any attributes specified
// over the grid.
int main(int argc, char* argv[])
{
FEAdaptor::Initialize(argc, argv);
unsigned int numberOfTimeSteps = 100;
for(unsigned int timeStep=0;timeStep<numberOfTimeSteps;timeStep++)
{
// use a time step length of 0.1
double time = timeStep * 0.1;
FEAdaptor::CoProcess(time, timeStep, timeStep == numberOfTimeSteps-1);
}
FEAdaptor::Finalize();
return 0;
}

View File

@ -0,0 +1,83 @@
try: paraview.simple
except: from paraview.simple import *
from paraview import coprocessing
#--------------------------------------------------------------
# Code generated from cpstate.py to create the CoProcessor.
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
filename_4_vthb = coprocessor.CreateProducer( datadescription, "input" )
Slice1 = Slice( guiName="Slice1", Crinkleslice=0, SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane" )
Slice1.SliceType.Offset = 0.0
Slice1.SliceType.Origin = [20.0, 40.0, 40.0]
Slice1.SliceType.Normal = [1.0, 0.0, 0.0]
ParallelMultiBlockDataSetWriter1 = coprocessor.CreateWriter( XMLMultiBlockDataWriter, "slice_%t.vtm", 10 )
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [10]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(False)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,22 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystCxxOverlappingAMRExample)
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
set(Adaptor_SRCS
FEAdaptor.cxx
)
add_library(CxxOverlappingAMRExampleAdaptor ${Adaptor_SRCS})
target_link_libraries(CxxOverlappingAMRExampleAdaptor vtkPVPythonCatalyst)
add_executable(CxxOverlappingAMRExample FEDriver.cxx)
target_link_libraries(CxxOverlappingAMRExample CxxOverlappingAMRExampleAdaptor)
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
add_test(NAME CxxOverlappingAMRExampleTest COMMAND CxxOverlappingAMRExample
${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/feslicescript.py)
set_tests_properties(CxxOverlappingAMRExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,131 @@
#include <iostream>
#include "FEAdaptor.h"
#include <vtkAMRBox.h>
#include <vtkCompositeDataIterator.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkCPPythonScriptPipeline.h>
#include <vtkNew.h>
#include <vtkOverlappingAMR.h>
#include <vtkUniformGrid.h>
namespace
{
vtkCPProcessor* Processor = NULL;
vtkOverlappingAMR* VTKGrid;
void BuildVTKGrid()
{
if(VTKGrid == NULL)
{
// The grid structure isn't changing so we only build it
// the first time it's needed. If we needed the memory
// we could delete it and rebuild as necessary.
VTKGrid = vtkOverlappingAMR::New();
BuildVTKGrid();
}
int numberOfLevels = 3;
int blocksPerLevel[3] = {1, 1, 1};
VTKGrid->Initialize(numberOfLevels, blocksPerLevel);
VTKGrid->SetGridDescription(VTK_XYZ_GRID);
double origin[] = {0,0,0};
double level0Spacing[] = {4, 4, 4};
double level1Spacing[] = {2, 2, 2};
double level2Spacing[] = {1, 1, 1};
VTKGrid->SetOrigin(origin);
int level0Dims[] = {25, 25, 25};
vtkAMRBox level0Box(origin, level0Dims, level0Spacing, origin, VTK_XYZ_GRID);
int level1Dims[] = {20, 20, 20};
vtkAMRBox level1Box(origin, level1Dims, level1Spacing, origin, VTK_XYZ_GRID);
int level2Dims[] = {10, 10, 10};
vtkAMRBox level2Box(origin, level2Dims, level2Spacing, origin, VTK_XYZ_GRID);
VTKGrid->SetSpacing(0, level0Spacing);
VTKGrid->SetAMRBox(0, 0, level0Box);
VTKGrid->SetSpacing(1, level1Spacing);
VTKGrid->SetAMRBox(1, 0, level1Box);
VTKGrid->SetSpacing(2, level2Spacing);
VTKGrid->SetAMRBox(2, 0, level2Box);
VTKGrid->GenerateParentChildInformation();
// the highest level grid
vtkNew<vtkUniformGrid> level0Grid;
level0Grid->SetSpacing(level0Spacing);
level0Grid->SetOrigin(0, 0, 0);
level0Grid->SetExtent(0, 25, 0, 25, 0, 25);
VTKGrid->SetDataSet(0, 0, level0Grid.GetPointer());
// the mid-level grid
vtkNew<vtkUniformGrid> level1Grid0;
level1Grid0->SetSpacing(level1Spacing);
level1Grid0->SetExtent(0, 20, 0, 20, 0, 20);
VTKGrid->SetDataSet(1, 0, level1Grid0.GetPointer());
// the lowest level grid
vtkNew<vtkUniformGrid> level2Grid;
level2Grid->SetSpacing(level2Spacing);
level2Grid->SetExtent(0, 10, 0, 10, 0, 10);
VTKGrid->SetDataSet(2, 0, level2Grid.GetPointer());
}
}
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[])
{
if(Processor == NULL)
{
Processor = vtkCPProcessor::New();
Processor->Initialize();
}
else
{
Processor->RemoveAllPipelines();
}
for(int i=1;i<numScripts;i++)
{
vtkNew<vtkCPPythonScriptPipeline> pipeline;
pipeline->Initialize(scripts[i]);
Processor->AddPipeline(pipeline.GetPointer());
}
}
void Finalize()
{
if(Processor)
{
Processor->Delete();
Processor = NULL;
}
if(VTKGrid)
{
VTKGrid->Delete();
VTKGrid = NULL;
}
}
void CoProcess(double time, unsigned int timeStep, bool lastTimeStep)
{
vtkNew<vtkCPDataDescription> dataDescription;
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(Processor->RequestDataDescription(dataDescription.GetPointer()) != 0)
{
BuildVTKGrid();
dataDescription->GetInputDescriptionByName("input")->SetGrid(VTKGrid);
Processor->CoProcess(dataDescription.GetPointer());
}
}
} // end of Catalyst namespace

View File

@ -0,0 +1,13 @@
#ifndef FEADAPTOR_HEADER
#define FEADAPTOR_HEADER
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[]);
void Finalize();
void CoProcess(double time, unsigned int timeStep, bool lastTimeStep);
}
#endif

View File

@ -0,0 +1,25 @@
#include <algorithm>
#include <iostream>
#include <vector>
#include "FEAdaptor.h"
// Example of a C++ adaptor for a simulation code
// where the simulation code has a fixed topology
// grid. The grid in this case is a vtkOverlappingAMR
// data set without any attributes specified
// over the grid.
int main(int argc, char* argv[])
{
FEAdaptor::Initialize(argc, argv);
unsigned int numberOfTimeSteps = 100;
for(unsigned int timeStep=0;timeStep<numberOfTimeSteps;timeStep++)
{
// use a time step length of 0.1
double time = timeStep * 0.1;
FEAdaptor::CoProcess(time, timeStep, timeStep == numberOfTimeSteps-1);
}
FEAdaptor::Finalize();
return 0;
}

View File

@ -0,0 +1,83 @@
try: paraview.simple
except: from paraview.simple import *
from paraview import coprocessing
#--------------------------------------------------------------
# Code generated from cpstate.py to create the CoProcessor.
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
filename_10_vthb = coprocessor.CreateProducer( datadescription, "input" )
Slice1 = Slice( guiName="Slice1", Crinkleslice=0, SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane" )
Slice1.SliceType.Offset = 0.0
Slice1.SliceType.Origin = [1.9627925694422252, 50.0, 50.0]
Slice1.SliceType.Normal = [1.0, 0.0, 0.0]
ParallelMultiBlockDataSetWriter1 = coprocessor.CreateWriter( XMLMultiBlockDataWriter, "filename_%t.vtm", 10 )
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [10]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(False)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,41 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystCxxPVSMPipelineExample)
set(USE_CATALYST ON CACHE BOOL "Link the simulator with Catalyst")
if(USE_CATALYST)
# We have a C++ pipeline so we don't need to link with Python
# or vtkPVPythonCatalyst since vtkPVCatalyst is sufficient.
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVCatalyst vtkPVVTKExtensionsDefault)
include("${PARAVIEW_USE_FILE}")
set(Adaptor_SRCS
FEAdaptor.cxx
vtkCPPVSMPipeline.cxx
)
add_library(CxxPVSMPipelineExampleAdaptor ${Adaptor_SRCS})
target_link_libraries(CxxPVSMPipelineExampleAdaptor vtkPVCatalyst vtkPVVTKExtensionsDefault)
add_definitions("-DUSE_CATALYST")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
else()
find_package(MPI REQUIRED)
include_directories(${MPI_CXX_INCLUDE_PATH})
endif()
add_executable(CxxPVSMPipelineExample FEDriver.cxx FEDataStructures.cxx)
if(USE_CATALYST)
target_link_libraries(CxxPVSMPipelineExample LINK_PRIVATE CxxPVSMPipelineExampleAdaptor)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(CxxPVSMPipelineExample)
else()
target_link_libraries(CxxPVSMPipelineExample LINK_PRIVATE ${MPI_LIBRARIES})
endif()
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
add_test(NAME CxxPVSMPipelineExampleTest COMMAND CxxPVSMPipelineExample 5 output)
set_tests_properties(CxxPVSMPipelineExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,149 @@
#include <iostream>
#include "FEAdaptor.h"
#include "FEDataStructures.h"
#include "vtkCPPVSMPipeline.h"
#include <vtkCellData.h>
#include <vtkCellType.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkPointData.h>
#include <vtkUnstructuredGrid.h>
#include <vtkSMSession.h>
namespace
{
vtkCPProcessor* Processor = NULL;
vtkUnstructuredGrid* VTKGrid;
void BuildVTKGrid(Grid& grid)
{
// create the points information
vtkNew<vtkDoubleArray> pointArray;
pointArray->SetNumberOfComponents(3);
pointArray->SetArray(grid.GetPointsArray(), static_cast<vtkIdType>(grid.GetNumberOfPoints()*3), 1);
vtkNew<vtkPoints> points;
points->SetData(pointArray.GetPointer());
VTKGrid->SetPoints(points.GetPointer());
// create the cells
size_t numCells = grid.GetNumberOfCells();
VTKGrid->Allocate(static_cast<vtkIdType>(numCells*9));
for(size_t cell=0;cell<numCells;cell++)
{
unsigned int* cellPoints = grid.GetCellPoints(cell);
vtkIdType tmp[8] = {cellPoints[0], cellPoints[1], cellPoints[2], cellPoints[3],
cellPoints[4], cellPoints[5], cellPoints[6], cellPoints[7]};
VTKGrid->InsertNextCell(VTK_HEXAHEDRON, 8, tmp);
}
}
void UpdateVTKAttributes(Grid& grid, Attributes& attributes)
{
if(VTKGrid->GetPointData()->GetNumberOfArrays() == 0)
{
// velocity array
vtkNew<vtkDoubleArray> velocity;
velocity->SetName("velocity");
velocity->SetNumberOfComponents(3);
velocity->SetNumberOfTuples(static_cast<vtkIdType>(grid.GetNumberOfPoints()));
VTKGrid->GetPointData()->AddArray(velocity.GetPointer());
}
if(VTKGrid->GetCellData()->GetNumberOfArrays() == 0)
{
// pressure array
vtkNew<vtkFloatArray> pressure;
pressure->SetName("pressure");
pressure->SetNumberOfComponents(1);
VTKGrid->GetCellData()->AddArray(pressure.GetPointer());
}
vtkDoubleArray* velocity = vtkDoubleArray::SafeDownCast(
VTKGrid->GetPointData()->GetArray("velocity"));
// The velocity array is ordered as vx0,vx1,vx2,..,vy0,vy1,vy2,..,vz0,vz1,vz2,..
// so we need to create a full copy of it with VTK's ordering of
// vx0,vy0,vz0,vx1,vy1,vz1,..
double* velocityData = attributes.GetVelocityArray();
vtkIdType numTuples = velocity->GetNumberOfTuples();
for(vtkIdType i=0;i<numTuples;i++)
{
double values[3] = {velocityData[i], velocityData[i+numTuples],
velocityData[i+2*numTuples]};
velocity->SetTupleValue(i, values);
}
vtkFloatArray* pressure = vtkFloatArray::SafeDownCast(
VTKGrid->GetCellData()->GetArray("pressure"));
// The pressure array is a scalar array so we can reuse
// memory as long as we ordered the points properly.
float* pressureData = attributes.GetPressureArray();
pressure->SetArray(pressureData, static_cast<vtkIdType>(grid.GetNumberOfCells()), 1);
}
void BuildVTKDataStructures(Grid& grid, Attributes& attributes)
{
if(VTKGrid == NULL)
{
// The grid structure isn't changing so we only build it
// the first time it's needed. If we needed the memory
// we could delete it and rebuild as necessary.
VTKGrid = vtkUnstructuredGrid::New();
BuildVTKGrid(grid);
}
UpdateVTKAttributes(grid, attributes);
}
}
namespace FEAdaptor
{
void Initialize(int outputFrequency, std::string fileName)
{
if(Processor == NULL)
{
Processor = vtkCPProcessor::New();
Processor->Initialize();
}
vtkSMSession::ConnectToSelf();
vtkNew<vtkCPPVSMPipeline> pipeline;
pipeline->Initialize(outputFrequency, fileName);
Processor->AddPipeline(pipeline.GetPointer());
}
void Finalize()
{
if(Processor)
{
Processor->Delete();
Processor = NULL;
}
if(VTKGrid)
{
VTKGrid->Delete();
VTKGrid = NULL;
}
}
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep)
{
vtkNew<vtkCPDataDescription> dataDescription;
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(Processor->RequestDataDescription(dataDescription.GetPointer()) != 0)
{
BuildVTKDataStructures(grid, attributes);
dataDescription->GetInputDescriptionByName("input")->SetGrid(VTKGrid);
Processor->CoProcess(dataDescription.GetPointer());
}
}
} // end of Catalyst namespace

View File

@ -0,0 +1,19 @@
#ifndef FEADAPTOR_HEADER
#define FEADAPTOR_HEADER
#include <string>
class Attributes;
class Grid;
namespace FEAdaptor
{
void Initialize(int outputFrequency, std::string fileName);
void Finalize();
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep);
}
#endif

View File

@ -0,0 +1,153 @@
#include "FEDataStructures.h"
#include <mpi.h>
#include <iostream>
Grid::Grid()
{}
void Grid::Initialize(const unsigned int numPoints[3], const double spacing[3] )
{
if(numPoints[0] == 0 || numPoints[1] == 0 || numPoints[2] == 0)
{
std::cerr << "Must have a non-zero amount of points in each direction.\n";
}
// in parallel, we do a simple partitioning in the x-direction.
int mpiSize = 1;
int mpiRank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
unsigned int startXPoint = mpiRank*numPoints[0]/mpiSize;
unsigned int endXPoint = (mpiRank+1)*numPoints[0]/mpiSize;
if(mpiSize != mpiRank+1)
{
endXPoint++;
}
// create the points -- slowest in the x and fastest in the z directions
double coord[3] = {0,0,0};
for(unsigned int i=startXPoint;i<endXPoint;i++)
{
coord[0] = i*spacing[0];
for(unsigned int j=0;j<numPoints[1];j++)
{
coord[1] = j*spacing[1];
for(unsigned int k=0;k<numPoints[2];k++)
{
coord[2] = k*spacing[2];
// add the coordinate to the end of the vector
std::copy(coord, coord+3, std::back_inserter(this->Points));
}
}
}
// create the hex cells
unsigned int cellPoints[8];
unsigned int numXPoints = endXPoint - startXPoint;
for(unsigned int i=0;i<numXPoints-1;i++)
{
for(unsigned int j=0;j<numPoints[1]-1;j++)
{
for(unsigned int k=0;k<numPoints[2]-1;k++)
{
cellPoints[0] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
cellPoints[1] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
cellPoints[2] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
cellPoints[3] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
cellPoints[4] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
cellPoints[5] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
cellPoints[6] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
cellPoints[7] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
std::copy(cellPoints, cellPoints+8, std::back_inserter(this->Cells));
}
}
}
}
size_t Grid::GetNumberOfPoints()
{
return this->Points.size()/3;
}
size_t Grid::GetNumberOfCells()
{
return this->Cells.size()/8;
}
double* Grid::GetPointsArray()
{
if(this->Points.empty())
{
return NULL;
}
return &(this->Points[0]);
}
double* Grid::GetPoint(size_t pointId)
{
if(pointId >= this->Points.size())
{
return NULL;
}
return &(this->Points[pointId*3]);
}
unsigned int* Grid::GetCellPoints(size_t cellId)
{
if(cellId >= this->Cells.size())
{
return NULL;
}
return &(this->Cells[cellId*8]);
}
Attributes::Attributes()
{
this->GridPtr = NULL;
}
void Attributes::Initialize(Grid* grid)
{
this->GridPtr = grid;
}
void Attributes::UpdateFields(double time)
{
size_t numPoints = this->GridPtr->GetNumberOfPoints();
this->Velocity.resize(numPoints*3);
for(size_t pt=0;pt<numPoints;pt++)
{
double* coord = this->GridPtr->GetPoint(pt);
this->Velocity[pt] = coord[1]*time;
}
std::fill(this->Velocity.begin()+numPoints, this->Velocity.end(), 0.);
size_t numCells = this->GridPtr->GetNumberOfCells();
this->Pressure.resize(numCells);
std::fill(this->Pressure.begin(), this->Pressure.end(), 1.);
}
double* Attributes::GetVelocityArray()
{
if(this->Velocity.empty())
{
return NULL;
}
return &this->Velocity[0];
}
float* Attributes::GetPressureArray()
{
if(this->Pressure.empty())
{
return NULL;
}
return &this->Pressure[0];
}

View File

@ -0,0 +1,41 @@
#ifndef FEDATASTRUCTURES_HEADER
#define FEDATASTRUCTURES_HEADER
#include <cstddef>
#include <vector>
class Grid
{
public:
Grid();
void Initialize(const unsigned int numPoints[3], const double spacing[3]);
size_t GetNumberOfPoints();
size_t GetNumberOfCells();
double* GetPointsArray();
double* GetPoint(size_t pointId);
unsigned int* GetCellPoints(size_t cellId);
private:
std::vector<double> Points;
std::vector<unsigned int> Cells;
};
class Attributes
{
// A class for generating and storing point and cell fields.
// Velocity is stored at the points and pressure is stored
// for the cells. The current velocity profile is for a
// shearing flow with U(y,t) = y*t, V = 0 and W = 0.
// Pressure is constant through the domain.
public:
Attributes();
void Initialize(Grid* grid);
void UpdateFields(double time);
double* GetVelocityArray();
float* GetPressureArray();
private:
std::vector<double> Velocity;
std::vector<float> Pressure;
Grid* GridPtr;
};
#endif

View File

@ -0,0 +1,74 @@
#include "FEDataStructures.h"
#include <mpi.h>
#ifdef USE_CATALYST
#include <cstdlib>
#include "FEAdaptor.h"
#include <iostream>
#endif
// Example of a C++ adaptor for a simulation code
// where we use a hard-coded ParaView server-manager
// C++ pipeline. The simulation code has a fixed topology
// grid. We treat the grid as an unstructured
// grid even though in the example provided it
// would be best described as a vtkImageData.
// Also, the points are stored in an inconsistent
// manner with respect to the velocity vector.
// This is purposefully done to demonstrate
// the different approaches for getting data
// into Catalyst. The hard-coded C++ pipeline
// uses a slice filter to cut 4 planes through
// the domain.
// Note that through configuration
// that the driver can be run without linking
// to Catalyst.
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
Grid grid;
unsigned int numPoints[3] = {70, 60, 44};
double spacing[3] = {1, 1.1, 1.3};
grid.Initialize(numPoints, spacing);
Attributes attributes;
attributes.Initialize(&grid);
#ifdef USE_CATALYST
bool doCoProcessing = false;
if(argc == 3)
{
doCoProcessing = true;
// pass in the number of time steps and base file name.
FEAdaptor::Initialize(atoi(argv[1]), argv[2]);
}
else
{
std::cerr << "To run with Catalyst you must pass in the output frequency and the base file name.\n";
}
#endif
unsigned int numberOfTimeSteps = 15;
for(unsigned int timeStep=0;timeStep<numberOfTimeSteps;timeStep++)
{
// use a time step length of 0.1
double time = timeStep * 0.1;
attributes.UpdateFields(time);
#ifdef USE_CATALYST
if(doCoProcessing)
{
FEAdaptor::CoProcess(grid, attributes, time, timeStep,
timeStep == numberOfTimeSteps-1);
}
#endif
}
#ifdef USE_CATALYST
if(doCoProcessing)
{
FEAdaptor::Finalize();
}
#endif
MPI_Finalize();
return 0;
}

View File

@ -0,0 +1,163 @@
#include "vtkCPPVSMPipeline.h"
#include <vtkCommunicator.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkMultiProcessController.h>
#include <vtkNew.h>
#include <vtkObjectFactory.h>
#include <vtkPVTrivialProducer.h>
#include <vtkSmartPointer.h>
#include <vtkSMDoubleVectorProperty.h>
#include <vtkSMInputProperty.h>
#include <vtkSMSourceProxy.h>
#include <vtkSMSessionProxyManager.h>
#include <vtkSMProxyManager.h>
#include <vtkSMStringVectorProperty.h>
#include <vtkSMWriterProxy.h>
#include <vtkUnstructuredGrid.h>
#include <string>
#include <sstream>
vtkStandardNewMacro(vtkCPPVSMPipeline);
//----------------------------------------------------------------------------
vtkCPPVSMPipeline::vtkCPPVSMPipeline()
{
this->OutputFrequency = 0;
}
//----------------------------------------------------------------------------
vtkCPPVSMPipeline::~vtkCPPVSMPipeline()
{
}
//----------------------------------------------------------------------------
void vtkCPPVSMPipeline::Initialize(int outputFrequency, std::string& fileName)
{
this->OutputFrequency = outputFrequency;
this->FileName = fileName;
}
//----------------------------------------------------------------------------
int vtkCPPVSMPipeline::RequestDataDescription(
vtkCPDataDescription* dataDescription)
{
if(!dataDescription)
{
vtkWarningMacro("dataDescription is NULL.");
return 0;
}
if(this->FileName.empty())
{
vtkWarningMacro("No output file name given to output results to.");
return 0;
}
if(dataDescription->GetForceOutput() == true ||
(this->OutputFrequency != 0 &&
dataDescription->GetTimeStep() % this->OutputFrequency == 0) )
{
dataDescription->GetInputDescriptionByName("input")->AllFieldsOn();
dataDescription->GetInputDescriptionByName("input")->GenerateMeshOn();
return 1;
}
return 0;
}
//----------------------------------------------------------------------------
int vtkCPPVSMPipeline::CoProcess(
vtkCPDataDescription* dataDescription)
{
if(!dataDescription)
{
vtkWarningMacro("DataDescription is NULL");
return 0;
}
vtkUnstructuredGrid* grid = vtkUnstructuredGrid::SafeDownCast(
dataDescription->GetInputDescriptionByName("input")->GetGrid());
if(grid == NULL)
{
vtkWarningMacro("DataDescription is missing input unstructured grid.");
return 0;
}
if(this->RequestDataDescription(dataDescription) == 0)
{
return 1;
}
vtkSMProxyManager* proxyManager = vtkSMProxyManager::GetProxyManager();
vtkSMSessionProxyManager* sessionProxyManager =
proxyManager->GetActiveSessionProxyManager();
// Create a vtkPVTrivialProducer and set its output
// to be the input grid.
vtkSmartPointer<vtkSMSourceProxy> producer;
producer.TakeReference(
vtkSMSourceProxy::SafeDownCast(
sessionProxyManager->NewProxy("sources", "PVTrivialProducer")));
producer->UpdateVTKObjects();
vtkObjectBase* clientSideObject = producer->GetClientSideObject();
vtkPVTrivialProducer* realProducer =
vtkPVTrivialProducer::SafeDownCast(clientSideObject);
realProducer->SetOutput(grid);
// Create a slice filter and set the cut type to plane
vtkSmartPointer<vtkSMSourceProxy> slice;
slice.TakeReference(
vtkSMSourceProxy::SafeDownCast(sessionProxyManager->NewProxy("filters", "Cut")));
vtkSMInputProperty* sliceInputConnection =
vtkSMInputProperty::SafeDownCast(slice->GetProperty("Input"));
vtkSMProxyProperty* cutType =
vtkSMProxyProperty::SafeDownCast(slice->GetProperty("CutFunction"));
vtkSmartPointer<vtkSMProxy> cutPlane;
cutPlane.TakeReference(
sessionProxyManager->NewProxy("implicit_functions", "Plane"));
cutPlane->UpdatePropertyInformation();
cutPlane->UpdateVTKObjects();
cutType->SetProxy(0, cutPlane);
cutType->UpdateAllInputs();
// We set 4 offsets, i.e. 4 parallel cut planes for the slice filter
vtkSMDoubleVectorProperty* offsets =
vtkSMDoubleVectorProperty::SafeDownCast(slice->GetProperty("ContourValues"));
offsets->SetElements4(1, 11, 21, 31);
producer->UpdateVTKObjects();
sliceInputConnection->SetInputConnection(0, producer, 0);
slice->UpdatePropertyInformation();
slice->UpdateVTKObjects();
// Finally, create the parallel poly data writer, set the
// filename and then update the pipeline.
vtkSmartPointer<vtkSMWriterProxy> writer;
writer.TakeReference(
vtkSMWriterProxy::SafeDownCast(sessionProxyManager->NewProxy("writers", "XMLPPolyDataWriter")));
vtkSMInputProperty* writerInputConnection =
vtkSMInputProperty::SafeDownCast(writer->GetProperty("Input"));
writerInputConnection->SetInputConnection(0, slice, 0);
vtkSMStringVectorProperty* fileName =
vtkSMStringVectorProperty::SafeDownCast(writer->GetProperty("FileName"));
std::ostringstream o;
o << dataDescription->GetTimeStep();
std::string name = this->FileName + o.str() + ".pvtp";
fileName->SetElement(0, name.c_str());
writer->UpdatePropertyInformation();
writer->UpdateVTKObjects();
writer->UpdatePipeline();
return 1;
}
//----------------------------------------------------------------------------
void vtkCPPVSMPipeline::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "OutputFrequency: " << this->OutputFrequency << "\n";
os << indent << "FileName: " << this->FileName << "\n";
}

View File

@ -0,0 +1,34 @@
#ifndef VTKCPPVSMPIPELINE_H
#define VTKCPPVSMPIPELINE_H
#include <vtkCPPipeline.h>
#include <string>
class vtkCPDataDescription;
class vtkCPPythonHelper;
class vtkCPPVSMPipeline : public vtkCPPipeline
{
public:
static vtkCPPVSMPipeline* New();
vtkTypeMacro(vtkCPPVSMPipeline,vtkCPPipeline);
virtual void PrintSelf(ostream& os, vtkIndent indent);
virtual void Initialize(int outputFrequency, std::string& fileName);
virtual int RequestDataDescription(vtkCPDataDescription* dataDescription);
virtual int CoProcess(vtkCPDataDescription* dataDescription);
protected:
vtkCPPVSMPipeline();
virtual ~vtkCPPVSMPipeline();
private:
vtkCPPVSMPipeline(const vtkCPPVSMPipeline&); // Not implemented
void operator=(const vtkCPPVSMPipeline&); // Not implemented
int OutputFrequency;
std::string FileName;
};
#endif

View File

@ -0,0 +1,58 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystCxxParticlePathExample)
set(USE_CATALYST ON CACHE BOOL "Link the simulator with Catalyst")
if(USE_CATALYST)
# we need 5.0 for the proper in situ particle path filter
find_package(ParaView 5.0 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
set(Adaptor_SRCS
FEAdaptor.cxx
)
add_library(CxxParticlePathExampleAdaptor ${Adaptor_SRCS})
target_link_libraries(CxxParticlePathExampleAdaptor vtkPVPythonCatalyst vtkParallelMPI)
add_definitions("-DUSE_CATALYST")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
else()
find_package(MPI REQUIRED)
include_directories(${MPI_C_INCLUDE_PATH})
endif()
add_executable(CxxParticlePathExample FEDriver.cxx FEDataStructures.cxx)
if(USE_CATALYST)
target_link_libraries(CxxParticlePathExample LINK_PRIVATE CxxParticlePathExampleAdaptor)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(CxxParticlePathExample)
else()
target_link_libraries(CxxParticlePathExample LINK_PRIVATE ${MPI_LIBRARIES})
endif()
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
if(PARAVIEW_TEST_OUTPUT_DIR)
set(TEST_OUTPUT_DIR ${PARAVIEW_TEST_OUTPUT_DIR})
else(PARAVIEW_TEST_OUTPUT_DIR)
set(TEST_OUTPUT_DIR "${CMAKE_BINARY_DIR}/Testing/Temporary")
endif(PARAVIEW_TEST_OUTPUT_DIR)
add_test(NAME CxxParticlePathExampleTest
COMMAND ${CMAKE_COMMAND}
-DCOPROCESSING_TEST_DRIVER:FILEPATH=$<TARGET_FILE:CxxParticlePathExample>
-DCOPROCESSING_TEST_DIR:PATH=${TEST_OUTPUT_DIR}
-DCOPROCESSING_SOURCE_DIR=${CMAKE_CURRENT_SOURCE_DIR}
-DPVBATCH:FILEPATH=$<TARGET_FILE:pvbatch>
-DMPIEXEC:FILEPATH=${MPIEXEC}
-DMPIEXEC_NUMPROC_FLAG:STRING=${MPIEXEC_NUMPROC_FLAG}
-DMPIEXEC_NUMPROCS=4
-DMPIEXEC_PREFLAGS:STRING=${MPIEXEC_PREFLAGS}
-DVTK_MPI_POSTFLAGS:STRING=${VTK_MPI_POSTFLAGS}
-P ${CMAKE_CURRENT_SOURCE_DIR}/ParticlePathTesting.cmake)
set_tests_properties(CxxParticlePathExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif (BUILD_TESTING)

View File

@ -0,0 +1,142 @@
#include <iostream>
#include "FEAdaptor.h"
#include "FEDataStructures.h"
#include <vtkCellData.h>
#include <vtkCellType.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkCPPythonScriptPipeline.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkPointData.h>
#include <vtkUnstructuredGrid.h>
namespace
{
vtkCPProcessor* Processor = NULL;
void BuildVTKGrid(Grid& grid, vtkUnstructuredGrid* vtkgrid)
{
// create the points information
vtkNew<vtkDoubleArray> pointArray;
pointArray->SetNumberOfComponents(3);
pointArray->SetArray(grid.GetPointsArray(), static_cast<vtkIdType>(grid.GetNumberOfPoints()*3), 1);
vtkNew<vtkPoints> points;
points->SetData(pointArray.GetPointer());
vtkgrid->SetPoints(points.GetPointer());
// create the cells
size_t numCells = grid.GetNumberOfCells();
vtkgrid->Allocate(static_cast<vtkIdType>(numCells*9));
for(size_t cell=0;cell<numCells;cell++)
{
unsigned int* cellPoints = grid.GetCellPoints(cell);
vtkIdType tmp[8] = {cellPoints[0], cellPoints[1], cellPoints[2], cellPoints[3],
cellPoints[4], cellPoints[5], cellPoints[6], cellPoints[7]};
vtkgrid->InsertNextCell(VTK_HEXAHEDRON, 8, tmp);
}
}
void UpdateVTKAttributes(Grid& grid, Attributes& attributes, vtkUnstructuredGrid* vtkgrid)
{
if(vtkgrid->GetPointData()->GetNumberOfArrays() == 0)
{
// velocity array
vtkNew<vtkDoubleArray> velocity;
velocity->SetName("velocity");
velocity->SetNumberOfComponents(3);
velocity->SetNumberOfTuples(static_cast<vtkIdType>(grid.GetNumberOfPoints()));
vtkgrid->GetPointData()->AddArray(velocity.GetPointer());
}
if(vtkgrid->GetCellData()->GetNumberOfArrays() == 0)
{
// pressure array
vtkNew<vtkFloatArray> pressure;
pressure->SetName("pressure");
pressure->SetNumberOfComponents(1);
vtkgrid->GetCellData()->AddArray(pressure.GetPointer());
}
vtkDoubleArray* velocity = vtkDoubleArray::SafeDownCast(
vtkgrid->GetPointData()->GetArray("velocity"));
// The velocity array is ordered as vx0,vx1,vx2,..,vy0,vy1,vy2,..,vz0,vz1,vz2,..
// so we need to create a full copy of it with VTK's ordering of
// vx0,vy0,vz0,vx1,vy1,vz1,..
double* velocityData = attributes.GetVelocityArray();
vtkIdType numTuples = velocity->GetNumberOfTuples();
for(vtkIdType i=0;i<numTuples;i++)
{
double values[3] = {velocityData[i], velocityData[i+numTuples],
velocityData[i+2*numTuples]};
velocity->SetTupleValue(i, values);
}
vtkFloatArray* pressure = vtkFloatArray::SafeDownCast(
vtkgrid->GetCellData()->GetArray("pressure"));
// The pressure array is a scalar array so we can reuse
// memory as long as we ordered the points properly.
float* pressureData = attributes.GetPressureArray();
pressure->SetArray(pressureData, static_cast<vtkIdType>(grid.GetNumberOfCells()), 1);
}
void BuildVTKDataStructures(Grid& grid, Attributes& attributes, vtkUnstructuredGrid* vtkgrid)
{
BuildVTKGrid(grid, vtkgrid);
UpdateVTKAttributes(grid, attributes, vtkgrid);
}
}
namespace FEAdaptor
{
void Initialize(std::vector<std::string>& scripts)
{
if(Processor == NULL)
{
Processor = vtkCPProcessor::New();
Processor->Initialize();
}
else
{
Processor->RemoveAllPipelines();
}
for(std::vector<std::string>::iterator it=scripts.begin();it!=scripts.end();it++)
{
vtkNew<vtkCPPythonScriptPipeline> pipeline;
pipeline->Initialize(it->c_str());
Processor->AddPipeline(pipeline.GetPointer());
}
}
void Finalize()
{
if(Processor)
{
Processor->Delete();
Processor = NULL;
}
}
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep)
{
vtkNew<vtkCPDataDescription> dataDescription;
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(Processor->RequestDataDescription(dataDescription.GetPointer()) != 0)
{
vtkNew<vtkUnstructuredGrid> vtkgrid;
BuildVTKDataStructures(grid, attributes, vtkgrid.GetPointer());
dataDescription->GetInputDescriptionByName("input")->SetGrid(vtkgrid.GetPointer());
Processor->CoProcess(dataDescription.GetPointer());
}
}
} // end of Catalyst namespace

View File

@ -0,0 +1,20 @@
#ifndef FEADAPTOR_HEADER
#define FEADAPTOR_HEADER
class Attributes;
class Grid;
#include <string>
#include <vector>
namespace FEAdaptor
{
void Initialize(std::vector<std::string>& scripts);
void Finalize();
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep);
}
#endif

View File

@ -0,0 +1,154 @@
#include "FEDataStructures.h"
#include <mpi.h>
#include <iostream>
Grid::Grid()
{}
void Grid::Initialize(const unsigned int numPoints[3], const double spacing[3] )
{
if(numPoints[0] == 0 || numPoints[1] == 0 || numPoints[2] == 0)
{
std::cerr << "Must have a non-zero amount of points in each direction.\n";
}
// in parallel, we do a simple partitioning in the x-direction.
int mpiSize = 1;
int mpiRank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
unsigned int startXPoint = mpiRank*numPoints[0]/mpiSize;
unsigned int endXPoint = (mpiRank+1)*numPoints[0]/mpiSize;
if(mpiSize != mpiRank+1)
{
endXPoint++;
}
// create the points -- slowest in the x and fastest in the z directions
double coord[3] = {0,0,0};
for(unsigned int i=startXPoint;i<endXPoint;i++)
{
coord[0] = i*spacing[0];
for(unsigned int j=0;j<numPoints[1];j++)
{
coord[1] = j*spacing[1];
for(unsigned int k=0;k<numPoints[2];k++)
{
coord[2] = k*spacing[2];
// add the coordinate to the end of the vector
std::copy(coord, coord+3, std::back_inserter(this->Points));
}
}
}
// create the hex cells
unsigned int cellPoints[8];
unsigned int numXPoints = endXPoint - startXPoint;
for(unsigned int i=0;i<numXPoints-1;i++)
{
for(unsigned int j=0;j<numPoints[1]-1;j++)
{
for(unsigned int k=0;k<numPoints[2]-1;k++)
{
cellPoints[0] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
cellPoints[1] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
cellPoints[2] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
cellPoints[3] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
cellPoints[4] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
cellPoints[5] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
cellPoints[6] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
cellPoints[7] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
std::copy(cellPoints, cellPoints+8, std::back_inserter(this->Cells));
}
}
}
}
size_t Grid::GetNumberOfPoints()
{
return this->Points.size()/3;
}
size_t Grid::GetNumberOfCells()
{
return this->Cells.size()/8;
}
double* Grid::GetPointsArray()
{
if(this->Points.empty())
{
return NULL;
}
return &(this->Points[0]);
}
double* Grid::GetPoint(size_t pointId)
{
if(pointId >= this->Points.size())
{
return NULL;
}
return &(this->Points[pointId*3]);
}
unsigned int* Grid::GetCellPoints(size_t cellId)
{
if(cellId >= this->Cells.size())
{
return NULL;
}
return &(this->Cells[cellId*8]);
}
Attributes::Attributes()
{
this->GridPtr = NULL;
}
void Attributes::Initialize(Grid* grid)
{
this->GridPtr = grid;
}
void Attributes::UpdateFields(double time)
{
size_t numPoints = this->GridPtr->GetNumberOfPoints();
this->Velocity.resize(numPoints*3);
for(size_t pt=0;pt<numPoints;pt++)
{
double* coord = this->GridPtr->GetPoint(pt);
this->Velocity[pt] = coord[1]*time;
//this->Velocity[pt] = time;
}
std::fill(this->Velocity.begin()+numPoints, this->Velocity.end(), 0.);
size_t numCells = this->GridPtr->GetNumberOfCells();
this->Pressure.resize(numCells);
std::fill(this->Pressure.begin(), this->Pressure.end(), 1.);
}
double* Attributes::GetVelocityArray()
{
if(this->Velocity.empty())
{
return NULL;
}
return &this->Velocity[0];
}
float* Attributes::GetPressureArray()
{
if(this->Pressure.empty())
{
return NULL;
}
return &this->Pressure[0];
}

View File

@ -0,0 +1,41 @@
#ifndef FEDATASTRUCTURES_HEADER
#define FEDATASTRUCTURES_HEADER
#include <cstddef>
#include <vector>
class Grid
{
public:
Grid();
void Initialize(const unsigned int numPoints[3], const double spacing[3]);
size_t GetNumberOfPoints();
size_t GetNumberOfCells();
double* GetPointsArray();
double* GetPoint(size_t pointId);
unsigned int* GetCellPoints(size_t cellId);
private:
std::vector<double> Points;
std::vector<unsigned int> Cells;
};
class Attributes
{
// A class for generating and storing point and cell fields.
// Velocity is stored at the points and pressure is stored
// for the cells. The current velocity profile is for a
// shearing flow with U(y,t) = y*t, V = 0 and W = 0.
// Pressure is constant through the domain.
public:
Attributes();
void Initialize(Grid* grid);
void UpdateFields(double time);
double* GetVelocityArray();
float* GetPressureArray();
private:
std::vector<double> Velocity;
std::vector<float> Pressure;
Grid* GridPtr;
};
#endif

View File

@ -0,0 +1,81 @@
#include "FEDataStructures.h"
#include <mpi.h>
#include <string>
#include <vector>
#ifdef USE_CATALYST
#include "FEAdaptor.h"
#endif
#include <stdlib.h>
#include <cstring>
// Example of a C++ adaptor for a simulation code
// where the simulation code has a fixed topology
// grid. We treat the grid as an unstructured
// grid even though in the example provided it
// would be best described as a vtkImageData.
// Also, the points are stored in an inconsistent
// manner with respect to the velocity vector.
// This is purposefully done to demonstrate
// the different approaches for getting data
// into Catalyst. The simulation can be run
// from a restarted time step with the
// -- restart <time step> command line argument.
// All other arguments are considered to be input
// script. Note that through configuration
// that the driver can be run without linking
// to Catalyst.
#include <unistd.h>
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
Grid grid;
unsigned int numPoints[3] = {70, 60, 44};
double spacing[3] = {1, 1.1, 1.3};
grid.Initialize(numPoints, spacing);
Attributes attributes;
attributes.Initialize(&grid);
// we are doing a restarted simulation
unsigned int startTimeStep = 0;
std::vector<std::string> scripts;
for(int i=1;i<argc;i++)
{
if(strcmp(argv[i], "--restart") == 0)
{
if(i+1 < argc)
{
startTimeStep = static_cast<unsigned int>(atoi(argv[2]));
i++;
}
}
else
{
scripts.push_back(argv[i]);
}
}
#ifdef USE_CATALYST
FEAdaptor::Initialize(scripts);
#endif
unsigned int numberOfTimeSteps = 50;
for(unsigned int timeStep=startTimeStep;timeStep<=startTimeStep+numberOfTimeSteps;timeStep++)
{
// use a time step length of 0.018
double time = timeStep * 0.018;
attributes.UpdateFields(time);
#ifdef USE_CATALYST
FEAdaptor::CoProcess(grid, attributes, time, timeStep, timeStep == numberOfTimeSteps+startTimeStep);
#endif
}
#ifdef USE_CATALYST
FEAdaptor::Finalize();
#endif
MPI_Finalize();
return 0;
}

View File

@ -0,0 +1,61 @@
# CoProcessing test expects the following arguments to be passed to cmake using
# -DFoo=BAR arguments.
# COPROCESSING_TEST_DRIVER -- path to CxxParticlePathExample
# COPROCESSING_TEST_DIR -- path to temporary dir
# COPROCESSING_SOURCE_DIR -- path to where the source code with the python scripts are
# PVBATCH
# MPIEXEC
# MPIEXEC_NUMPROC_FLAG
# MPIEXEC_NUMPROCS
# MPIEXEC_PREFLAGS
# VTK_MPI_POSTFLAGS
# remove result files generated by the test
file(REMOVE "${COPROCESSING_TEST_DIR}/particles*vtp" )
if(NOT EXISTS "${COPROCESSING_TEST_DRIVER}")
message(FATAL_ERROR "'${COPROCESSING_TEST_DRIVER}' does not exist")
endif()
message("Executing :
${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROCS} ${MPIEXEC_PREFLAGS}
\"${COPROCESSING_TEST_DRIVER}\"
\"${COPROCESSING_SOURCE_DIR}/SampleScripts/particlepath.py\"")
execute_process(COMMAND
${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROCS} ${MPIEXEC_PREFLAGS}
"${COPROCESSING_TEST_DRIVER}"
"${COPROCESSING_SOURCE_DIR}/SampleScripts/particlepath.py"
WORKING_DIRECTORY ${COPROCESSING_TEST_DIR}
RESULT_VARIABLE rv)
if(NOT rv EQUAL 0)
message(FATAL_ERROR "Test executable return value was ${rv}")
endif()
# below is the restarted "simulation"
message("Executing (restart):
${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROCS} ${MPIEXEC_PREFLAGS}
\"${COPROCESSING_TEST_DRIVER}\" --restart 50
\"${COPROCESSING_SOURCE_DIR}/SampleScripts/particlepath.py\"")
execute_process(COMMAND
${MPIEXEC} ${MPIEXEC_NUMPROC_FLAG} ${MPIEXEC_NUMPROCS} ${MPIEXEC_PREFLAGS}
"${COPROCESSING_TEST_DRIVER}" --restart 50
"${COPROCESSING_SOURCE_DIR}/SampleScripts/particlepath.py"
WORKING_DIRECTORY ${COPROCESSING_TEST_DIR}
RESULT_VARIABLE rv)
if(NOT rv EQUAL 0)
message(FATAL_ERROR "Test executable (restart) return value was ${rv}")
endif()
message("Executing :
${PVBATCH} ${COPROCESSING_SOURCE_DIR}/TestScripts/verifyparticlepath.py ${COPROCESSING_TEST_DIR}")
execute_process(COMMAND "${PVBATCH}" "${COPROCESSING_SOURCE_DIR}/TestScripts/verifyparticlepath.py" "${COPROCESSING_TEST_DIR}"
RESULT_VARIABLE rv
WORKING_DIRECTORY ${COPROCESSING_TEST_DIR})
if(NOT rv EQUAL 0)
message(FATAL_ERROR "verifyparticlepath.py failed.")
endif()

View File

@ -0,0 +1,131 @@
from paraview.simple import *
from paraview import coprocessing
outputfrequency = 1
reinjectionfrequency = 70
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
# state file generated using paraview version 4.4.0-117-ge0a3d77
# ----------------------------------------------------------------
# setup the data processing pipelines
# ----------------------------------------------------------------
#### disable automatic camera reset on 'Show'
paraview.simple._DisableFirstRenderCameraReset()
# create a new 'Line' for seed sources
line1 = Line()
line1.Point1 = [1., 1., 30.]
line1.Point2 = [1., 64., 30.]
# create a producer from a simulation input
fullgrid_99pvtu = coprocessor.CreateProducer(datadescription, 'input')
# create a new 'ParticlePath'
# disable resetting the cache so that the particle path filter works in situ
# and only updates from previously computed information.
particlePath1 = InSituParticlePath(Input=fullgrid_99pvtu, SeedSource=line1, DisableResetCache=1)
particlePath1.SelectInputVectors = ['POINTS', 'velocity']
# don't save particle locations from previous time steps. they can take
# up a surprising amount of memory for long running simulations.
particlePath1.ClearCache = 1
# if we're starting from a restarted simulation, the following are
# used to specify the time step for the restarted simulation and
# the input for the previously advected particles to continue
# advecting them
if datadescription.GetTimeStep() != 0:
restartparticles = XMLPartitionedPolydataReader(FileName='particles_50.pvtp')
particlePath1.RestartSource = restartparticles
particlePath1.FirstTimeStep = datadescription.GetTimeStep()
particlePath1.RestartedSimulation = 1
# create a new 'Parallel PolyData Writer'
parallelPolyDataWriter1 = servermanager.writers.XMLPPolyDataWriter(Input=particlePath1)
# register the writer with coprocessor
# and provide it with information such as the filename to use,
# how frequently to write the data, etc.
coprocessor.RegisterWriter(parallelPolyDataWriter1, filename='particles_%t.pvtp', freq=outputfrequency)
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
# these are the frequencies at which the coprocessor updates. for
# particle paths this is done every time step
freqs = {'input': [1]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(False, 1)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
#if not coprocessor.__PipelineCreated:
coprocessor.UpdateProducers(datadescription)
# tell the particle path filter how far to integrate in time (i.e. our current time)
coprocessor.Pipeline.particlePath1.TerminationTime = datadescription.GetTime()
# specify reinjection frequency manually so that reinjection
# occurs based on the simulation time step to avoid restart issues since
# the particle path filter only knows how many time steps
# it has been updated. this is the same when the simulation has not been
# restarted.
timestep = datadescription.GetTimeStep()
if timestep % reinjectionfrequency == 0:
coprocessor.Pipeline.particlePath1.ForceReinjectionEveryNSteps = 1
else:
coprocessor.Pipeline.particlePath1.ForceReinjectionEveryNSteps = timestep+1
coprocessor.Pipeline.particlePath1.UpdatePipeline()
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,91 @@
import sys
print 'running the test script with args', sys.argv
if len(sys.argv) != 2:
print 'need to pass in the test directory location'
sys.exit(1)
# the velocity is [y*t, 0, 0] and the time step length is 0.018
# the beginning x-location for the seeds is 1
# the y location for the seeds are [1, 11.5, 22, 32.5, 43, 53.5, 64]
# the z location for the seeds is 30
# the analytical locations of the initially injected seeds are 1+y*time*time/2
# the seeds are reinjected at time step 70 (time 1.26) and their
# analytical locations are 1+y*(time*time-1.26*1.26)/2
# in parallel when seeds are migrated to different processes there
# is a slight loss of accuracy due to using a first order time
# integration method instead of vtkRungeKutta4.cxx.
from paraview.simple import *
r = XMLPartitionedPolydataReader(FileName=sys.argv[1]+'/particles_40.pvtp')
r.UpdatePipeline()
bounds = r.GetDataInformation().DataInformation.GetBounds()
if bounds[0] < 1.25 or bounds[0] > 1.27 or \
bounds[1] < 17.5 or bounds[1] > 17.7 or \
bounds[2] < .9 or bounds[2] > 1.1 or \
bounds[3] < 63.9 or bounds[3] > 64.1 or \
bounds[4] < 29.9 or bounds[4] > 30.1 or \
bounds[5] < 29.9 or bounds[5] > 30.1:
print 'Time step 40: wrong particle bounds', bounds
sys.exit(1)
g = Glyph()
g.GlyphMode = 'All Points'
g.GlyphType = '2D Glyph'
g.GlyphType.GlyphType = 'Vertex'
t = Threshold()
t.Scalars = ['POINTS', 'ParticleAge']
t.ThresholdRange = [0.71, 0.73]
t.UpdatePipeline()
grid = servermanager.Fetch(t)
if grid.GetNumberOfPoints() != 7:
print 'Time step 40: wrong number of points', grid.GetNumberOfPoints()
sys.exit(1)
r.FileName = sys.argv[1]+'/particles_80.pvtp'
# threshold to get the seeds that were originally injected only
t.ThresholdRange = [1.43, 1.45]
t.UpdatePipeline()
bounds = t.GetDataInformation().DataInformation.GetBounds()
if bounds[0] < 2. or bounds[0] > 2.1 or \
bounds[1] < 67.3 or bounds[1] > 67.4 or \
bounds[2] < .9 or bounds[2] > 1.1 or \
bounds[3] < 63.9 or bounds[3] > 64.1 or \
bounds[4] < 29.9 or bounds[4] > 30.1 or \
bounds[5] < 29.9 or bounds[5] > 30.1:
print 'Time step 80: wrong particle bounds for initial injected particles', bounds
sys.exit(1)
grid = servermanager.Fetch(t)
if grid.GetNumberOfPoints() != 7:
print 'Time step 80: wrong number of points for initial injected particles', grid.GetNumberOfPoints()
sys.exit(1)
# threshold to get the seeds that were injected at time step 70
t.Scalars = ['POINTS', 'InjectionStepId']
t.ThresholdRange = [69, 71]
t.UpdatePipeline()
bounds = t.GetDataInformation().DataInformation.GetBounds()
if bounds[0] < 1.23 or bounds[0] > 1.25 or \
bounds[1] < 16.5 or bounds[1] > 16.7 or \
bounds[2] < .9 or bounds[2] > 1.1 or \
bounds[3] < 63.9 or bounds[3] > 64.1 or \
bounds[4] < 29.9 or bounds[4] > 30.1 or \
bounds[5] < 29.9 or bounds[5] > 30.1:
print 'Time step 80: wrong particle bounds for reinjected particles', bounds
sys.exit(1)
grid = servermanager.Fetch(t)
if grid.GetNumberOfPoints() != 7:
print 'Time step 80: wrong number of points for reinjected particles', grid.GetNumberOfPoints()
sys.exit(1)
print 'test passed'

View File

@ -0,0 +1,42 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystCxxVTKPipelineExample)
set(USE_CATALYST ON CACHE BOOL "Link the simulator with Catalyst")
if(USE_CATALYST)
# We have a C++ pipeline so we don't need to link with Python
# or vtkPVPythonCatalyst since vtkPVCatalyst is sufficient.
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVCatalyst vtkPVVTKExtensionsDefault vtkPVClientServerCoreCore)
include("${PARAVIEW_USE_FILE}")
set(Adaptor_SRCS
FEAdaptor.cxx
vtkCPVTKPipeline.cxx
)
add_library(CxxVTKPipelineExampleAdaptor ${Adaptor_SRCS})
target_link_libraries(CxxVTKPipelineExampleAdaptor vtkPVCatalyst vtkPVVTKExtensionsDefault vtkPVClientServerCoreCore)
add_definitions("-DUSE_CATALYST")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
else()
find_package(MPI REQUIRED)
include_directories(${MPI_CXX_INCLUDE_PATH})
endif()
add_executable(CxxVTKPipelineExample FEDriver.cxx FEDataStructures.cxx)
if(USE_CATALYST)
target_link_libraries(CxxVTKPipelineExample LINK_PRIVATE CxxVTKPipelineExampleAdaptor)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(CxxVTKPipelineExample)
else()
target_link_libraries(CxxVTKPipelineExample LINK_PRIVATE ${MPI_LIBRARIES})
endif()
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
add_test(NAME CxxVTKPipelineExampleTest COMMAND CxxVTKPipelineExample 10 output)
set_tests_properties(CxxVTKPipelineExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,147 @@
#include <iostream>
#include "FEAdaptor.h"
#include "FEDataStructures.h"
#include "vtkCPVTKPipeline.h"
#include <vtkCellData.h>
#include <vtkCellType.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkPointData.h>
#include <vtkUnstructuredGrid.h>
namespace
{
vtkCPProcessor* Processor = NULL;
vtkUnstructuredGrid* VTKGrid;
void BuildVTKGrid(Grid& grid)
{
// create the points information
vtkNew<vtkDoubleArray> pointArray;
pointArray->SetNumberOfComponents(3);
pointArray->SetArray(grid.GetPointsArray(), static_cast<vtkIdType>(grid.GetNumberOfPoints()*3), 1);
vtkNew<vtkPoints> points;
points->SetData(pointArray.GetPointer());
VTKGrid->SetPoints(points.GetPointer());
// create the cells
size_t numCells = grid.GetNumberOfCells();
VTKGrid->Allocate(static_cast<vtkIdType>(numCells*9));
for(size_t cell=0;cell<numCells;cell++)
{
unsigned int* cellPoints = grid.GetCellPoints(cell);
vtkIdType tmp[8] = {cellPoints[0], cellPoints[1], cellPoints[2], cellPoints[3],
cellPoints[4], cellPoints[5], cellPoints[6], cellPoints[7]};
VTKGrid->InsertNextCell(VTK_HEXAHEDRON, 8, tmp);
}
}
void UpdateVTKAttributes(Grid& grid, Attributes& attributes)
{
if(VTKGrid->GetPointData()->GetNumberOfArrays() == 0)
{
// velocity array
vtkNew<vtkDoubleArray> velocity;
velocity->SetName("velocity");
velocity->SetNumberOfComponents(3);
velocity->SetNumberOfTuples(static_cast<vtkIdType>(grid.GetNumberOfPoints()));
VTKGrid->GetPointData()->AddArray(velocity.GetPointer());
}
if(VTKGrid->GetCellData()->GetNumberOfArrays() == 0)
{
// pressure array
vtkNew<vtkFloatArray> pressure;
pressure->SetName("pressure");
pressure->SetNumberOfComponents(1);
VTKGrid->GetCellData()->AddArray(pressure.GetPointer());
}
vtkDoubleArray* velocity = vtkDoubleArray::SafeDownCast(
VTKGrid->GetPointData()->GetArray("velocity"));
// The velocity array is ordered as vx0,vx1,vx2,..,vy0,vy1,vy2,..,vz0,vz1,vz2,..
// so we need to create a full copy of it with VTK's ordering of
// vx0,vy0,vz0,vx1,vy1,vz1,..
double* velocityData = attributes.GetVelocityArray();
vtkIdType numTuples = velocity->GetNumberOfTuples();
for(vtkIdType i=0;i<numTuples;i++)
{
double values[3] = {velocityData[i], velocityData[i+numTuples],
velocityData[i+2*numTuples]};
velocity->SetTupleValue(i, values);
}
vtkFloatArray* pressure = vtkFloatArray::SafeDownCast(
VTKGrid->GetCellData()->GetArray("pressure"));
// The pressure array is a scalar array so we can reuse
// memory as long as we ordered the points properly.
float* pressureData = attributes.GetPressureArray();
pressure->SetArray(pressureData, static_cast<vtkIdType>(grid.GetNumberOfCells()), 1);
}
void BuildVTKDataStructures(Grid& grid, Attributes& attributes)
{
if(VTKGrid == NULL)
{
// The grid structure isn't changing so we only build it
// the first time it's needed. If we needed the memory
// we could delete it and rebuild as necessary.
VTKGrid = vtkUnstructuredGrid::New();
BuildVTKGrid(grid);
}
UpdateVTKAttributes(grid, attributes);
}
}
namespace FEAdaptor
{
void Initialize(int outputFrequency, std::string fileName)
{
if(Processor == NULL)
{
Processor = vtkCPProcessor::New();
Processor->Initialize();
}
vtkNew<vtkCPVTKPipeline> pipeline;
pipeline->Initialize(outputFrequency, fileName);
Processor->AddPipeline(pipeline.GetPointer());
}
void Finalize()
{
if(Processor)
{
Processor->Delete();
Processor = NULL;
}
if(VTKGrid)
{
VTKGrid->Delete();
VTKGrid = NULL;
}
}
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep)
{
vtkNew<vtkCPDataDescription> dataDescription;
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(Processor->RequestDataDescription(dataDescription.GetPointer()) != 0)
{
BuildVTKDataStructures(grid, attributes);
dataDescription->GetInputDescriptionByName("input")->SetGrid(VTKGrid);
Processor->CoProcess(dataDescription.GetPointer());
}
}
} // end of Catalyst namespace

View File

@ -0,0 +1,19 @@
#ifndef FEADAPTOR_HEADER
#define FEADAPTOR_HEADER
#include <string>
class Attributes;
class Grid;
namespace FEAdaptor
{
void Initialize(int outputFrequency, std::string fileName);
void Finalize();
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep);
}
#endif

View File

@ -0,0 +1,153 @@
#include "FEDataStructures.h"
#include <mpi.h>
#include <iostream>
Grid::Grid()
{}
void Grid::Initialize(const unsigned int numPoints[3], const double spacing[3] )
{
if(numPoints[0] == 0 || numPoints[1] == 0 || numPoints[2] == 0)
{
std::cerr << "Must have a non-zero amount of points in each direction.\n";
}
// in parallel, we do a simple partitioning in the x-direction.
int mpiSize = 1;
int mpiRank = 0;
MPI_Comm_rank(MPI_COMM_WORLD, &mpiRank);
MPI_Comm_size(MPI_COMM_WORLD, &mpiSize);
unsigned int startXPoint = mpiRank*numPoints[0]/mpiSize;
unsigned int endXPoint = (mpiRank+1)*numPoints[0]/mpiSize;
if(mpiSize != mpiRank+1)
{
endXPoint++;
}
// create the points -- slowest in the x and fastest in the z directions
double coord[3] = {0,0,0};
for(unsigned int i=startXPoint;i<endXPoint;i++)
{
coord[0] = i*spacing[0];
for(unsigned int j=0;j<numPoints[1];j++)
{
coord[1] = j*spacing[1];
for(unsigned int k=0;k<numPoints[2];k++)
{
coord[2] = k*spacing[2];
// add the coordinate to the end of the vector
std::copy(coord, coord+3, std::back_inserter(this->Points));
}
}
}
// create the hex cells
unsigned int cellPoints[8];
unsigned int numXPoints = endXPoint - startXPoint;
for(unsigned int i=0;i<numXPoints-1;i++)
{
for(unsigned int j=0;j<numPoints[1]-1;j++)
{
for(unsigned int k=0;k<numPoints[2]-1;k++)
{
cellPoints[0] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
cellPoints[1] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k;
cellPoints[2] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
cellPoints[3] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k;
cellPoints[4] = i*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
cellPoints[5] = (i+1)*numPoints[1]*numPoints[2] +
j*numPoints[2] + k+1;
cellPoints[6] = (i+1)*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
cellPoints[7] = i*numPoints[1]*numPoints[2] +
(j+1)*numPoints[2] + k+1;
std::copy(cellPoints, cellPoints+8, std::back_inserter(this->Cells));
}
}
}
}
size_t Grid::GetNumberOfPoints()
{
return this->Points.size()/3;
}
size_t Grid::GetNumberOfCells()
{
return this->Cells.size()/8;
}
double* Grid::GetPointsArray()
{
if(this->Points.empty())
{
return NULL;
}
return &(this->Points[0]);
}
double* Grid::GetPoint(size_t pointId)
{
if(pointId >= this->Points.size())
{
return NULL;
}
return &(this->Points[pointId*3]);
}
unsigned int* Grid::GetCellPoints(size_t cellId)
{
if(cellId >= this->Cells.size())
{
return NULL;
}
return &(this->Cells[cellId*8]);
}
Attributes::Attributes()
{
this->GridPtr = NULL;
}
void Attributes::Initialize(Grid* grid)
{
this->GridPtr = grid;
}
void Attributes::UpdateFields(double time)
{
size_t numPoints = this->GridPtr->GetNumberOfPoints();
this->Velocity.resize(numPoints*3);
for(size_t pt=0;pt<numPoints;pt++)
{
double* coord = this->GridPtr->GetPoint(pt);
this->Velocity[pt] = coord[1]*time;
}
std::fill(this->Velocity.begin()+numPoints, this->Velocity.end(), 0.);
size_t numCells = this->GridPtr->GetNumberOfCells();
this->Pressure.resize(numCells);
std::fill(this->Pressure.begin(), this->Pressure.end(), 1.);
}
double* Attributes::GetVelocityArray()
{
if(this->Velocity.empty())
{
return NULL;
}
return &this->Velocity[0];
}
float* Attributes::GetPressureArray()
{
if(this->Pressure.empty())
{
return NULL;
}
return &this->Pressure[0];
}

View File

@ -0,0 +1,41 @@
#ifndef FEDATASTRUCTURES_HEADER
#define FEDATASTRUCTURES_HEADER
#include <cstddef>
#include <vector>
class Grid
{
public:
Grid();
void Initialize(const unsigned int numPoints[3], const double spacing[3]);
size_t GetNumberOfPoints();
size_t GetNumberOfCells();
double* GetPointsArray();
double* GetPoint(size_t pointId);
unsigned int* GetCellPoints(size_t cellId);
private:
std::vector<double> Points;
std::vector<unsigned int> Cells;
};
class Attributes
{
// A class for generating and storing point and cell fields.
// Velocity is stored at the points and pressure is stored
// for the cells. The current velocity profile is for a
// shearing flow with U(y,t) = y*t, V = 0 and W = 0.
// Pressure is constant through the domain.
public:
Attributes();
void Initialize(Grid* grid);
void UpdateFields(double time);
double* GetVelocityArray();
float* GetPressureArray();
private:
std::vector<double> Velocity;
std::vector<float> Pressure;
Grid* GridPtr;
};
#endif

View File

@ -0,0 +1,76 @@
#include "FEDataStructures.h"
#include <mpi.h>
#ifdef USE_CATALYST
#include <cstdlib>
#include "FEAdaptor.h"
#include <iostream>
#endif
// Example of a C++ adaptor for a simulation code
// where we use a hard-coded VTK C++ pipeline.
// The simulation code has a fixed topology
// grid. We treat the grid as an unstructured
// grid even though in the example provided it
// would be best described as a vtkImageData.
// Also, the points are stored in an inconsistent
// manner with respect to the velocity vector.
// This is purposefully done to demonstrate
// the different approaches for getting data
// into Catalyst. The hard-coded C++ pipeline
// computes the velocity magnitude from a velocity
// vector and then uses a threshold filter to keep the
// portion of the domain where velocity magnitude
// is within the 10% of the maximum velocity.
// Note that through configuration
// that the driver can be run without linking
// to Catalyst.
int main(int argc, char* argv[])
{
MPI_Init(&argc, &argv);
Grid grid;
unsigned int numPoints[3] = {70, 60, 44};
double spacing[3] = {1, 1.1, 1.3};
grid.Initialize(numPoints, spacing);
Attributes attributes;
attributes.Initialize(&grid);
#ifdef USE_CATALYST
bool doCoProcessing = false;
if(argc == 3)
{
doCoProcessing = true;
// pass in the number of time steps and base file name.
FEAdaptor::Initialize(atoi(argv[1]), argv[2]);
}
else
{
std::cerr << "To run with Catalyst you must pass in the output frequency and the base file name.\n";
}
#endif
unsigned int numberOfTimeSteps = 100;
for(unsigned int timeStep=0;timeStep<numberOfTimeSteps;timeStep++)
{
// use a time step length of 0.1
double time = timeStep * 0.1;
attributes.UpdateFields(time);
#ifdef USE_CATALYST
if(doCoProcessing)
{
FEAdaptor::CoProcess(grid, attributes, time, timeStep,
timeStep == numberOfTimeSteps-1);
}
#endif
}
#ifdef USE_CATALYST
if(doCoProcessing)
{
FEAdaptor::Finalize();
}
#endif
MPI_Finalize();
return 0;
}

View File

@ -0,0 +1,137 @@
#include "vtkCPVTKPipeline.h"
#include <vtkCommunicator.h>
#include <vtkCompleteArrays.h>
#include <vtkCPDataDescription.h>
#include <vtkDataArray.h>
#include <vtkCPInputDataDescription.h>
#include <vtkMultiProcessController.h>
#include <vtkNew.h>
#include <vtkObjectFactory.h>
#include <vtkPointData.h>
#include <vtkPVArrayCalculator.h>
#include <vtkPVTrivialProducer.h>
#include <vtkSMProxyManager.h>
#include <vtkThreshold.h>
#include <vtkUnstructuredGrid.h>
#include <vtkXMLPUnstructuredGridWriter.h>
#include <string>
#include <sstream>
vtkStandardNewMacro(vtkCPVTKPipeline);
//----------------------------------------------------------------------------
vtkCPVTKPipeline::vtkCPVTKPipeline()
{
this->OutputFrequency = 0;
}
//----------------------------------------------------------------------------
vtkCPVTKPipeline::~vtkCPVTKPipeline()
{
}
//----------------------------------------------------------------------------
void vtkCPVTKPipeline::Initialize(int outputFrequency, std::string& fileName)
{
this->OutputFrequency = outputFrequency;
this->FileName = fileName;
}
//----------------------------------------------------------------------------
int vtkCPVTKPipeline::RequestDataDescription(
vtkCPDataDescription* dataDescription)
{
if(!dataDescription)
{
vtkWarningMacro("dataDescription is NULL.");
return 0;
}
if(this->FileName.empty())
{
vtkWarningMacro("No output file name given to output results to.");
return 0;
}
if(dataDescription->GetForceOutput() == true ||
(this->OutputFrequency != 0 && dataDescription->GetTimeStep() % this->OutputFrequency == 0) )
{
dataDescription->GetInputDescriptionByName("input")->AllFieldsOn();
dataDescription->GetInputDescriptionByName("input")->GenerateMeshOn();
return 1;
}
return 0;
}
//----------------------------------------------------------------------------
int vtkCPVTKPipeline::CoProcess(
vtkCPDataDescription* dataDescription)
{
if(!dataDescription)
{
vtkWarningMacro("DataDescription is NULL");
return 0;
}
vtkUnstructuredGrid* grid = vtkUnstructuredGrid::SafeDownCast(
dataDescription->GetInputDescriptionByName("input")->GetGrid());
if(grid == NULL)
{
vtkWarningMacro("DataDescription is missing input unstructured grid.");
return 0;
}
if(this->RequestDataDescription(dataDescription) == 0)
{
return 1;
}
vtkNew<vtkPVTrivialProducer> producer;
producer->SetOutput(grid);
vtkNew<vtkPVArrayCalculator> calculator;
calculator->SetInputConnection(producer->GetOutputPort());
calculator->SetAttributeMode(1);
calculator->SetResultArrayName("velocity magnitude");
calculator->SetFunction("mag(velocity)");
// update now so that we can get the global data bounds of
// the velocity magnitude for thresholding
calculator->Update();
double range[2];
vtkUnstructuredGrid::SafeDownCast(calculator->GetOutput())->GetPointData()
->GetArray("velocity magnitude")->GetRange(range, 0);
double globalRange[2];
vtkMultiProcessController::GetGlobalController()->AllReduce(
range+1, globalRange+1, 1, vtkCommunicator::MAX_OP);
vtkNew<vtkThreshold> threshold;
threshold->SetInputConnection(calculator->GetOutputPort());
threshold->SetInputArrayToProcess(
0, 0, 0, "vtkDataObject::FIELD_ASSOCIATION_POINTS", "velocity magnitude");
threshold->ThresholdBetween(0.9*globalRange[1], globalRange[1]);
// If process 0 doesn't have any points or cells, the writer may
// have problems in parallel so we use completeArrays to fill in
// the missing information.
vtkNew<vtkCompleteArrays> completeArrays;
completeArrays->SetInputConnection(threshold->GetOutputPort());
vtkNew<vtkXMLPUnstructuredGridWriter> writer;
writer->SetInputConnection(completeArrays->GetOutputPort());
std::ostringstream o;
o << dataDescription->GetTimeStep();
std::string name = this->FileName + o.str() + ".pvtu";
writer->SetFileName(name.c_str());
writer->Update();
return 1;
}
//----------------------------------------------------------------------------
void vtkCPVTKPipeline::PrintSelf(ostream& os, vtkIndent indent)
{
this->Superclass::PrintSelf(os, indent);
os << indent << "OutputFrequency: " << this->OutputFrequency << "\n";
os << indent << "FileName: " << this->FileName << "\n";
}

View File

@ -0,0 +1,34 @@
#ifndef VTKCPVTKPIPELINE_H
#define VTKCPVTKPIPELINE_H
#include <vtkCPPipeline.h>
#include <string>
class vtkCPDataDescription;
class vtkCPPythonHelper;
class vtkCPVTKPipeline : public vtkCPPipeline
{
public:
static vtkCPVTKPipeline* New();
vtkTypeMacro(vtkCPVTKPipeline,vtkCPPipeline);
virtual void PrintSelf(ostream& os, vtkIndent indent);
virtual void Initialize(int outputFrequency, std::string& fileName);
virtual int RequestDataDescription(vtkCPDataDescription* dataDescription);
virtual int CoProcess(vtkCPDataDescription* dataDescription);
protected:
vtkCPVTKPipeline();
virtual ~vtkCPVTKPipeline();
private:
vtkCPVTKPipeline(const vtkCPVTKPipeline&); // Not implemented
void operator=(const vtkCPVTKPipeline&); // Not implemented
int OutputFrequency;
std::string FileName;
};
#endif

View File

@ -0,0 +1,26 @@
cmake_minimum_required(VERSION 2.6)
project(CatalystFortran90FullExample CXX Fortran)
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include(${PARAVIEW_USE_FILE})
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
if(NOT MPI_Fortran_LIBRARIES)
find_package(MPI)
endif()
add_executable(Fortran90FullExample FEDriver.f90 FEFortranAdaptor.f90 FECxxAdaptor.cxx)
target_link_libraries(Fortran90FullExample vtkPVPythonCatalyst ${MPI_Fortran_LIBRARIES})
set_target_properties(Fortran90FullExample PROPERTIES
LINKER_LANGUAGE Fortran)
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/coproc.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
include(CTest)
add_test(NAME Fortran90FullExampleTest COMMAND Fortran90FullExample)
set_tests_properties(Fortran90FullExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,67 @@
// Adaptor for getting Fortran simulation code into ParaView CoProcessor.
// CoProcessor specific headers
#include "vtkCPDataDescription.h"
#include "vtkCPInputDataDescription.h"
#include "vtkCPProcessor.h"
#include "vtkCPPythonScriptPipeline.h"
#include "vtkSmartPointer.h"
#include "vtkDoubleArray.h"
#include "vtkPointData.h"
#include "vtkImageData.h"
// Fortran specific header
#include "vtkCPPythonAdaptorAPI.h"
// These will be called from the Fortran "glue" code"
// Completely dependent on data layout, structured vs. unstructured, etc.
// since VTK/ParaView uses different internal layouts for each.
// Creates the data container for the CoProcessor.
extern "C" void createcpimagedata_(int* nxstart, int* nxend, int* nx,
int* ny, int* nz)
{
if (!vtkCPPythonAdaptorAPI::GetCoProcessorData())
{
vtkGenericWarningMacro("Unable to access CoProcessorData.");
return;
}
// The simulation grid is a 3-dimensional topologically and geometrically
// regular grid. In VTK/ParaView, this is considered an image data set.
vtkSmartPointer<vtkImageData> grid = vtkSmartPointer<vtkImageData>::New();
grid->SetExtent(*nxstart-1, *nxend-1, 0, *ny-1, 0, *nz-1);
// Name should be consistent between here, Fortran and Python client script.
vtkCPPythonAdaptorAPI::GetCoProcessorData()->GetInputDescriptionByName("input")->SetGrid(grid);
vtkCPPythonAdaptorAPI::GetCoProcessorData()->GetInputDescriptionByName("input")->SetWholeExtent(0, *nx-1, 0, *ny-1, 0, *nz-1);
}
// Add field(s) to the data container.
// Separate from above because this will be dynamic, grid is static.
// By hand name mangling for fortran.
extern "C" void addfield_(double* scalars, char* name)
{
vtkCPInputDataDescription* idd =
vtkCPPythonAdaptorAPI::GetCoProcessorData()->GetInputDescriptionByName("input");
vtkImageData* Image = vtkImageData::SafeDownCast(idd->GetGrid());
if (!Image)
{
vtkGenericWarningMacro("No adaptor grid to attach field data to.");
return;
}
// field name must match that in the fortran code.
if (idd->IsFieldNeeded(name))
{
vtkSmartPointer<vtkDoubleArray> field = vtkSmartPointer<vtkDoubleArray>::New();
field->SetNumberOfComponents(2);
field->SetName(name);
field->SetArray(scalars, 2*Image->GetNumberOfPoints(), 1);
Image->GetPointData()->AddArray(field);
}
}

View File

@ -0,0 +1,56 @@
! A Fortran Catalyst example. Note that coproc.py
! must be in the directory where the example
! is run from.
! Thanks to Lucas Pettey for helping create the example.
PROGRAM coproc
use tcp
implicit none
include 'mpif.h'
integer,parameter :: nx=100,ny=100,nz=100,ntime=10
integer :: i,j,k,time,nxstart,nxend
real :: max
complex(kind=8), allocatable :: psi01(:,:,:)
integer :: numtasks,rank,ierr
call mpi_init(ierr)
call mpi_comm_size(MPI_COMM_WORLD, numtasks, ierr)
call mpi_comm_rank(MPI_COMM_WORLD, rank, ierr)
call coprocessorinitializewithpython("coproc.py",9)
! partition in the x-direction only
nxstart=rank*nx/numtasks+1
nxend=(rank+1)*nx/numtasks
if(numtasks .ne. rank+1) then
nxend=nxend+1
endif
allocate(psi01(nxend-nxstart+1,ny,nz))
! set initial values
max=sqrt(real(nx)**2+real(ny)**2+real(nz)**2)
do k=1,nz
do j=1,ny
do i=1,nxend-nxstart+1
psi01(i,j,k)=CMPLX(max-sqrt(real(i-50)**2+real(j-50)**2+real(k-50)**2),&
real(1+j+k))/max*100.0
enddo
enddo
enddo
do time=1,ntime
do k=1,nz
do j=1,ny
do i=1,nxend-nxstart+1
psi01(i,j,k)=CMPLX(real(0.30),0.0)+psi01(i,j,k)
end do
end do
end do
call testcoprocessor(nxstart,nxend,nx,ny,nz,time,dble(time),psi01)
enddo
deallocate(psi01)
call coprocessorfinalize()
call mpi_finalize(ierr)
end program coproc

View File

@ -0,0 +1,32 @@
module tcp
use iso_c_binding
implicit none
public
interface tcp_adaptor
module procedure testcoprocessor
end interface
contains
subroutine testcoprocessor(nxstart,nxend,nx,ny,nz,step,time,psi01)
use iso_c_binding
implicit none
integer, intent(in) :: nxstart,nxend,nx,ny,nz,step
real(kind=8), intent(in) :: time
complex(kind=8), dimension(:,:,:), intent (in) :: psi01
integer :: flag
call requestdatadescription(step,time,flag)
if (flag .ne. 0) then
call needtocreategrid(flag)
if (flag .ne. 0) then
call createcpimagedata(nxstart,nxend,nx,nz,nz)
end if
! adding //char(0) appends the C++ terminating character
! to the Fortran array
call addfield(psi01,"psi01"//char(0))
call coprocess()
end if
return
end subroutine
end module tcp

View File

@ -0,0 +1,83 @@
try: paraview.simple
except: from paraview.simple import *
from paraview import coprocessing
#--------------------------------------------------------------
# Code generated from cpstate.py to create the CoProcessor.
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
filename_6_pvti = coprocessor.CreateProducer( datadescription, "input" )
Slice1 = Slice( guiName="Slice1", Crinkleslice=0, SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane" )
Slice1.SliceType.Offset = 0.0
Slice1.SliceType.Origin = [49.5, 49.5, 49.5]
Slice1.SliceType.Normal = [1.0, 0.0, 0.0]
ParallelPolyDataWriter1 = coprocessor.CreateWriter( XMLPPolyDataWriter, "slice_%t.pvtp", 10 )
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [10]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(False)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,114 @@
module Box
implicit none
public :: getownedbox, getlocalbox
contains
! this gives a partitioning of an extent based on its inputs.
! the partitioning will overlap (e.g. 0 to 10 partitioned into
! 2 pieces would result in 0-5 and 5-10). arguments are:
! piece: which piece to get the partition for (between 1 and numpieces)
! numpieces: the number of pieces that the extent is partitioned into
! globalbox: the extent where the values are ordered by
! {max x index, max y index, max z index}
! box: the returned local extent where the values are ordered by
! {min x index, max x index, min y index, max y index, min z index, max z index}
subroutine getlocalbox(piece, numpieces, dimensions, box)
implicit none
integer, intent(in) :: piece, numpieces, dimensions(3)
integer, intent(inout) :: box(6)
integer :: numpiecesinfirsthalf, splitaxis, mid, cnt
integer :: numpieceslocal, piecelocal, i, size(3)
do i=1, 3
box((i-1)*2+1) = 1
box(2*i) = dimensions(i)
enddo
if (piece .gt. numpieces .or. piece .lt. 0) return
! keep splitting until we have only one piece.
! piece and numpieces will always be relative to the current box.
cnt = 0
numpieceslocal = numpieces
piecelocal = piece-1
do while (numpieceslocal .gt. 1)
size(1) = box(2) - box(1)
size(2) = box(4) - box(3)
size(3) = box(6) - box(5)
! choose what axis to split on based on the SplitMode
! if the user has requested x, y, or z slabs then try to
! honor that request. If that axis is already split as
! far as it can go, then drop to block mode.
! choose the biggest axis
if (size(3) .ge. size(2) .and. size(3) .ge. size(1) .and. size(3)/2 .ge. 1) then
splitaxis = 3
else if (size(2) .ge. size(1) .and. size(2)/2 .ge. 1) then
splitaxis = 2
else if (size(1)/2 .ge. 1) then
splitaxis = 1
else
splitaxis = -1
endif
if (splitaxis .eq. -1) then
! can not split any more.
if (piecelocal .eq. 0) then
! just return the remaining piece
numpieceslocal = 1
else
! the rest must be empty
return
endif
else ! (splitaxis .eq. -1)
! split the chosen axis into two pieces.
numpiecesinfirsthalf = (numpieceslocal / 2)
mid = size(splitaxis)
mid = (mid * numpiecesinfirsthalf) / numpieceslocal + box((splitaxis-1)*2+1)
if (piecelocal .lt. numpiecesinfirsthalf) then
! piece is in the first half
! set boxent to the first half of the previous value.
box((splitaxis-1)*2+2) = mid
! piece must adjust.
numpieceslocal = numpiecesinfirsthalf
else
! piece is in the second half.
! set the boxent to be the second half. (two halves share points)
box((splitaxis-1)*2+1) = mid
! piece must adjust
numpieceslocal = numpieceslocal - numpiecesinfirsthalf
piecelocal = piecelocal - numpiecesinfirsthalf
endif
endif
end do
return
end subroutine getlocalbox
! box is only locally owned on the minimum side of that
! is at the domain boundary
subroutine getownedbox(piece, numpieces, dimensions, ownedbox)
implicit none
integer, intent(in) :: dimensions(3), piece, numpieces
integer, intent(out) :: ownedbox(6)
integer :: i, localbox(6)
call getlocalbox(piece, numpieces, dimensions, localbox)
do i=1, 3
! minimums
if(localbox((i-1)*2+1) .eq. 1) then
ownedbox((i-1)*2+1) = 1
else
if(localbox((i-1)*2+1) .ne. dimensions(i)) then
ownedbox((i-1)*2+1) = localbox((i-1)*2+1)+1
else
! this happens when the domain has a single point in this direction
ownedbox((i-1)*2+1) = 1
endif
endif
! maximums
ownedbox(i*2) = localbox(i*2)
end do
end subroutine getownedbox
end module Box

View File

@ -0,0 +1,43 @@
cmake_minimum_required(VERSION 2.6)
project(CatalystFortranPoissonSolver CXX Fortran)
set(SOURCES
FEDriver.F90
Box.F90
SparseMatrix.F90
ConjugateGradient.F90
PoissonDiscretization.F90
)
set(USE_CATALYST ON CACHE BOOL "Link the simulator with Catalyst")
if(USE_CATALYST)
find_package(ParaView 4.1 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
# we don't create a separate library for the adaptor here
# since FEFortranAdaptor.F90 depends on Box.F90
list(APPEND SOURCES FEFortranAdaptor.F90 FECxxAdaptor.cxx)
add_definitions("-DUSE_CATALYST")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
else()
find_package(MPI REQUIRED)
include_directories(${MPI_Fortran_INCLUDE_PATH})
endif()
add_executable(FortranPoissonSolver ${SOURCES})
target_link_libraries(FortranPoissonSolver ${MPI_Fortran_LIBRARIES})
if(USE_CATALYST)
target_link_libraries(FortranPoissonSolver vtkPVPythonCatalyst vtkParallelMPI)
endif()
set_target_properties(FortranPoissonSolver PROPERTIES
LINKER_LANGUAGE Fortran)
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/coproc.py DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
include(CTest)
add_test(NAME FortranPoissonSolverTest COMMAND FortranPoissonSolver coproc.py)
set_tests_properties(FortranPoissonSolverTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,67 @@
module ConjugateGradient
use SparseMatrix
#ifdef USE_CATALYST
use CoProcessor
#endif
implicit none
private :: dotproduct
public :: solve
contains
real(kind=8) function dotproduct(sm, a, b)
type(SparseMatrixData), intent(inout) :: sm
integer :: i
real(kind=8), intent(in) :: a(:), b(:)
real(kind=8) :: value
value = 0.d0
do i=1, sm%globalsize
value = value + a(i)*b(i)
enddo
dotproduct = value
end function dotproduct
subroutine solve(dimensions, sm, x, rhs)
type(SparseMatrixData), intent(inout) :: sm
integer, intent(in) :: dimensions(3)
real(kind=8), intent(in) :: rhs(:)
real(kind=8), intent(inout) :: x(:)
integer :: k, i
real(kind=8) :: alpha, beta, rdotproduct, rnewdotproduct, sqrtorigresid
real(kind=8), DIMENSION(:), allocatable :: r(:), p(:), ap(:)
allocate(r(sm%globalsize), p(sm%globalsize), ap(sm%globalsize))
#ifdef USE_CATALYST
x(:) = 0.d0
call runcoprocessor(dimensions, 0, 0.d0, x)
#endif
r(:) = rhs(:)
p(:) = rhs(:)
k = 1
rdotproduct = dotproduct(sm, r, r)
sqrtorigresid = sqrt(rdotproduct)
do while(k .le. sm%globalsize .and. sqrt(rdotproduct) .gt. sqrtorigresid*0.000001d0)
call matvec(sm, p, ap)
alpha = rdotproduct/dotproduct(sm, ap, p)
x(:) = x(:) + alpha*p(:)
r(:) = r(:) - alpha*ap(:)
rnewdotproduct = dotproduct(sm, r, r)
beta = rnewdotproduct/rdotproduct
p(:) = r(:) + beta*p(:)
rdotproduct = rnewdotproduct
!write(*,*) 'on iteration ', k, sqrtorigresid, sqrt(rdotproduct), alpha
#ifdef USE_CATALYST
call runcoprocessor(dimensions, k, k*1.d0, x)
#endif
k = k+1
end do
deallocate(r, p, ap)
end subroutine solve
end module ConjugateGradient

View File

@ -0,0 +1,66 @@
// Adaptor for getting Fortran simulation code into ParaView Catalyst.
// CoProcessor specific headers
#include "vtkCPDataDescription.h"
#include "vtkCPInputDataDescription.h"
#include "vtkCPProcessor.h"
#include "vtkSmartPointer.h"
#include "vtkDoubleArray.h"
#include "vtkPointData.h"
#include "vtkImageData.h"
// Fortran specific header
#include "vtkCPPythonAdaptorAPI.h"
// These will be called from the Fortran "glue" code"
// Completely dependent on data layout, structured vs. unstructured, etc.
// since VTK/ParaView uses different internal layouts for each.
// Creates the data container for the CoProcessor.
extern "C" void createcpimagedata_(int* dimensions, int* extent)
{
if (!vtkCPPythonAdaptorAPI::GetCoProcessorData())
{
vtkGenericWarningMacro("Unable to access CoProcessorData.");
return;
}
// The simulation grid is a 3-dimensional topologically and geometrically
// regular grid. In VTK/ParaView, this is considered an image data set.
vtkSmartPointer<vtkImageData> grid = vtkSmartPointer<vtkImageData>::New();
grid->SetExtent(extent[0]-1, extent[1]-1, extent[2]-1,
extent[3]-1, extent[4]-1, extent[5]-1);
grid->SetSpacing(1./(dimensions[0]-1), 1./(dimensions[1]-1), 1./(dimensions[2]-1));
// Name should be consistent between here, Fortran and Python client script.
vtkCPPythonAdaptorAPI::GetCoProcessorData()->GetInputDescriptionByName("input")->SetGrid(grid);
vtkCPPythonAdaptorAPI::GetCoProcessorData()->GetInputDescriptionByName("input")->SetWholeExtent(
0, dimensions[0]-1, 0, dimensions[1]-1, 0, dimensions[2]-1);
}
// Add field(s) to the data container.
// Separate from above because this will be dynamic, grid is static.
// By hand name mangling for fortran.
extern "C" void addfield_(double* scalars, char* name)
{
vtkCPInputDataDescription* idd =
vtkCPPythonAdaptorAPI::GetCoProcessorData()->GetInputDescriptionByName("input");
vtkImageData* image = vtkImageData::SafeDownCast(idd->GetGrid());
if (!image)
{
vtkGenericWarningMacro("No adaptor grid to attach field data to.");
return;
}
// field name must match that in the fortran code.
if (idd->IsFieldNeeded(name))
{
vtkSmartPointer<vtkDoubleArray> field = vtkSmartPointer<vtkDoubleArray>::New();
field->SetName(name);
field->SetArray(scalars, image->GetNumberOfPoints(), 1);
image->GetPointData()->AddArray(field);
}
}

View File

@ -0,0 +1,54 @@
PROGRAM coproc
#ifdef USE_CATALYST
use CoProcessor ! ParaView Catalyst adaptor
#endif
use SparseMatrix ! contains initialize() and finalize()
use PoissonDiscretization ! contains fillmatrixandrhs()
use ConjugateGradient ! contains solve()
use Box ! contains getownedbox()
implicit none
include 'mpif.h'
integer :: numtasks,rank,ierr,allocatestatus
integer :: dimensions(3), ownedbox(6)
type(SparseMatrixData) :: sm
real(kind=8), DIMENSION(:), allocatable :: x, rhs
call mpi_init(ierr)
call mpi_comm_size(MPI_COMM_WORLD, numtasks, ierr)
call mpi_comm_rank(MPI_COMM_WORLD, rank, ierr)
#ifdef USE_CATALYST
call initializecoprocessor()
#endif
dimensions(1) = 10
dimensions(2) = 10
dimensions(3) = 10
! given a piece between 1 and numtasks, compute the nodes that
! are owned by this process (ownedbox)
call getownedbox(rank+1, numtasks, dimensions, ownedbox)
call initialize(sm, ownedbox, dimensions)
! each process has a full copy of the solution and the RHS to keep things simple
allocate(x(dimensions(1)*dimensions(2)*dimensions(3)), rhs(dimensions(1)*dimensions(2)*dimensions(3)), STAT = allocatestatus)
if (allocatestatus /= 0) STOP "*** FEDriver.F90: Not enough memory for arrays ***"
call fillmatrixandrhs(sm, rhs, ownedbox, dimensions)
call solve(dimensions, sm, x, rhs)
#ifdef USE_CATALYST
call finalizecoprocessor()
#endif
deallocate(x, rhs)
call finalize(sm)
call mpi_finalize(ierr)
write(*,*) 'Finished on rank', rank, 'of', numtasks
end program coproc

View File

@ -0,0 +1,86 @@
module CoProcessor
implicit none
public initializecoprocessor, runcoprocessor, finalizecoprocessor
contains
subroutine initializecoprocessor()
implicit none
integer :: ilen, i
character(len=200) :: arg
call coprocessorinitialize()
do i=1, iargc()
call getarg(i, arg)
ilen = len_trim(arg)
arg(ilen+1:) = char(0)
call coprocessoraddpythonscript(arg, ilen)
enddo
end subroutine initializecoprocessor
subroutine runcoprocessor(dimensions, step, time, x)
use iso_c_binding
implicit none
integer, intent(in) :: dimensions(3), step
real(kind=8), dimension(:), intent(in) :: x
real(kind=8), intent(in) :: time
integer :: flag, extent(6)
real(kind=8), DIMENSION(:), allocatable :: xcp(:)
call requestdatadescription(step,time,flag)
if (flag .ne. 0) then
call needtocreategrid(flag)
call getvtkextent(dimensions, extent)
if (flag .ne. 0) then
call createcpimagedata(dimensions, extent)
end if
! x is the array with global values, we need just this process's
! values for Catalyst which will be put in xcp
allocate(xcp((extent(2)-extent(1)+1)*(extent(4)-extent(3)+1)*(extent(6)-extent(5)+1)))
call getlocalfield(dimensions, extent, x, xcp)
! adding //char(0) appends the C++ terminating character
! to the Fortran array
call addfield(xcp,"solution"//char(0))
call coprocess()
deallocate(xcp)
end if
end subroutine runcoprocessor
subroutine finalizecoprocessor()
call coprocessorfinalize()
end subroutine finalizecoprocessor
! helper methods
subroutine getvtkextent(dimensions, extent)
use Box
implicit none
include 'mpif.h'
integer, intent(in) :: dimensions(3)
integer, intent(out) :: extent(6)
integer :: numtasks, rank, ierr
call mpi_comm_size(MPI_COMM_WORLD, numtasks, ierr)
call mpi_comm_rank(MPI_COMM_WORLD, rank, ierr)
call getlocalbox(rank+1, numtasks, dimensions, extent)
end subroutine getvtkextent
subroutine getlocalfield(dimensions, extent, x, xcp)
implicit none
integer :: i, j, k, counter
integer, intent(in) :: dimensions(3), extent(6)
real(kind=8), dimension(:), intent(in) :: x
real(kind=8), dimension(:), intent(out) :: xcp
counter = 1
do k=extent(5), extent(6)
do j=extent(3), extent(4)
do i=extent(1), extent(2)
xcp(counter) = x(i+(j-1)*dimensions(1)+(k-1)*dimensions(1)*dimensions(2))
counter = counter + 1
enddo
enddo
enddo
end subroutine getlocalfield
end module CoProcessor

View File

@ -0,0 +1,127 @@
module PoissonDiscretization
implicit none
public :: fillmatrixandrhs
contains
subroutine fillmatrixandrhs(sm, rhs, ownedbox, dimensions)
use SparseMatrix
implicit none
real(kind=8), intent(out) :: rhs(:)
type(SparseMatrixData), intent(inout) :: sm
integer, intent(in) :: ownedbox(6), dimensions(3)
integer :: i,j,k
real(kind=8) :: dxsqinverse, dysqinverse, dzsqinverse, value
logical :: negativex, negativey, negativez, positivex, positivey, positivez, interior
integer :: numx, numy, numz, row
! construct the matrix
numx = ownedbox(2)-ownedbox(1)+1
if(numx < 1) numx = 1
numy = ownedbox(4)-ownedbox(3)+1
if(numy < 1) numy = 1
numz = ownedbox(6)-ownedbox(5)+1
rhs(:) = 0.0
dxsqinverse = 1.d0/((dimensions(1)-1)*(dimensions(1)-1))
dysqinverse = 1.d0/((dimensions(2)-1)*(dimensions(2)-1))
dzsqinverse = 1.d0/((dimensions(3)-1)*(dimensions(3)-1))
do k=1, numz
negativez = .TRUE.
if(k .eq. 1 .and. ownedbox(5) .eq. 1) negativez = .FALSE.
positivez = .TRUE.
if(k .eq. numz .and. ownedbox(6) .eq. dimensions(3)) positivez = .FALSE.
do j=1, numy
negativey = .TRUE.
if(j .eq. 1 .and. ownedbox(3) .eq. 1) negativey = .FALSE.
positivey = .TRUE.
if(j .eq. numy .and. ownedbox(4) .eq. dimensions(2)) positivey = .FALSE.
do i=1, numx
negativex = .TRUE.
if(i .eq. 1 .and. ownedbox(1) .eq. 1) negativex = .FALSE.
positivex = .TRUE.
if(i .eq. numx .and. ownedbox(2) .eq. dimensions(1)) positivex = .FALSE.
interior = negativex .and. negativey .and. negativez .and. positivex .and. positivey .and. positivez
row = i+ownedbox(1)-1+(j+ownedbox(3)-2)*dimensions(1)+(k+ownedbox(5)-2)*dimensions(1)*dimensions(2)
if(.NOT. interior) then
call insert(sm, row, row, 1.d0)
if(.NOT. positivez) rhs(row) = 1.d0
else
call insert(sm, row, row-1, dxsqinverse)
call insert(sm, row, row+1, dxsqinverse)
call insert(sm, row, row-dimensions(1), dysqinverse)
call insert(sm, row, row+dimensions(1), dysqinverse)
call insert(sm, row, row-dimensions(1)*dimensions(2), dzsqinverse)
call insert(sm, row, row+dimensions(1)*dimensions(2), dzsqinverse)
call insert(sm, row, row, -2.d0*dxsqinverse-2.d0*dysqinverse-2.d0*dzsqinverse)
endif
end do
end do
end do
! now we need to diagonalize the matrix based on boundary conditions
! do the non-homogeneous bcs on the z=1 plane first
if(ownedbox(5) .le. dimensions(3)-1 .and. ownedbox(6) .ge. dimensions(3)-1) then
do i=ownedbox(1), ownedbox(2)
do j=ownedbox(3), ownedbox(4)
row = i+(j-1)*dimensions(1)+dimensions(1)*dimensions(2)*(dimensions(3)-2)
call get(sm, row, row+dimensions(1)*dimensions(2), value)
rhs(row) = rhs(row) - value*1.d0
call insert(sm, row, row+dimensions(1)*dimensions(2), 0.d0)
enddo
enddo
endif
! the homogeneous bcs
! the z=0 plane
if(ownedbox(5) .le. 2 .and. ownedbox(6) .ge. 2) then
do i=ownedbox(1), ownedbox(2)
do j=ownedbox(3), ownedbox(4)
row = i+(j-1)*dimensions(1)+dimensions(1)*dimensions(2)
call insert(sm, row, row-dimensions(1)*dimensions(2), 0.d0)
enddo
enddo
endif
! the x=0 plane
if(ownedbox(1) .le. 2 .and. ownedbox(2) .ge. 2) then
do i=ownedbox(3), ownedbox(4)
do j=ownedbox(5), ownedbox(6)
row = 2+(i-1)*dimensions(1)+(j-1)*dimensions(1)*dimensions(2)
call insert(sm, row, row-1, 0.d0)
enddo
enddo
endif
! the x=1 plane
if(ownedbox(1) .le. dimensions(1)-1 .and. ownedbox(2) .ge. dimensions(1)-1) then
do i=ownedbox(3), ownedbox(4)
do j=ownedbox(5), ownedbox(6)
row = dimensions(1)-1+(i-1)*dimensions(1)+(j-1)*dimensions(1)*dimensions(2)
call insert(sm, row, row+1, 0.d0)
enddo
enddo
endif
! the y=0 plane
if(ownedbox(3) .le. 2 .and. ownedbox(4) .ge. 2) then
do i=ownedbox(1), ownedbox(2)
do j=ownedbox(5), ownedbox(6)
row = i+dimensions(1)+(j-1)*dimensions(1)*dimensions(2)
call insert(sm, row, row-dimensions(1), 0.d0)
enddo
enddo
endif
! the y=1 plane
if(ownedbox(3) .le. dimensions(2)-1 .and. ownedbox(4) .ge. dimensions(2)-1) then
do i=ownedbox(1), ownedbox(2)
do j=ownedbox(5), ownedbox(6)
row = i+dimensions(1)*(dimensions(2)-2)+(j-1)*dimensions(1)*dimensions(2)
call insert(sm, row, row+dimensions(1), 0.d0)
enddo
enddo
endif
! now do an mpi_allreduce on the rhs
call allreducevector(sm%globalsize, rhs)
end subroutine fillmatrixandrhs
end module PoissonDiscretization

View File

@ -0,0 +1,83 @@
try: paraview.simple
except: from paraview.simple import *
from paraview import coprocessing
#--------------------------------------------------------------
# Code generated from cpstate.py to create the CoProcessor.
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
filename_6_pvti = coprocessor.CreateProducer( datadescription, "input" )
Slice1 = Slice( guiName="Slice1", Crinkleslice=0, SliceOffsetValues=[0.0], Triangulatetheslice=1, SliceType="Plane" )
Slice1.SliceType.Offset = 0.0
Slice1.SliceType.Origin = [49.5, 49.5, 49.5]
Slice1.SliceType.Normal = [1.0, 0.0, 0.0]
ParallelPolyDataWriter1 = coprocessor.CreateWriter( XMLPPolyDataWriter, "slice_%t.pvtp", 10 )
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'input': [10]}
coprocessor.SetUpdateFrequencies(freqs)
return coprocessor
#--------------------------------------------------------------
# Global variables that will hold the pipeline for each timestep
# Creating the CoProcessor object, doesn't actually create the ParaView pipeline.
# It will be automatically setup when coprocessor.UpdateProducers() is called the
# first time.
coprocessor = CreateCoProcessor()
#--------------------------------------------------------------
# Enable Live-Visualizaton with ParaView
coprocessor.EnableLiveVisualization(False)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
if datadescription.GetForceOutput() == True:
# We are just going to request all fields and meshes from the simulation
# code/adaptor.
for i in range(datadescription.GetNumberOfInputDescriptions()):
datadescription.GetInputDescription(i).AllFieldsOn()
datadescription.GetInputDescription(i).GenerateMeshOn()
return
# setup requests for all inputs based on the requirements of the
# pipeline.
coprocessor.LoadRequestedData(datadescription)
# ------------------------ Processing method ------------------------
def DoCoProcessing(datadescription):
"Callback to do co-processing for current timestep"
global coprocessor
# Update the coprocessor by providing it the newly generated simulation data.
# If the pipeline hasn't been setup yet, this will setup the pipeline.
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)

View File

@ -0,0 +1,177 @@
module SparseMatrix
! this uses the MKL sparse matrix storage scheme. it assumes we have a
! node-based partitioning of the grid.
implicit none
public :: initialize, insert, finalize, allreducevector, matvec
type SparseMatrixData
real(kind=8), DIMENSION(:), allocatable :: values
integer, DIMENSION(:), allocatable :: columns, rowindices, mapping
integer :: globalsize
end type SparseMatrixData
contains
subroutine initialize(sm, ownedbox, dimensions)
type(SparseMatrixData), intent(inout) :: sm
integer, intent(in) :: ownedbox(6), dimensions(3)
integer :: numpoints, i, j, k, numx, numy, numz, counter, rowcounter, globalindex
logical :: negativey, positivey, negativez, positivez
integer :: allocatestatus
numpoints = 1
numx = ownedbox(2)-ownedbox(1)+1
if(numx < 1) numx = 1
numy = ownedbox(4)-ownedbox(3)+1
if(numy < 1) numy = 1
numz = ownedbox(6)-ownedbox(5)+1
if(numz < 1) numz = 1
numpoints = numx*numy*numz
! over-allocate both values and columns
allocate(sm%rowindices(numpoints+1), sm%columns(7*numpoints), sm%values(7*numpoints), STAT = allocatestatus)
if (allocatestatus /= 0) STOP "*** SparseMatrix.F90: Not enough memory for arrays ***"
sm%values(:) = 0.d0
sm%columns(:) = -1
! a mapping from the global row index to the local row index
sm%globalsize = dimensions(1)*dimensions(2)*dimensions(3)
allocate(sm%mapping(sm%globalsize))
sm%mapping(:) = -1 ! initialize with bad values
rowcounter = 1
counter = 1
do k=1, numz
negativez = .TRUE.
if(k .eq. 1 .and. ownedbox(5) .eq. 1) negativez = .FALSE.
positivez = .TRUE.
if(k .eq. numz .and. ownedbox(6) .eq. dimensions(3)) positivez = .FALSE.
do j=1, numy
negativey = .TRUE.
if(j .eq. 1 .and. ownedbox(3) .eq. 1) negativey = .FALSE.
positivey = .TRUE.
if(j .eq. numy .and. ownedbox(4) .eq. dimensions(2)) positivey = .FALSE.
do i=1, numx
globalindex = (ownedbox(5)+k-2)*dimensions(1)*dimensions(2)+(ownedbox(3)+j-2)*dimensions(1)+ownedbox(1)+i-1
sm%rowindices(rowcounter) = counter
sm%mapping(globalindex) = rowcounter
rowcounter = rowcounter + 1
if(negativez) then
sm%columns(counter) = globalindex-dimensions(1)*dimensions(2)
counter = counter + 1
endif
if(negativey) then
sm%columns(counter) = globalindex-dimensions(1)
counter = counter + 1
endif
if(i .ne. 1 .or. ownedbox(1) .ne. 1) then
sm%columns(counter) = globalindex-1
counter = counter + 1
endif
sm%columns(counter) = globalindex
counter = counter + 1
if(i .ne. numx .or. ownedbox(2) .ne. dimensions(1)) then
sm%columns(counter) = globalindex+1
counter = counter + 1
endif
if(positivey) then
sm%columns(counter) = globalindex+dimensions(1)
counter = counter + 1
endif
if(positivez) then
sm%columns(counter) = globalindex+dimensions(1)*dimensions(2)
counter = counter + 1
endif
end do
end do
end do
! the final value for ending the row
sm%rowindices(rowcounter) = counter
end subroutine initialize
subroutine finalize(sm)
type(SparseMatrixData), intent(inout) :: sm
if(allocated(sm%values)) deallocate(sm%values)
if(allocated(sm%columns)) deallocate(sm%columns)
if(allocated(sm%rowindices)) deallocate(sm%rowindices)
if(allocated(sm%mapping)) deallocate(sm%mapping)
end subroutine finalize
subroutine insert(sm, row, column, value)
type(SparseMatrixData), intent(inout) :: sm
integer, intent(in) :: row, column
real(kind=8), intent(in) :: value
integer :: i, rowindex
rowindex = sm%mapping(row)
do i=sm%rowindices(rowindex), sm%rowindices(rowindex+1)-1
if(sm%columns(i) .eq. column) then
sm%values(i) = value
return
endif
end do
write(*,*) 'SparseMatrix: bad indices for insert() ', row, column
end subroutine insert
subroutine get(sm, row, column, value)
type(SparseMatrixData), intent(in) :: sm
integer, intent(in) :: row, column
real(kind=8), intent(out) :: value
integer :: i, rowindex
rowindex = sm%mapping(row)
do i=sm%rowindices(rowindex), sm%rowindices(rowindex+1)-1
if(sm%columns(i) .eq. column) then
value = sm%values(i)
return
endif
end do
write(*,*) 'SparseMatrix: bad indices for get() ', row, column
end subroutine get
subroutine matvec(sm, x, b)
!Ax=b
type(SparseMatrixData), intent(inout) :: sm
real(kind=8), intent(in) :: x(:)
real(kind=8), intent(out) :: b(:)
integer :: i, j
do i=1, sm%globalsize
b(i) = 0.d0
if(sm%mapping(i) .gt. 0) then
do j=sm%rowindices(sm%mapping(i)), sm%rowindices(sm%mapping(i)+1)-1
b(i) = b(i) + sm%values(j)*x(sm%columns(j))
end do
endif
end do
call allreducevector(sm%globalsize, b)
end subroutine matvec
subroutine allreducevector(globalsize, vec)
implicit none
include 'mpif.h'
integer, intent(in) :: globalsize
real(kind=8), intent(inout) :: vec(:)
real(kind=8), DIMENSION(:), allocatable :: temp
integer :: ierr, i, numtasks, allocatestatus
call mpi_comm_size(MPI_COMM_WORLD, numtasks, ierr)
if(numtasks .eq. 1) then
return
endif
allocate(temp(globalsize), STAT = allocatestatus)
if (allocatestatus /= 0) STOP "*** SparseMatrix.F90: Not enough memory for temp array ***"
do i=1, globalsize
temp(i) = vec(i)
enddo
call mpi_allreduce(temp, vec, globalsize, MPI_DOUBLE_PRECISION, MPI_SUM, MPI_COMM_WORLD, ierr)
deallocate(temp)
end subroutine allreducevector
end module SparseMatrix

View File

@ -0,0 +1,27 @@
Copyright (c) 2013, Kitware Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of Kitware Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@ -0,0 +1,23 @@
cmake_minimum_required(VERSION 2.8.8)
project(CatalystMPISubCommunicatorExample)
find_package(ParaView 4.2 REQUIRED COMPONENTS vtkPVPythonCatalyst)
include("${PARAVIEW_USE_FILE}")
if(NOT PARAVIEW_USE_MPI)
message(SEND_ERROR "ParaView must be built with MPI enabled")
endif()
add_executable(MPISubCommunicatorExample FEDriver.cxx FEDataStructures.cxx FEAdaptor.cxx)
target_link_libraries(MPISubCommunicatorExample LINK_PRIVATE vtkPVPythonCatalyst vtkParallelMPI)
include(vtkModuleMacros)
include(vtkMPI)
vtk_mpi_link(MPISubCommunicatorExample)
option(BUILD_TESTING "Build Testing" OFF)
# Setup testing.
if (BUILD_TESTING)
include(CTest)
add_test(NAME MPISubCommunicatorExampleTest COMMAND MPISubCommunicatorExample
${CMAKE_CURRENT_SOURCE_DIR}/SampleScripts/feslicescript.py)
set_tests_properties(MPISubCommunicatorExampleTest PROPERTIES LABELS "PARAVIEW;CATALYST")
endif()

View File

@ -0,0 +1,164 @@
#include <iostream>
#include "FEAdaptor.h"
#include "FEDataStructures.h"
#include <vtkCellData.h>
#include <vtkCellType.h>
#include <vtkCPDataDescription.h>
#include <vtkCPInputDataDescription.h>
#include <vtkCPProcessor.h>
#include <vtkCPPythonScriptPipeline.h>
#include <vtkDoubleArray.h>
#include <vtkFloatArray.h>
#include <vtkMPI.h>
#include <vtkNew.h>
#include <vtkPoints.h>
#include <vtkPointData.h>
#include <vtkUnstructuredGrid.h>
namespace
{
vtkCPProcessor* Processor = NULL;
vtkUnstructuredGrid* VTKGrid;
vtkMPICommunicatorOpaqueComm* Comm = NULL;
void BuildVTKGrid(Grid& grid)
{
// create the points information
vtkNew<vtkDoubleArray> pointArray;
pointArray->SetNumberOfComponents(3);
pointArray->SetArray(grid.GetPointsArray(), static_cast<vtkIdType>(grid.GetNumberOfPoints()*3), 1);
vtkNew<vtkPoints> points;
points->SetData(pointArray.GetPointer());
VTKGrid->SetPoints(points.GetPointer());
// create the cells
size_t numCells = grid.GetNumberOfCells();
VTKGrid->Allocate(static_cast<vtkIdType>(numCells*9));
for(size_t cell=0;cell<numCells;cell++)
{
unsigned int* cellPoints = grid.GetCellPoints(cell);
vtkIdType tmp[8] = {cellPoints[0], cellPoints[1], cellPoints[2], cellPoints[3],
cellPoints[4], cellPoints[5], cellPoints[6], cellPoints[7]};
VTKGrid->InsertNextCell(VTK_HEXAHEDRON, 8, tmp);
}
}
void UpdateVTKAttributes(Grid& grid, Attributes& attributes)
{
if(VTKGrid->GetPointData()->GetNumberOfArrays() == 0)
{
// velocity array
vtkNew<vtkDoubleArray> velocity;
velocity->SetName("velocity");
velocity->SetNumberOfComponents(3);
velocity->SetNumberOfTuples(static_cast<vtkIdType>(grid.GetNumberOfPoints()));
VTKGrid->GetPointData()->AddArray(velocity.GetPointer());
}
if(VTKGrid->GetCellData()->GetNumberOfArrays() == 0)
{
// pressure array
vtkNew<vtkFloatArray> pressure;
pressure->SetName("pressure");
pressure->SetNumberOfComponents(1);
VTKGrid->GetCellData()->AddArray(pressure.GetPointer());
}
vtkDoubleArray* velocity = vtkDoubleArray::SafeDownCast(
VTKGrid->GetPointData()->GetArray("velocity"));
// The velocity array is ordered as vx0,vx1,vx2,..,vy0,vy1,vy2,..,vz0,vz1,vz2,..
// so we need to create a full copy of it with VTK's ordering of
// vx0,vy0,vz0,vx1,vy1,vz1,..
double* velocityData = attributes.GetVelocityArray();
vtkIdType numTuples = velocity->GetNumberOfTuples();
for(vtkIdType i=0;i<numTuples;i++)
{
double values[3] = {velocityData[i], velocityData[i+numTuples],
velocityData[i+2*numTuples]};
velocity->SetTupleValue(i, values);
}
vtkFloatArray* pressure = vtkFloatArray::SafeDownCast(
VTKGrid->GetCellData()->GetArray("pressure"));
// The pressure array is a scalar array so we can reuse
// memory as long as we ordered the points properly.
float* pressureData = attributes.GetPressureArray();
pressure->SetArray(pressureData, static_cast<vtkIdType>(grid.GetNumberOfCells()), 1);
}
void BuildVTKDataStructures(Grid& grid, Attributes& attributes)
{
if(VTKGrid == NULL)
{
// The grid structure isn't changing so we only build it
// the first time it's needed. If we needed the memory
// we could delete it and rebuild as necessary.
VTKGrid = vtkUnstructuredGrid::New();
BuildVTKGrid(grid);
}
UpdateVTKAttributes(grid, attributes);
}
}
namespace FEAdaptor
{
void Initialize(int numScripts, char* scripts[], MPI_Comm* handle)
{
if(Processor == NULL)
{
Processor = vtkCPProcessor::New();
Comm = new vtkMPICommunicatorOpaqueComm(handle);
Processor->Initialize(*Comm);
}
else
{
Processor->RemoveAllPipelines();
}
for(int i=1;i<numScripts;i++)
{
vtkNew<vtkCPPythonScriptPipeline> pipeline;
pipeline->Initialize(scripts[i]);
Processor->AddPipeline(pipeline.GetPointer());
}
}
void Finalize()
{
if(Processor)
{
Processor->Finalize();
Processor->Delete();
Processor = NULL;
}
if(VTKGrid)
{
VTKGrid->Delete();
VTKGrid = NULL;
}
if(Comm)
{
delete Comm;
Comm = NULL;
}
}
void CoProcess(Grid& grid, Attributes& attributes, double time,
unsigned int timeStep, bool lastTimeStep)
{
vtkNew<vtkCPDataDescription> dataDescription;
dataDescription->AddInput("input");
dataDescription->SetTimeData(time, timeStep);
if(lastTimeStep == true)
{
// assume that we want to all the pipelines to execute if it
// is the last time step.
dataDescription->ForceOutputOn();
}
if(Processor->RequestDataDescription(dataDescription.GetPointer()) != 0)
{
BuildVTKDataStructures(grid, attributes);
dataDescription->GetInputDescriptionByName("input")->SetGrid(VTKGrid);
Processor->CoProcess(dataDescription.GetPointer());
}
}
} // end of Catalyst namespace

Some files were not shown because too many files have changed in this diff Show More