Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
No results found
Show changes
Commits on Source (6)
  • Mark OLESEN's avatar
    ENH: initial support for overset blanking (volume mesh only) · 14eaf2dd
    Mark OLESEN authored
    - uses the cellCellStencil information to blank holes and the
      interpolated cells.
    
    - needs more detailed attention for handling cases when three or more
      meshes are overlapping.
    14eaf2dd
  • Mark OLESEN's avatar
    ENH: disable function objects in post-process mode · fc136d27
    Mark OLESEN authored
    - prevents accidental overwrite of runtime conversions.
    fc136d27
  • Mark OLESEN's avatar
    ENH: use Catalyst Initialize with working directory (issue #4) · a03e77a8
    Mark OLESEN authored
    - reflects current change being made to the vtkCPProcessor API to
      include chdir capabilities.
    a03e77a8
  • Mark OLESEN's avatar
    ENH: unified catalyst function object (closes #3) · 4f692e4d
    Mark OLESEN authored
    - previously had separate function objects for each category of source
      (finite volume, finite area, lagrangian). This lead to isolation of
      the individual input sources, which highly restricted the flexibility.
    
    - now have a single catalyst function object with an arbitrary number
      of inputs (sources). The catalyst channel name is that of the
      corresponds dictionary name. For volume mesh sources, it is possible
      to address "mesh" and "patches" sub-channels.
    
      Eg,
          mesh = coprocessor.CreateProducer(datadescription, 'myregion/mesh')
    
      This restructuring also aims at integration of other OpenFOAM content
      (eg, surfMesh, sampled probes, sampled surfaces) as a future
      development.
    
    - the output directory (by default "<case>/insitu") is passed onto
      catalyst as its working directory, using the upstream patches that
      now close issue #4
    4f692e4d
  • Mark OLESEN's avatar
    Merge branch 'feature-unified-output' into 'develop' · 82a83cab
    Mark OLESEN authored
    Feature unified output
    
    Closes #3 and #4
    
    See merge request Community/catalyst!1
    82a83cab
  • Mark OLESEN's avatar
    STYLE: typo · dfd64a1b
    Mark OLESEN authored
    dfd64a1b
Showing
with 999 additions and 878 deletions
What:
Library and function object for embedding the ParaView Catalyst
into OpenFOAM.
Requirements:
OpenFOAM.com development version (April-2018 or newer) or OpenFOAM-v1806.
ParaView or ParaView Catalyst 5.4 or newer, compiled with mpi and
python support.
Authors:
Mark Olesen <Mark.Olesen@esi-group.com>
Simone Bna <Simone.Bna@cineca.it>
License:
Same terms as OpenFOAM.
Licensed under GNU General Public License <http://www.gnu.org/licenses/>.
## What
Library and function object for embedding ParaView Catalyst into OpenFOAM.
## Requirements
1. OpenFOAM.com development version (11-May-2018 or newer) or OpenFOAM-v1806.
2. ParaView or ParaView Catalyst 5.5 or newer, compiled with mpi and
python support.
It is highly recommended to patch the ParaView 5.5 sources (eg,
using the OpenFOAM ThirdParty makeParaView script) to ensure that
they properly handle outputting results in directories other than
the main simulation directory:
* [MR2433]
* [MR2436]
## Authors
* Mark Olesen <Mark.Olesen@esi-group.com>
* Simone Bna <Simone.Bna@cineca.it>
## License
Same terms as OpenFOAM.
Licensed under GNU General Public License <http://www.gnu.org/licenses/>.
[MR2433]: https://gitlab.kitware.com/paraview/paraview/merge_requests/2433
[MR2436]: https://gitlab.kitware.com/paraview/paraview/merge_requests/2436
......@@ -10,7 +10,6 @@ def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
for i in range(datadescription.GetNumberOfInputDescriptions()):
inputdescription = datadescription.GetInputDescription(i)
name = datadescription.GetInputDescriptionName(i)
adaptorinput = coprocessor.CreateProducer(datadescription, name)
grid = adaptorinput.GetClientSideObject().GetOutputDataObject(0)
......@@ -40,7 +39,7 @@ def CreateCoProcessor():
print("Don't know how to create a writer for a ", grid.GetClassName())
if extension:
coprocessor.RegisterWriter(writer, filename='insitu/'+name+'_%t'+extension, freq=outputfrequency)
coprocessor.RegisterWriter(writer, filename=name+'_%t'+extension, freq=outputfrequency)
return Pipeline()
......
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: plus |
| \\ / A nd | Web: www.OpenFOAM.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
// Insitu processing of finiteArea fields with ParaView Catalyst
type catalyst::area;
libs ("libcatalystFoam.so");
executeControl timeStep;
writeControl none;
// ************************************************************************* //
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: plus |
| \\ / A nd | Web: www.OpenFOAM.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
// Insitu processing of lagrangian clouds with ParaView Catalyst
type catalyst::cloud;
libs ("libcatalystFoam.so");
executeControl timeStep;
writeControl none;
// ************************************************************************* //
/*--------------------------------*- C++ -*----------------------------------*\
| ========= | |
| \\ / F ield | OpenFOAM: The Open Source CFD Toolbox |
| \\ / O peration | Version: plus |
| \\ / A nd | Web: www.OpenFOAM.com |
| \\/ M anipulation | |
\*---------------------------------------------------------------------------*/
// Insitu processing of finiteVolume fields with ParaView Catalyst
type catalyst;
libs ("libcatalystFoam.so");
executeControl timeStep;
writeControl none;
// ************************************************************************* //
from paraview.simple import *
from paraview import coprocessing
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
input1 = coprocessor.CreateProducer(datadescription, 'input')
writer1 = servermanager.writers.XMLMultiBlockDataWriter(Input=input1)
coprocessor.RegisterWriter(writer1, filename='insitu/area_%t.vtm', freq=2)
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(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)
from paraview.simple import *
from paraview import coprocessing
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
input1 = coprocessor.CreateProducer(datadescription, 'cloud')
writer1 = servermanager.writers.XMLMultiBlockDataWriter(Input=input1)
# register writer with coprocessor, with filename + output freq
coprocessor.RegisterWriter(writer1, filename='insitu/cloud_%t.vtm', freq=10)
return Pipeline()
class CoProcessor(coprocessing.CoProcessor):
def CreatePipeline(self, datadescription):
self.Pipeline = _CreatePipeline(self, datadescription)
coprocessor = CoProcessor()
freqs = {'cloud': [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(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)
......@@ -9,6 +9,7 @@ include_directories(
$ENV{WM_PROJECT_DIR}/src/conversion/lnInclude
$ENV{WM_PROJECT_DIR}/src/meshTools/lnInclude
$ENV{WM_PROJECT_DIR}/src/lagrangian/basic/lnInclude
$ENV{WM_PROJECT_DIR}/src/overset/lnInclude
${CMAKE_CURRENT_SOURCE_DIR}
${CMAKE_CURRENT_BINARY_DIR}
)
......@@ -34,6 +35,26 @@ set(CMAKE_CXX_FLAGS_RELEASE
"-O3 -std=c++11 -Wall -Wextra -Wno-unused-parameter -Wnon-virtual-dtor -Wno-overloaded-virtual")
set(CMAKE_C_FLAGS_RELEASE "-O3 -std=c++11")
# Some characteristics
set(test_file ${CMAKE_CURRENT_BINARY_DIR}/check_initialize.cxx)
file(WRITE ${test_file}
"#include <vtkCPProcessor.h>\n"
"int main() {\n"
" vtkCPProcessor* p = vtkCPProcessor::New();\n"
" p->Initialize(\"AAA\");\n"
" p->Delete();\n"
" return 0;\n"
"}")
try_compile(CATALYST_HAS_WORKING_DIRECTORY
${CMAKE_CURRENT_BINARY_DIR} ${test_file}
LINK_LIBRARIES vtkPVPythonCatalyst
CMAKE_FLAGS "-DINCLUDE_DIRECTORIES=${PARAVIEW_INCLUDE_DIRS}"
)
if (CATALYST_HAS_WORKING_DIRECTORY)
add_definitions(-DUSE_CATALYST_WORKING_DIRECTORY)
endif()
# Set output library destination to OpenFOAM library directory
set(LIBRARY_OUTPUT_PATH $ENV{FOAM_LIBBIN}
CACHE INTERNAL
......@@ -42,6 +63,9 @@ set(LIBRARY_OUTPUT_PATH $ENV{FOAM_LIBBIN}
file(GLOB SOURCE_FILES
catalystCoprocess.C
catalystTools.C
catalystInput.C
catalystFunctionObject.C
cloud/catalystCloud.C
cloud/foamVtkCloudAdaptor.C
......@@ -67,6 +91,7 @@ set(OPENFOAM_LIBRARIES
Pstream
meshTools
lagrangian
overset
)
add_library(
......
......@@ -24,12 +24,10 @@ License
\*---------------------------------------------------------------------------*/
#include "catalystFaMesh.H"
#include "catalystCoprocess.H"
#include "addToRunTimeSelectionTable.H"
#include "faMesh.H"
#include "fvMesh.H"
#include <vtkNew.h>
#include <vtkCPDataDescription.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkInformation.h>
......@@ -38,59 +36,39 @@ License
namespace Foam
{
namespace functionObjects
namespace catalyst
{
defineTypeNameAndDebug(catalystFaMesh, 0);
addToRunTimeSelectionTable(functionObject, catalystFaMesh, dictionary);
defineTypeNameAndDebug(faMeshInput, 0);
addNamedToRunTimeSelectionTable
(
catalystInput,
faMeshInput,
dictionary,
area
);
}
}
// * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
bool Foam::functionObjects::catalystFaMesh::readBasics(const dictionary& dict)
{
int debugLevel = 0;
if (dict.readIfPresent("debug", debugLevel))
{
catalystCoprocess::debug = debugLevel;
}
fileName outputDir;
if (dict.readIfPresent("mkdir", outputDir))
{
outputDir.expand();
outputDir.clean();
Foam::mkDir(outputDir);
}
dict.lookup("scripts") >> scripts_; // Python scripts
catalystCoprocess::expand(scripts_, dict); // Expand and check availability
return true;
}
void Foam::functionObjects::catalystFaMesh::updateState
(
polyMesh::readUpdateState state
)
void Foam::catalyst::faMeshInput::update()
{
// Trigger change of state
// Be really paranoid and verify if the mesh actually exists
const wordList regionNames(backends_.toc());
// Backend requires a corresponding mesh
backends_.filterKeys
(
[this](const word& k){ return meshes_.found(k); }
);
for (const word& regionName : regionNames)
forAllConstIters(meshes_, iter)
{
if (meshes_.found(regionName) && time_.found(regionName))
if (!backends_.found(iter.key()))
{
backends_[regionName]->updateState(state);
}
else
{
backends_.erase(regionName);
meshes_.erase(regionName);
backends_.insert
(
iter.key(),
new Foam::vtk::faMeshAdaptor(*(iter.object()))
);
}
}
}
......@@ -98,52 +76,52 @@ void Foam::functionObjects::catalystFaMesh::updateState
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::functionObjects::catalystFaMesh::catalystFaMesh
Foam::catalyst::faMeshInput::faMeshInput
(
const word& name,
const Time& runTime,
const dictionary& dict
)
:
fvMeshFunctionObject(name, runTime, dict),
catalystInput(name),
time_(runTime),
regionName_(),
selectAreas_(),
selectFields_(),
scripts_(),
meshes_(),
backends_(),
adaptor_()
backends_()
{
read(dict);
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::functionObjects::catalystFaMesh::~catalystFaMesh()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::functionObjects::catalystFaMesh::read(const dictionary& dict)
bool Foam::catalyst::faMeshInput::read(const dictionary& dict)
{
fvMeshFunctionObject::read(dict);
catalystInput::read(dict);
readBasics(dict);
selectAreas_.clear();
selectFields_.clear();
backends_.clear();
// All possible meshes
meshes_ = mesh_.lookupClass<faMesh>();
regionName_ =
dict.lookupOrDefault<word>("region", polyMesh::defaultRegion);
const objectRegistry& obr =
time_.lookupObject<objectRegistry>(regionName_);
// All possible meshes for the given region
meshes_ = obr.lookupClass<faMesh>();
selectAreas_.clear();
dict.readIfPresent("areas", selectAreas_);
if (selectAreas_.empty())
{
word areaName;
if (!dict.readIfPresent("area", areaName))
{
wordList available = mesh_.sortedNames<faMesh>();
wordList available = obr.sortedNames<faMesh>();
if (available.size())
{
......@@ -159,65 +137,49 @@ bool Foam::functionObjects::catalystFaMesh::read(const dictionary& dict)
}
// Restrict to specified meshes
meshes_.filterKeys(wordRes(selectAreas_));
meshes_.filterKeys(selectAreas_);
dict.lookup("fields") >> selectFields_;
Info<< type() << " " << name() << ":" << nl
<<" areas " << flatOutput(selectAreas_) << nl
<<" meshes " << flatOutput(meshes_.sortedToc()) << nl
<<" fields " << flatOutput(selectFields_) << nl
<<" scripts " << scripts_ << nl;
if (adaptor_.valid())
{
// Run-time modification of pipeline
adaptor_().reset(scripts_);
}
// Ensure consistency - only retain backends with corresponding mesh region
backends_.retain(meshes_);
return true;
}
bool Foam::functionObjects::catalystFaMesh::execute()
void Foam::catalyst::faMeshInput::update(polyMesh::readUpdateState state)
{
const wordList regionNames(meshes_.sortedToc());
// Trigger change of state
if (regionNames.empty())
{
return false;
}
const objectRegistry& obr =
time_.lookupObject<objectRegistry>(regionName_);
// Enforce sanity for backends and adaptor
// Be really paranoid and verify if the mesh actually exists
const wordList regionNames(backends_.toc());
for (const word& regionName : regionNames)
{
bool updateAdaptor = false;
forAllConstIters(meshes_, iter)
if (meshes_.found(regionName) && obr.found(regionName))
{
if (!backends_.found(iter.key()))
{
backends_.insert
(
iter.key(),
new Foam::vtk::faMeshAdaptor(*(iter.object()))
);
updateAdaptor = true;
}
backends_[regionName]->updateState(state);
}
if (updateAdaptor && !adaptor_.valid())
else
{
adaptor_.reset(new catalystCoprocess());
adaptor_().reset(scripts_);
backends_.erase(regionName);
meshes_.erase(regionName);
}
}
}
// Gather all fields that we know how to convert
Foam::label Foam::catalyst::faMeshInput::addChannels(dataQuery& dataq)
{
update(); // Enforce sanity for backends and adaptor
if (backends_.empty())
{
return 0;
}
// Gather all fields that we know how to convert
wordHashSet allFields;
forAllConstIters(backends_, iter)
{
......@@ -225,96 +187,89 @@ bool Foam::functionObjects::catalystFaMesh::execute()
}
// Data description for co-processing
vtkNew<vtkCPDataDescription> descrip;
dataq.set(name(), allFields);
// Form data query for catalyst
catalystCoprocess::dataQuery dataq
(
vtk::faMeshAdaptor::channelNames.names(),
time_, // timeQuery
descrip.Get()
);
return 1;
}
// Query catalyst
const HashTable<wordHashSet> expecting(adaptor_().query(dataq, allFields));
if (catalystCoprocess::debug)
bool Foam::catalyst::faMeshInput::convert
(
dataQuery& dataq,
outputChannels& outputs
)
{
const wordList regionNames(backends_.sortedToc());
if (regionNames.empty())
{
if (expecting.empty())
{
Info<< type() << ": expecting no data" << nl;
}
else
{
Info<< type() << ": expecting data " << expecting << nl;
}
return false; // skip - not available
}
if (expecting.empty())
// Single channel only
label nChannels = 0;
if (dataq.found(name()))
{
return true;
++nChannels;
}
if (!nChannels)
{
return false; // skip - not requested
}
auto output = vtkSmartPointer<vtkMultiBlockDataSet>::New();
// TODO: currently don't rely on the results from expecting much at all
// Each region in a separate block
unsigned int regionNo = 0;
unsigned int blockNo = 0;
for (const word& regionName : regionNames)
{
auto pieces = backends_[regionName]->output(selectFields_);
output->SetBlock(regionNo, pieces);
output->GetMetaData(regionNo)->Set
(
vtkCompositeDataSet::NAME(),
regionName
);
++regionNo;
}
if (regionNo)
{
Log << type() << ": send data" << nl;
auto dataset =
backends_[regionName]->output(selectFields_);
adaptor_().process(dataq, output);
}
{
const fileName channel = name();
return true;
}
if (dataq.found(channel))
{
// Get existing or new
vtkSmartPointer<vtkMultiBlockDataSet> block =
outputs.lookup
(
channel,
vtkSmartPointer<vtkMultiBlockDataSet>::New()
);
block->SetBlock(blockNo, dataset);
bool Foam::functionObjects::catalystFaMesh::write()
{
return true;
}
block->GetMetaData(blockNo)->Set
(
vtkCompositeDataSet::NAME(),
regionName
);
outputs.set(channel, block); // overwrites existing
}
}
bool Foam::functionObjects::catalystFaMesh::end()
{
// Only here for extra feedback
if (log && adaptor_.valid())
{
Info<< type() << ": Disconnecting ParaView Catalyst..." << nl;
++blockNo;
}
adaptor_.clear();
return true;
}
void Foam::functionObjects::catalystFaMesh::updateMesh(const mapPolyMesh&)
Foam::Ostream& Foam::catalyst::faMeshInput::print(Ostream& os) const
{
updateState(polyMesh::TOPO_CHANGE);
}
os << name() << nl
<<" areas " << flatOutput(selectAreas_) << nl
<<" meshes " << flatOutput(meshes_.sortedToc()) << nl
<<" fields " << flatOutput(selectFields_) << nl;
void Foam::functionObjects::catalystFaMesh::movePoints(const polyMesh&)
{
updateState(polyMesh::POINTS_MOVED);
return os;
}
......
......@@ -22,51 +22,35 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::functionObjects::catalystFaMesh
Group
grpUtilitiesFunctionObjects
Foam::catalyst::faMeshInput
Description
A Paraview Catalyst adaptor for OpenFOAM faMesh regions.
A Paraview Catalyst source for OpenFOAM faMesh regions.
The output comprises a single "input" channel, which is a multi-block
dataset (one block per area mesh).
The source comprises a single internal "input" channel,
which is a multi-block dataset (one block per area mesh).
On output, the "input" sub-channel receives the name of the source.
Example of function object specification:
Example specification:
\verbatim
catalyst
myArea
{
type catalyst::area;
libs ("libcatalystFoam.so");
type area;
fields (U p);
scripts ( ... );
executeControl timeStep;
executeInterval 1;
}
\endverbatim
Usage
\table
Property | Description | Required | Default
type | catalyst::area | yes |
log | report extra information | no | false
mkdir | initial directory to create | no |
type | area | yes |
region | | no | region0
regions | wordRe list of regions | no |
fields | wordRe list of fields | yes |
scripts | Python pipeline scripts | yes |
\endtable
Note
The execution frequency can be defined by the functionObject and
by the Catalyst pipeline.
See also
Foam::functionObjects::functionObject
Foam::functionObjects::fvMeshFunctionObject
Foam::functionObjects::timeControl
Foam::catalystCoprocess
Foam::catalyst::catalystInput
Foam::vtk::faMeshAdaptor
SourceFiles
......@@ -75,82 +59,73 @@ SourceFiles
\*---------------------------------------------------------------------------*/
#ifndef functionObjects_catalystFaMesh_H
#define functionObjects_catalystFaMesh_H
#ifndef catalyst_faMeshInput_H
#define catalyst_faMeshInput_H
#include "fvMeshFunctionObject.H"
#include "foamVtkFaMeshAdaptor.H"
#include "wordRes.H"
#include "HashPtrTable.H"
#include "catalystInput.H"
#include "foamVtkFaMeshAdaptor.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
// Forward declarations
class catalystCoprocess;
namespace functionObjects
namespace catalyst
{
/*---------------------------------------------------------------------------*\
Class catalystFaMesh Declaration
Class catalyst::faMeshInput Declaration
\*---------------------------------------------------------------------------*/
class catalystFaMesh
class faMeshInput
:
public fvMeshFunctionObject
public catalystInput
{
protected:
// Protected data
//- Reference to the time database
const Time& time_;
//- The region name for the area meshes
word regionName_;
//- Requested names of areas to process
wordRes selectAreas_;
//- Names of fields to process
wordRes selectFields_;
//- Python scripts for the catalyst pipeline
stringList scripts_;
//- Pointers to the requested mesh regions
HashTable<const faMesh*> meshes_;
//- Backends for OpenFOAM to VTK translation (with internal caching)
HashPtrTable<vtk::faMeshAdaptor> backends_;
//- The catalyst coprocess
autoPtr<catalystCoprocess> adaptor_;
// Protected Member Functions
//- Common boilerplate settings
bool readBasics(const dictionary& dict);
//- On movement
void updateState(polyMesh::readUpdateState state);
//- Update/synchronize internals with catalyst backends
void update();
//- No copy construct
catalystFaMesh(const catalystFaMesh&) = delete;
faMeshInput(const faMeshInput&) = delete;
//- No copy assignment
void operator=(const catalystFaMesh&) = delete;
void operator=(const faMeshInput&) = delete;
public:
//- Runtime type information
TypeName("catalyst::area");
ClassName("catalyst::faMesh");
// Constructors
//- Construct from Time and dictionary
catalystFaMesh
faMeshInput
(
const word& name,
const Time& runTime,
......@@ -159,7 +134,7 @@ public:
//- Destructor
virtual ~catalystFaMesh();
virtual ~faMeshInput() = default;
// Member Functions
......@@ -167,27 +142,23 @@ public:
//- Read the specification
virtual bool read(const dictionary& dict);
//- Execute catalyst pipelines
virtual bool execute();
//- Write - does nothing
virtual bool write();
//- On end - provide feedback about disconnecting from catatyst.
virtual bool end();
//- Update for changes of mesh or mesh point-motion
virtual void update(polyMesh::readUpdateState state);
//- Update for changes of mesh
virtual void updateMesh(const mapPolyMesh& mpm);
//- Add available channels (with fields) to data query
virtual label addChannels(dataQuery& dataq);
//- Update for mesh point-motion
virtual void movePoints(const polyMesh& mesh);
//- Convert channels to vtkMultiBlockDataSet outputs
virtual bool convert(dataQuery& dataq, outputChannels& outputs);
//- Print information
virtual Ostream& print(Ostream& os) const;
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace functionObjects
} // End namespace catalyst
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
......
......@@ -46,16 +46,6 @@ namespace vtk
}
}
const Foam::Enum
<
Foam::vtk::faMeshAdaptor::channel
>
Foam::vtk::faMeshAdaptor::channelNames
{
{ channel::INPUT, "input" },
};
const Foam::word Foam::vtk::faMeshAdaptor::internalName("internal");
......
......@@ -45,7 +45,6 @@ SourceFiles
#include "fileName.H"
#include "stringList.H"
#include "wordList.H"
#include "Enum.H"
#include "polyMesh.H"
#include "areaFieldsFwd.H"
#include "foamVtkTools.H"
......@@ -89,14 +88,6 @@ public:
//- Public Data Members
//- The Catalyst output channels
enum channel
{
INPUT = 0x3
};
static const Enum<channel> channelNames;
//- Name for internal mesh ("internal")
static const word internalName;
......
......@@ -28,11 +28,6 @@ License
// OpenFOAM includes
#include "error.H"
#include "emptyFvPatchField.H"
#include "wallPolyPatch.H"
#include "volPointInterpolation.H"
#include "zeroGradientFvPatchField.H"
#include "interpolatePointToCell.H"
// vtk includes
#include "vtkFloatArray.h"
......
......@@ -24,8 +24,6 @@ License
#include "catalystCoprocess.H"
#include "Time.H"
#include "stringOps.H"
#include "OSspecific.H"
// VTK includes
#include <vtkNew.h>
......@@ -42,135 +40,38 @@ License
namespace Foam
{
defineTypeNameAndDebug(catalystCoprocess, 0);
}
// * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
namespace Foam
namespace catalyst
{
namespace
{
#if 0
static void printInfo(vtkCPDataDescription* descrip)
{
if (!descrip)
{
return;
}
const unsigned nItems = descrip->GetNumberOfInputDescriptions();
for (unsigned itemi = 0; itemi < nItems; ++itemi)
{
vtkCPInputDataDescription* input = descrip->GetInputDescription(itemi);
if (!input) continue; // should not happen
Info<<"input: " << descrip->GetInputDescriptionName(itemi) << nl;
const unsigned nFields = input->GetNumberOfFields();
for (unsigned fieldi = 0; fieldi < nFields; ++fieldi)
{
Info<< " field: " << input->GetFieldName(fieldi) << nl;
}
if (!nFields) Info<<" no fields requested" << nl;
}
defineTypeNameAndDebug(coprocess, 0);
}
#endif
} // End anonymous namespace
} // End namespace Foam
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
Foam::label Foam::catalystCoprocess::expand
(
List<string>& scripts,
const dictionary& dict
)
{
label nscript = 0;
forAll(scripts, scripti)
{
string& s = scripts[scripti];
stringOps::inplaceExpand(s, dict, true, true);
fileName::clean(s); // Remove trailing, repeated slashes etc.
if (isFile(s))
{
if (nscript != scripti)
{
scripts[nscript] = std::move(s);
}
++nscript;
}
}
scripts.resize(nscript);
return nscript;
}
// * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
template<class DataType>
bool Foam::catalystCoprocess::processImpl
(
const dataQuery& dataq,
vtkSmartPointer<DataType>& output
)
{
vtkCPDataDescription* descrip = dataq.get();
if (!coproc_->RequestDataDescription(descrip))
{
return false;
}
for (const word& chanName : dataq.channels())
{
auto* input = descrip->GetInputDescriptionByName(chanName.c_str());
if (input && input->GetIfGridIsNecessary())
{
input->SetGrid(output);
}
}
coproc_->CoProcess(descrip);
return true;
}
template<class DataType>
bool Foam::catalystCoprocess::processImpl
bool Foam::catalyst::coprocess::processImpl
(
const dataQuery& dataq,
HashTable<vtkSmartPointer<DataType>>& outputs
HashTable<vtkSmartPointer<DataType>, fileName>& outputs
)
{
vtkCPDataDescription* descrip = dataq.get();
auto* descrip = dataq.description();
if (!coproc_->RequestDataDescription(descrip))
{
return false;
}
for (const word& chanName : dataq.channels())
for (const fileName& channel : dataq.names())
{
if (outputs.found(chanName))
if (outputs.found(channel))
{
auto* input = descrip->GetInputDescriptionByName(chanName.c_str());
auto* input = descrip->GetInputDescriptionByName(channel.c_str());
if (input && input->GetIfGridIsNecessary())
{
input->SetGrid(outputs[chanName]);
input->SetGrid(outputs[channel]);
}
}
}
......@@ -180,21 +81,11 @@ bool Foam::catalystCoprocess::processImpl
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::catalystCoprocess::timeQuery::timeQuery(const Foam::Time& t)
:
timeValue(t.timeOutputValue()),
timeIndex(t.timeIndex()),
forced(t.timeOutputValue() >= t.endTime().value())
{}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::catalystCoprocess::~catalystCoprocess()
Foam::catalyst::coprocess::~coprocess()
{
// stop(), but without output
// As per stop(), but without output
if (coproc_)
{
coproc_->Delete();
......@@ -205,13 +96,13 @@ Foam::catalystCoprocess::~catalystCoprocess()
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::catalystCoprocess::good() const
bool Foam::catalyst::coprocess::good() const
{
return coproc_;
}
void Foam::catalystCoprocess::stop()
void Foam::catalyst::coprocess::stop()
{
if (coproc_)
{
......@@ -222,8 +113,33 @@ void Foam::catalystCoprocess::stop()
}
void Foam::catalystCoprocess::reset()
void Foam::catalyst::coprocess::reset(const fileName& outputDir)
{
#ifdef USE_CATALYST_WORKING_DIRECTORY
if (coproc_ == nullptr)
{
coproc_ = vtkCPProcessor::New();
coproc_->Initialize(outputDir.c_str());
Info<< "Connecting ParaView Catalyst..." << endl;
}
else
{
coproc_->RemoveAllPipelines();
if (outputDir == coproc_->GetWorkingDirectory())
{
Info<< "Rebinding ParaView Catalyst..." << endl;
}
else
{
// Changed working directory ... redo everything.
coproc_->Delete();
coproc_ = nullptr;
reset(outputDir);
}
}
#else
if (coproc_ == nullptr)
{
coproc_ = vtkCPProcessor::New();
......@@ -235,12 +151,19 @@ void Foam::catalystCoprocess::reset()
coproc_->RemoveAllPipelines();
Info<< "Rebinding ParaView Catalyst..." << endl;
}
Info<< " Caution: using current working directory" << nl
<< " which may not be the same as the simulation directory" << endl;
#endif
}
void Foam::catalystCoprocess::reset(const UList<string>& scripts)
void Foam::catalyst::coprocess::reset
(
const fileName& outputDir,
const UList<string>& scripts
)
{
reset();
reset(outputDir);
int nscript = 0;
for (const auto& script : scripts)
......@@ -257,139 +180,20 @@ void Foam::catalystCoprocess::reset(const UList<string>& scripts)
}
Foam::HashTable<Foam::wordHashSet>
Foam::catalystCoprocess::query
(
dataQuery& dataq,
const wordHashSet& allFields
)
{
// Desirable to also know which fields have been requested
HashTable<wordHashSet> requests;
if (!good())
{
Info<< "No ParaView Catalyst initialized" << endl;
return requests;
}
if (dataq.channels().empty())
{
// No channels names have been published by the simulation
return requests;
}
vtkCPDataDescription* descrip = dataq.get();
descrip->SetTimeData(dataq.timeValue, dataq.timeIndex);
descrip->SetForceOutput(dataq.forced);
// Sort out which channels already exist, are new, or disappeared
{
// The currently defined channels
wordHashSet currChannels;
const unsigned n = descrip->GetNumberOfInputDescriptions();
for (unsigned i=0; i < n; ++i)
{
currChannels.insert
(
word::validate(descrip->GetInputDescriptionName(i))
);
}
wordHashSet newChannels(dataq.channels());
wordHashSet oldChannels(currChannels);
oldChannels.erase(newChannels);
if (oldChannels.size())
{
descrip->ResetAll();
}
else
{
newChannels.erase(currChannels);
}
// Add channels
for (const word& chanName : newChannels)
{
descrip->AddInput(chanName.c_str());
auto* input = descrip->GetInputDescriptionByName(chanName.c_str());
for (const word& fieldName : allFields)
{
input->AddPointField(fieldName.c_str());
input->AddCellField(fieldName.c_str());
}
}
// Note: this misses updating field information for previously
// existing inputs.
}
if
(
!coproc_->RequestDataDescription(descrip)
|| !descrip->GetIfAnyGridNecessary()
)
{
return requests;
}
for (const word& chanName : dataq.channels())
{
auto* input = descrip->GetInputDescriptionByName(chanName.c_str());
if (input && input->GetIfGridIsNecessary())
{
wordHashSet& fields = requests(chanName); // auto-vivify
for (const word& fieldName : allFields)
{
if (input->IsFieldNeeded(fieldName.c_str()))
{
fields.insert(fieldName);
}
}
}
}
return requests;
}
bool Foam::catalystCoprocess::process
(
const dataQuery& dataq,
vtkSmartPointer<vtkMultiBlockDataSet>& output
)
Foam::label Foam::catalyst::coprocess::query(dataQuery& dataq)
{
return processImpl(dataq, output);
return dataq.query(coproc_);
}
bool Foam::catalystCoprocess::process
bool Foam::catalyst::coprocess::process
(
const dataQuery& dataq,
HashTable<vtkSmartPointer<vtkMultiBlockDataSet>>& outputs
outputChannels& outputs
)
{
return processImpl(dataq, outputs);
}
// * * * * * * * * * * * * * * * Ostream Operator * * * * * * * * * * * * * //
Foam::Ostream& Foam::operator<<
(
Ostream& os,
const catalystCoprocess::timeQuery& when
)
{
os << "Time = " << when.timeValue << ", index: " << when.timeIndex;
return os;
}
// ************************************************************************* //
......@@ -22,136 +22,37 @@ License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::catalystCoprocess
Foam::catalyst::coprocess
Description
Low-level interface between OpenFOAM and ParaView Catalyst.
\code
... initialize catalyst
... define a data query for catalyst
// Data description for co-processing
vtkNew<vtkCPDataDescription> descrip;
// Form data query for catalyst
catalystCoprocess::dataQuery dataq(channelNames, runTime, descrip.Get());
// Query catalyst
HashTable<wordHashSet> expecting = adaptor_().query(dataq, fields);
\endcode
SourceFiles
catalystCoprocess.C
\*---------------------------------------------------------------------------*/
#ifndef functionObjects_catalystCoprocess_H
#define functionObjects_catalystCoprocess_H
#ifndef catalyst_coprocess_H
#define catalyst_coprocess_H
#include "className.H"
#include "wordList.H"
#include "stringList.H"
#include "HashSet.H"
#include "catalystTools.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
// Forward Declarations
class vtkCPProcessor;
class vtkCPDataDescription;
class vtkMultiBlockDataSet;
template<class T> class vtkSmartPointer;
namespace Foam
{
// Forward Declarations
class dictionary;
class Time;
class Ostream;
class catalystCoprocess;
namespace catalyst
{
/*---------------------------------------------------------------------------*\
Class catalystCoprocess Declaration
Class catalyst::coprocess Declaration
\*---------------------------------------------------------------------------*/
class catalystCoprocess
class coprocess
{
public:
//- Simple structure for time queries
struct timeQuery
{
double timeValue;
long timeIndex;
bool forced;
constexpr timeQuery
(
double val,
long index,
bool forcedOutput=false
) noexcept
:
timeValue(val),
timeIndex(index),
forced(forcedOutput)
{}
timeQuery(const Foam::Time& currTime);
};
//- Simple structure for data description queries
// The storage for the description is held outside this class
class dataQuery
:
public timeQuery
{
//- Catalyst channel names to query
List<word> channels_;
mutable vtkCPDataDescription* descrip_;
public:
dataQuery
(
const UList<word>& channelNames,
const timeQuery& when,
vtkCPDataDescription* description
)
:
timeQuery(when),
channels_(channelNames),
descrip_(description)
{}
dataQuery
(
const UList<word>& channelNames,
const Foam::Time& when,
vtkCPDataDescription* description
)
:
dataQuery(channelNames, timeQuery(when), description)
{}
//- The declared output channels
const UList<word>& channels() const
{
return channels_;
}
//- Pointer to coprocess data description
vtkCPDataDescription* get() const
{
return this->descrip_;
}
};
private:
// Private Data
......@@ -162,30 +63,16 @@ private:
// Private Member Functions
//- Process single output channel
template<class DataType>
bool processImpl
(
const dataQuery& query,
vtkSmartPointer<DataType>& outputs
);
//- Process multiple output channels
template<class DataType>
bool processImpl
(
const dataQuery& query,
HashTable<vtkSmartPointer<DataType>>& outputs
const dataQuery& dataq,
HashTable<vtkSmartPointer<DataType>, fileName>& outputs
);
public:
// Static Methods
//- Expand strings as filenames, retaining only those that exist
static label expand(List<string>& scripts, const dictionary& dict);
//- Define class name and debug
ClassName("catalyst");
......@@ -193,14 +80,14 @@ public:
// Constructors
//- Construct null. Does not initialize catalyst.
constexpr catalystCoprocess() noexcept
constexpr coprocess() noexcept
:
coproc_(nullptr)
{}
//- Destructor. Shutdown process.
~catalystCoprocess();
~coprocess();
// Member Functions
......@@ -208,46 +95,34 @@ public:
//- \return True if the coprocess has been initialized.
bool good() const;
//- Reset/initialize pipeline without pipeline scripts.
void reset();
//- Reset/initialize pipeline with output directory, but without
//- pipeline scripts.
void reset(const fileName& outputDir);
//- Reset/initialize pipeline with python scripts.
//- Reset/initialize pipeline with output directory and with
//- pipeline scripts.
// The script names must have already been resolved prior calling this.
void reset(const UList<string>& scripts);
void reset
(
const fileName& outputDir,
const UList<string>& scripts
);
//- Query the coprocess pipelines if they should be executed at this
//- iteration and possibly which fields they require.
//
// \param[in,out] dataq the data query for catalyst.
// On input it contains the published channel names, the current
// simulation time (index, value) and allocation for the coprocess
// data description.
// On output the data description will be filled with the field
// names added per channel.
// \param[in] allFields the fields that can be published from the
// simulation.
// On input it contains the published channel names with their
// published fields, as well as the current simulation time
// (index, value).
// On output the data description part of dataq will be filled
// with the requested field names added per channel.
//
// \return HashTable with fields requested (what Catalyst expects)
// on a per-channel basis.
HashTable<wordHashSet> query
(
dataQuery& dataq,
const wordHashSet& allFields
);
// \return number of channels request at this time
label query(dataQuery& dataq);
//- Single-channel source (eg, "input" or "cloud", ...)
bool process
(
const dataQuery& dataq,
vtkSmartPointer<vtkMultiBlockDataSet>& output
);
//- Multi-channel source (eg, "input", "mesh", "patches")
bool process
(
const dataQuery& dataq,
HashTable<vtkSmartPointer<vtkMultiBlockDataSet>>& outputs
);
//- Process the output channels
bool process(const dataQuery& dataq, outputChannels& outputs);
//- Finalize
void stop();
......@@ -255,10 +130,9 @@ public:
};
// Ostream Operator
Ostream& operator<<(Ostream& os, const catalystCoprocess::timeQuery& when);
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace catalyst
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
......
// ParaView Catalyst function object for OpenFOAM (-*- C++ -*-)
functions
{
catalyst
{
type catalyst;
libs ("libcatalystFoam.so");
executeControl timeStep;
writeControl none;
// OR #includeEtc "caseDicts/insitu/catalyst/catalyst.cfg"
// mkdir "<case>/someDir";
scripts
(
// "<etc>/caseDicts/insitu/catalyst/printChannels.py"
"<etc>/caseDicts/insitu/catalyst/writeAll.py"
);
inputs
{
// fvMesh
region
{
type default;
// All regions
regions (".*");
// Selected fields (words or regex)
fields (T U p);
}
// faMesh
area
{
type area;
// Selected fields (words or regex)
fields ( ".*" );
}
// lagrangian
cloud
{
type cloud;
// Selected clouds (words or regex)
clouds ( coalCloud1 limestoneCloud1 );
// Selected fields (words or regex)
fields ( T U p rho "Y.*" );
}
}
}
}
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
\\/ M anipulation | Copyright (C) 2018 CINECA
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "catalystFunctionObject.H"
#include "catalystCoprocess.H"
#include "stringOps.H"
#include "OSHA1stream.H"
#include "OSspecific.H"
#include "addToRunTimeSelectionTable.H"
#include <vtkCPDataDescription.h>
#include <vtkMultiBlockDataSet.h>
#include <vtkInformation.h>
#include <vtkSmartPointer.h>
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace functionObjects
{
defineTypeNameAndDebug(catalystFunctionObject, 0);
addToRunTimeSelectionTable
(
functionObject,
catalystFunctionObject,
dictionary
);
} // End namespace functionObjects
} // End namespace Foam
// * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
Foam::label Foam::functionObjects::catalystFunctionObject::expand
(
List<string>& scripts,
const dictionary& dict
)
{
label nscript = 0;
forAll(scripts, scripti)
{
string& s = scripts[scripti];
stringOps::inplaceExpand(s, dict, true, true);
fileName::clean(s); // Remove trailing, repeated slashes etc.
if (isFile(s))
{
if (nscript != scripti)
{
scripts[nscript] = std::move(s);
}
++nscript;
}
}
scripts.resize(nscript);
return nscript;
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::functionObjects::catalystFunctionObject::catalystFunctionObject
(
const word& name,
const Time& runTime,
const dictionary& dict
)
:
functionObject(name),
time_(runTime),
outputDir_("<case>/insitu"),
scripts_(),
adaptor_(),
inputs_()
{
if (postProcess)
{
// Disable for post-process mode.
// Emit as FatalError for the try/catch in the caller.
FatalError
<< type() << " disabled in post-process mode"
<< exit(FatalError);
}
if (!read(dict))
{
FatalError
<< type() << " with errors"
<< exit(FatalError);
}
}
// * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
Foam::functionObjects::catalystFunctionObject::~catalystFunctionObject()
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
bool Foam::functionObjects::catalystFunctionObject::read(const dictionary& dict)
{
functionObject::read(dict);
const dictionary* inputDictPtr = dict.subDictPtr("inputs");
if (!inputDictPtr || inputDictPtr->empty())
{
inputs_.clear();
WarningInFunction
<< type() << " missing inputs" << endl;
return false;
}
int debugLevel = 0;
if (dict.readIfPresent("debug", debugLevel))
{
catalyst::coprocess::debug = debugLevel;
}
if (Pstream::master())
{
fileName dir;
if (dict.readIfPresent("mkdir", dir))
{
dir.expand();
dir.clean();
Foam::mkDir(dir);
}
}
// Track changes in outputDir and/or scripts
OSHA1stream osha1;
osha1 << outputDir_ << scripts_;
const SHA1Digest oldDigest(osha1.digest());
dict.readIfPresent("outputDir", outputDir_);
outputDir_.expand();
outputDir_.clean();
if (Pstream::master())
{
Foam::mkDir(outputDir_);
}
dict.lookup("scripts") >> scripts_; // Python scripts
expand(scripts_, dict); // Expand and check availability
// Any changes detected
osha1.reset();
osha1 << outputDir_ << scripts_;
if (adaptor_.valid() && oldDigest != osha1.digest())
{
adaptor_().reset(outputDir_, scripts_);
}
PtrList<catalyst::catalystInput> newList(inputDictPtr->size());
label nInputs = 0;
forAllConstIters(*inputDictPtr, iter)
{
const dictionary* subDictPtr = iter().dictPtr();
if (iter().keyword().isPattern() || !subDictPtr)
{
continue;
}
newList.set
(
nInputs,
catalyst::catalystInput::New
(
word(iter().keyword()),
time_,
*subDictPtr
).ptr()
);
++nInputs;
}
newList.resize(nInputs);
inputs_.transfer(newList);
if (inputs_.empty())
{
WarningInFunction
<< type() << " missing inputs" << endl;
}
else
{
Info<< type() << " " << name() << ":" << nl
<< " scripts " << scripts_ << nl
<< " inputs:" << nl
<< "(" << nl;
nInputs = 0;
for (const auto& inp : inputs_)
{
if (nInputs++) Info << nl;
inp.print(Info);
}
Info<< ")" << nl;
}
return true;
}
bool Foam::functionObjects::catalystFunctionObject::execute()
{
// Enforce sanity for backends and adaptor
if (inputs_.empty())
{
return false;
}
if (!adaptor_.valid())
{
adaptor_.reset(new catalyst::coprocess());
adaptor_().reset(outputDir_, scripts_);
}
catalyst::dataQuery dataq(time_);
label nChannels = 0;
for (auto& inp : inputs_)
{
nChannels += inp.addChannels(dataq);
}
if (nChannels)
{
nChannels = adaptor_().query(dataq);
}
if (catalyst::coprocess::debug)
{
Info<< type() << ": expecting data for " << nChannels << nl;
}
if (!nChannels)
{
return true;
}
catalyst::outputChannels outputs;
for (auto& inp : inputs_)
{
inp.convert(dataq, outputs);
}
if (outputs.size())
{
Log << type() << ": send data" << nl;
adaptor_().process(dataq, outputs);
}
return true;
}
bool Foam::functionObjects::catalystFunctionObject::write()
{
return true;
}
bool Foam::functionObjects::catalystFunctionObject::end()
{
// Only here for extra feedback
if (log && adaptor_.valid())
{
Info<< type() << ": Disconnecting ParaView Catalyst..." << nl;
}
adaptor_.clear();
return true;
}
void Foam::functionObjects::catalystFunctionObject::updateMesh
(
const mapPolyMesh&
)
{
for (auto& inp : inputs_)
{
inp.update(polyMesh::TOPO_CHANGE);
}
}
void Foam::functionObjects::catalystFunctionObject::movePoints
(
const polyMesh&
)
{
for (auto& inp : inputs_)
{
inp.update(polyMesh::POINTS_MOVED);
}
}
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
\\/ M anipulation | Copyright (C) 2018 CINECA
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
Class
Foam::functionObjects::catalystFunctionObject
Group
grpUtilitiesFunctionObjects
Description
A Paraview Catalyst adaptor for OpenFOAM fvMesh regions.
The output comprises up to three channels ("input", "mesh", "patches"),
each of which is a multi-block dataset.
Example of function object specification:
\verbatim
catalyst
{
type catalyst;
libs ("libcatalystFoam.so");
scripts ( ... );
executeControl timeStep;
executeInterval 1;
inputs
{
type default;
regions ( ".*Solid" )
fields (U p);
}
}
\endverbatim
Usage
\table
Property | Description | Required | Default
type | catalyst | yes |
log | report extra information | no | false
mkdir | initial directory to create | no |
scripts | Python pipeline scripts | yes |
\endtable
Note
The execution frequency can be defined by the functionObject and
by the Catalyst pipeline.
See also
Foam::functionObjects::functionObject
Foam::functionObjects::fvMeshFunctionObject
Foam::functionObjects::timeControl
Foam::catalyst::coprocess
Foam::catalyst::cloudInput
Foam::catalyst::faMeshInput
Foam::catalyst::fvMeshInput
SourceFiles
catalystFunctionObject.C
catalystFunctionObjectTemplates.C
\*---------------------------------------------------------------------------*/
#ifndef functionObjects_catalystFunctionObject_H
#define functionObjects_catalystFunctionObject_H
#include "className.H"
#include "wordList.H"
#include "stringList.H"
#include "polyMesh.H"
#include "PtrList.H"
#include "functionObject.H"
#include "catalystCoprocess.H"
#include "catalystInput.H"
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
namespace Foam
{
namespace functionObjects
{
/*---------------------------------------------------------------------------*\
Class catalystFunctionObject Declaration
\*---------------------------------------------------------------------------*/
class catalystFunctionObject
:
public functionObject
{
// Private data
//- Reference to the time database
const Time& time_;
//- The output directory
fileName outputDir_;
//- Python scripts for the catalyst pipeline
stringList scripts_;
//- The catalyst coprocess
autoPtr<catalyst::coprocess> adaptor_;
//- Pointers to the requested mesh regions
PtrList<catalyst::catalystInput> inputs_;
// Private Member Functions
//- No copy construct
catalystFunctionObject(const catalystFunctionObject&) = delete;
//- No copy assignment
void operator=(const catalystFunctionObject&) = delete;
public:
// Static Methods
//- Expand strings as filenames, retaining only those that exist
static label expand(List<string>& scripts, const dictionary& dict);
//- Runtime type information
TypeName("catalyst");
// Constructors
//- Construct from Time and dictionary
catalystFunctionObject
(
const word& name,
const Time& runTime,
const dictionary& dict
);
//- Destructor
virtual ~catalystFunctionObject();
// Member Functions
//- Read the specification
virtual bool read(const dictionary& dict);
//- Execute catalyst pipelines
virtual bool execute();
//- Write - does nothing
virtual bool write();
//- On end - provide feedback about disconnecting from catatyst.
virtual bool end();
//- Update for changes of mesh
virtual void updateMesh(const mapPolyMesh& mpm);
//- Update for mesh point-motion
virtual void movePoints(const polyMesh& mesh);
};
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
} // End namespace functionObjects
} // End namespace Foam
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
#endif
// ************************************************************************* //
/*---------------------------------------------------------------------------*\
========= |
\\ / F ield | OpenFOAM: The Open Source CFD Toolbox
\\ / O peration |
\\ / A nd | Copyright (C) 2018 OpenCFD Ltd.
\\/ M anipulation |
-------------------------------------------------------------------------------
License
This file is part of OpenFOAM.
OpenFOAM is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
for more details.
You should have received a copy of the GNU General Public License
along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
\*---------------------------------------------------------------------------*/
#include "catalystInput.H"
#include "dictionary.H"
#include "Time.H"
#include "addToRunTimeSelectionTable.H"
// * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
namespace Foam
{
namespace catalyst
{
defineTypeNameAndDebug(catalystInput, 0);
defineRunTimeSelectionTable(catalystInput, dictionary);
}
}
// * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
Foam::catalyst::catalystInput::catalystInput(const word& channel)
:
name_(channel)
{}
// * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
Foam::autoPtr<Foam::catalyst::catalystInput>
Foam::catalyst::catalystInput::New
(
const word& name,
const Time& runTime,
const dictionary& dict
)
{
const word sourceType = dict.lookupOrDefault<word>("type", "default");
auto cstrIter = dictionaryConstructorTablePtr_->cfind(sourceType);
if (!cstrIter.found())
{
FatalErrorInFunction
<< "Unknown catalystInput "
<< sourceType << nl << nl
<< "Valid sources : " << endl
<< dictionaryConstructorTablePtr_->sortedToc()
<< exit(FatalError);
}
return autoPtr<catalystInput>(cstrIter()(name, runTime, dict));
}
bool Foam::catalyst::catalystInput::read(const dictionary&)
{
return true;
}
void Foam::catalyst::catalystInput::update(polyMesh::readUpdateState state)
{}
Foam::Ostream& Foam::catalyst::catalystInput::print(Ostream& os) const
{
return os;
}
// ************************************************************************* //