Commit 8914bb2c authored by Lars Bilke's avatar Lars Bilke
Browse files

Merge branch 'insitu-refactor-new' into 'master'

Refactor Insitu-functionality for VTK 9 / ParaView 5.8

See merge request ogs/ogs!3092
parents 5d48f9ed df375f5e
# .git
Tests/Data
_out/images
......@@ -18,6 +18,7 @@
#include "MeshLib/Mesh.h"
#include "MeshLib/Vtk/VtkMappedMeshSource.h"
#include "filesystem.h"
namespace InSituLib
{
......@@ -39,12 +40,19 @@ void Initialize(BaseLib::ConfigTree const& scripts_config,
for (auto script_config : scripts_config.getConfigSubtreeList("script"))
{
//! \ogs_file_param{prj__insitu__scripts__script__name}
auto scriptName = script_config.getConfigParameter<std::string>("name");
INFO("Initializing in-situ script: {:s}", scriptName);
std::stringstream ss;
ss << path << scriptName;
auto scriptPath =
fs::path(script_config.getConfigParameter<std::string>("name"));
if (scriptPath.is_relative())
{
scriptPath = fs::path(path) / scriptPath;
}
if (!fs::exists(scriptPath))
{
ERR("In-situ script {:s} does not exist!", scriptPath.string());
}
INFO("Initializing in-situ script: {:s}", scriptPath.string());
vtkNew<vtkCPPythonScriptPipeline> pipeline;
pipeline->Initialize(ss.str().c_str());
pipeline->Initialize(scriptPath.c_str());
Processor->AddPipeline(pipeline.GetPointer());
}
}
......@@ -57,7 +65,8 @@ void Finalize()
}
}
void CoProcess(MeshLib::Mesh const& mesh, double const time,
unsigned int const timeStep, bool const lastTimeStep)
unsigned int const timeStep, bool const lastTimeStep,
std::string output_directory)
{
if (Processor == nullptr)
return;
......@@ -80,8 +89,11 @@ void CoProcess(MeshLib::Mesh const& mesh, double const time,
vtkSource->Update();
dataDescription->GetInputDescriptionByName("input")->SetGrid(
vtkSource->GetOutput());
auto const cwd = fs::current_path();
fs::current_path(std::move(output_directory));
Processor->CoProcess(dataDescription.GetPointer());
fs::current_path(cwd);
INFO("End InSitu process.");
}
}
} // namespace
} // namespace InSituLib
......@@ -20,5 +20,6 @@ void Initialize(BaseLib::ConfigTree const& scripts_config,
std::string const& path);
void Finalize();
void CoProcess(MeshLib::Mesh const& mesh, double const time,
unsigned int const timeStep, bool const lastTimeStep);
unsigned int const timeStep, bool const lastTimeStep,
std::string output_directory);
}
......@@ -3,5 +3,15 @@ get_source_files(SOURCES)
# Library
ogs_add_library(InSituLib ${SOURCES})
if(BUILD_SHARED_LIBS)
install(TARGETS InSituLib LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
endif()
target_link_libraries(PUBLIC BaseLib PRIVATE MeshLib)
target_link_libraries(InSituLib
INTERFACE
VTK::PythonUsed
PRIVATE
ParaView::PythonCatalyst
VTK::CommonDataModel)
target_link_libraries(InSituLib PUBLIC BaseLib PRIVATE MeshLib)
# OGS insitu-visualization with ParaView Catalyst
## Getting started
Requirements:
- ParaView insitu-build, at least commit [056de649](https://gitlab.kitware.com/paraview/paraview/commit/056de649320f52c8a14668ffa383d7361313a133), and therefore Conan is not supported
### Build ParaView
```bash
git clone --recursive https://gitlab.kitware.com/paraview/paraview.git
mkdir paraview_build paraview_install
cd paraview_build
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release -DPARAVIEW_USE_PYTHON=ON \
-DPARAVIEW_BUILD_EDITION=CATALYST -DCMAKE_INSTALL_PREFIX=../paraview_install \
../paraview
ninja install
```
### Build OGS
```bash
cmake -DCMAKE_BUILD_TYPE=Release -DOGS_INSITU=ON -DOGS_USE_CONAN=OFF \
-DParaView_DIR=~/path/to/paraview_install/lib/cmake/paraview-5.8 \
-DOGS_BUILD_PROCESSES=GroundwaterFlow ../ogs
```
**OR:** Build with [ogs-container-maker](https://github.com/ufz/ogs-container-maker):
```bash
python ogscm/cli.py --compiler_version 9 --ogs bilke/ogs@insitu-refactor --cmake_args ' -DOGS_BUILD_PROCESSES=GroundwaterFlow' --pm system --insitu -B -C -R
```
### Run benchmark
```
bin/ogs -o _out ../ogs/Tests/Data/Elliptic/cube_1x1x1_GroundWaterFlow/cube_1e1.prj
```
Open generated `cube_1e1_*.pvtp` in ParaView.
## How it works
See section `<insitu>` in `Elliptic/cube_1x1x1_GroundWaterFlow/cube_1e1.prj`. It defines Python scripts with visualization pipelines which are executed after every time step.
These python scripts can be generated with ParaView:
- Load a typical dataset representative for your expected simulation output
- Rename dataset in the *Pipeline Browser* to `input`
- Setup the filter pipeline
- In the menu click *Catalyst / Define Exports* which opens the *Export Inspector*
- In the *Export Inspector* under *Data Extracts*:
- Select the filter you want to write out
- Choose the data format, e.g. *XMLPPolyDataWriter*
- Click the check-box next to the data format drop-down
- In the menu click *Catalyst / Export Catalyst Script*
----
TODO:
- Test parallel benchmark
- Live connection not working: https://gitlab.kitware.com/paraview/paraview/issues/19613
- Check https://gitlab.kitware.com/paraview/paraview/tree/master/Examples/Catalyst/CxxMappedDataArrayExample for more syntax changes
......@@ -243,7 +243,6 @@ include(scripts/cmake/packaging/Pack.cmake)
# External projects
if(OGS_INSITU)
include("${PARAVIEW_USE_FILE}")
add_definitions(-DUSE_INSITU)
endif()
......
......@@ -79,9 +79,10 @@ int VtkMappedMeshSource::RequestData(vtkInformation* /*request*/,
// Points
this->Points->Reset();
vtkNew<VtkMeshNodalCoordinatesTemplate<double>> nodeCoords;
VtkMeshNodalCoordinatesTemplate<double>* nodeCoords =
VtkMeshNodalCoordinatesTemplate<double>::New();
nodeCoords->SetNodes(_mesh->getNodes());
this->Points->SetData(nodeCoords.GetPointer());
this->Points->SetData(nodeCoords);
output->SetPoints(this->Points.GetPointer());
// Cells
......
......@@ -16,55 +16,45 @@
#include <vtkMappedDataArray.h>
#include <vtkObjectFactory.h> // for vtkStandardNewMacro
#include <vtkVersion.h>
#if VTK_MAJOR_VERSION < 7 || VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION < 1
#include <vtkTypeTemplate.h> // For templated vtkObject API
#endif
namespace MeshLib
{
class Node;
class Node;
}
namespace MeshLib
{
template <class Scalar>
class VtkMeshNodalCoordinatesTemplate :
#if !(VTK_MAJOR_VERSION < 7 || VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION < 1)
public vtkMappedDataArray<Scalar>
#else
public vtkTypeTemplate<VtkMeshNodalCoordinatesTemplate<Scalar>,
vtkMappedDataArray<Scalar>>
#endif // vtk version
class VtkMeshNodalCoordinatesTemplate : public vtkMappedDataArray<Scalar>
{
public:
#if !(VTK_MAJOR_VERSION < 7 || VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION < 1)
vtkTemplateTypeMacro(VtkMeshNodalCoordinatesTemplate<Scalar>,
vtkMappedDataArray<Scalar>);
#else
vtkMappedDataArrayNewInstanceMacro(VtkMeshNodalCoordinatesTemplate<Scalar>);
#endif // vtk version
static VtkMeshNodalCoordinatesTemplate *New();
vtkAbstractTemplateTypeMacro(VtkMeshNodalCoordinatesTemplate<Scalar>,
vtkMappedDataArray<Scalar>)
vtkMappedDataArrayNewInstanceMacro(
VtkMeshNodalCoordinatesTemplate<
Scalar>) static VtkMeshNodalCoordinatesTemplate* New();
void PrintSelf(std::ostream& os, vtkIndent indent) override;
using ValueType = typename Superclass::ValueType;
/// Pass the nodes from OGS mesh
void SetNodes(std::vector<MeshLib::Node*> const & nodes);
void SetNodes(std::vector<MeshLib::Node*> const& nodes);
// Reimplemented virtuals -- see superclasses for descriptions
void Initialize() override;
void GetTuples(vtkIdList *ptIds, vtkAbstractArray *output) override;
void GetTuples(vtkIdType p1, vtkIdType p2, vtkAbstractArray *output) override;
void GetTuples(vtkIdList* ptIds, vtkAbstractArray* output) override;
void GetTuples(vtkIdType p1, vtkIdType p2,
vtkAbstractArray* output) override;
void Squeeze() override;
vtkArrayIterator *NewIterator() override;
vtkArrayIterator* NewIterator() override;
vtkIdType LookupValue(vtkVariant value) override;
void LookupValue(vtkVariant value, vtkIdList *ids) override;
void LookupValue(vtkVariant value, vtkIdList* ids) override;
vtkVariant GetVariantValue(vtkIdType idx) override;
void ClearLookup() override;
double* GetTuple(vtkIdType i) override;
void GetTuple(vtkIdType i, double *tuple) override;
void GetTuple(vtkIdType i, double* tuple) override;
vtkIdType LookupTypedValue(Scalar value) override;
void LookupTypedValue(Scalar value, vtkIdList *ids) override;
void LookupTypedValue(Scalar value, vtkIdList* ids) override;
Scalar& GetValueReference(vtkIdType idx) override;
// This container is read only -- this method does nothing but print a
......@@ -72,27 +62,29 @@ public:
int Allocate(vtkIdType sz, vtkIdType ext) override;
int Resize(vtkIdType numTuples) override;
void SetNumberOfTuples(vtkIdType number) override;
void SetTuple(vtkIdType i, vtkIdType j, vtkAbstractArray *source) override;
void SetTuple(vtkIdType i, const float *source) override;
void SetTuple(vtkIdType i, const double *source) override;
void InsertTuple(vtkIdType i, vtkIdType j, vtkAbstractArray *source) override;
void InsertTuple(vtkIdType i, const float *source) override;
void InsertTuple(vtkIdType i, const double *source) override;
void InsertTuples(vtkIdList *dstIds, vtkIdList *srcIds,
vtkAbstractArray *source) override;
void SetTuple(vtkIdType i, vtkIdType j, vtkAbstractArray* source) override;
void SetTuple(vtkIdType i, const float* source) override;
void SetTuple(vtkIdType i, const double* source) override;
void InsertTuple(vtkIdType i, vtkIdType j,
vtkAbstractArray* source) override;
void InsertTuple(vtkIdType i, const float* source) override;
void InsertTuple(vtkIdType i, const double* source) override;
void InsertTuples(vtkIdList* dstIds, vtkIdList* srcIds,
vtkAbstractArray* source) override;
void InsertTuples(vtkIdType /*unused*/, vtkIdType /*unused*/,
vtkIdType /*unused*/,
vtkAbstractArray* /*unused*/) override;
vtkIdType InsertNextTuple(vtkIdType j, vtkAbstractArray *source) override;
vtkIdType InsertNextTuple(const float *source) override;
vtkIdType InsertNextTuple(const double *source) override;
vtkIdType InsertNextTuple(vtkIdType j, vtkAbstractArray* source) override;
vtkIdType InsertNextTuple(const float* source) override;
vtkIdType InsertNextTuple(const double* source) override;
void InsertVariantValue(vtkIdType idx, vtkVariant value) override;
void DeepCopy(vtkAbstractArray *aa) override;
void DeepCopy(vtkDataArray *da) override;
void InterpolateTuple(vtkIdType i, vtkIdList *ptIndices,
vtkAbstractArray* source, double* weights) override;
void InterpolateTuple(vtkIdType i, vtkIdType id1, vtkAbstractArray *source1,
vtkIdType id2, vtkAbstractArray *source2, double t) override;
void DeepCopy(vtkAbstractArray* aa) override;
void DeepCopy(vtkDataArray* da) override;
void InterpolateTuple(vtkIdType i, vtkIdList* ptIndices,
vtkAbstractArray* source, double* weights) override;
void InterpolateTuple(vtkIdType i, vtkIdType id1, vtkAbstractArray* source1,
vtkIdType id2, vtkAbstractArray* source2,
double t) override;
void SetVariantValue(vtkIdType idx, vtkVariant value) override;
void RemoveTuple(vtkIdType id) override;
void RemoveFirstTuple() override;
......@@ -101,20 +93,12 @@ public:
vtkIdType InsertNextValue(Scalar v) override;
void InsertValue(vtkIdType idx, Scalar v) override;
#if !(VTK_MAJOR_VERSION < 7 || VTK_MAJOR_VERSION == 7 && VTK_MINOR_VERSION < 1)
Scalar& GetValueReference(vtkIdType idx) const;
Scalar GetValue(vtkIdType idx) const override;
void GetTypedTuple(vtkIdType tupleId, Scalar* t) const override;
void SetTypedTuple(vtkIdType i, const Scalar* t) override;
void InsertTypedTuple(vtkIdType i, const Scalar* t) override;
vtkIdType InsertNextTypedTuple(const Scalar* t) override;
#else
Scalar GetValue(vtkIdType idx) override;
void GetTupleValue(vtkIdType idx, Scalar* t) override;
void SetTupleValue(vtkIdType i, const Scalar* t) override;
void InsertTupleValue(vtkIdType i, const Scalar* t) override;
vtkIdType InsertNextTupleValue(const Scalar* t) override;
#endif // vtk version
VtkMeshNodalCoordinatesTemplate(const VtkMeshNodalCoordinatesTemplate&) =
delete;
......@@ -127,7 +111,7 @@ protected:
const std::vector<MeshLib::Node*>* _nodes{nullptr};
private:
vtkIdType Lookup(const Scalar &val, vtkIdType startIndex);
vtkIdType Lookup(const Scalar& val, vtkIdType startIndex);
double* TempDoubleArray{nullptr};
};
......
......@@ -38,5 +38,5 @@ if(OGS_USE_PYTHON)
endif()
if(OGS_INSITU)
target_link_libraries(ProcessLib InSituLib)
target_link_libraries(ProcessLib PRIVATE InSituLib)
endif()
......@@ -350,7 +350,7 @@ void Output::doOutput(Process const& process,
#ifdef USE_INSITU
// Note: last time step may be output twice: here and in
// doOutputLastTimestep() which throws a warning.
InSituLib::CoProcess(process.getMesh(), t, timestep, false);
InSituLib::CoProcess(process.getMesh(), t, timestep, false, _output_directory);
#endif
}
......@@ -365,7 +365,7 @@ void Output::doOutputLastTimestep(Process const& process,
doOutputAlways(process, process_id, timestep, t, x);
}
#ifdef USE_INSITU
InSituLib::CoProcess(process.getMesh(), t, timestep, true);
InSituLib::CoProcess(process.getMesh(), t, timestep, true, _output_directory);
#endif
}
......
......@@ -39,7 +39,7 @@ def CreateCoProcessor():
# 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='filename_%t.pvtp', freq=1)
coprocessor.RegisterWriter(parallelPolyDataWriter1, filename='cube_1e1_%t.pvtp', freq=1)
# ----------------------------------------------------------------
# finally, restore active source
......@@ -97,7 +97,7 @@ def DoCoProcessing(datadescription):
coprocessor.UpdateProducers(datadescription)
# Write output data, if appropriate.
coprocessor.WriteData(datadescription);
coprocessor.WriteData(datadescription)
# Write image capture (Last arg: rescale lookup table), if appropriate.
coprocessor.WriteImages(datadescription, rescale_lookuptable=False)
......
#--------------------------------------------------------------
# Global timestep output options
timeStepToStartOutputAt = 0
forceOutputAtFirstCall = False
# Global screenshot output options
imageFileNamePadding = 0
rescale_lookuptable = False
# Whether or not to request specific arrays from the adaptor.
requestSpecificArrays = False
# a root directory under which all Catalyst output goes
rootDirectory = ''
# makes a cinema D index table
make_cinema_table = False
#--------------------------------------------------------------
# Code generated from cpstate.py to create the CoProcessor.
# paraview version 5.8.0
#--------------------------------------------------------------
from paraview.simple import *
from paraview import coprocessing
# ----------------------- CoProcessor definition -----------------------
def CreateCoProcessor():
def _CreatePipeline(coprocessor, datadescription):
class Pipeline:
# state file generated using paraview version 5.8.0
# ----------------------------------------------------------------
# setup the data processing pipelines
# ----------------------------------------------------------------
# trace generated using paraview version 5.8.0
#
# To ensure correct image size when batch processing, please search
# for and uncomment the line `# renderView*.ViewSize = [*,*]`
#### disable automatic camera reset on 'Show'
paraview.simple._DisableFirstRenderCameraReset()
# create a new 'Superquadric'
ogs_output = coprocessor.CreateProducer(datadescription, 'input')
# create a new 'Clip'
clip1 = Clip(Input=ogs_output)
clip1.ClipType = 'Plane'
clip1.HyperTreeGridClipper = 'Plane'
clip1.Scalars = ['POINTS', 'D1_left_bottom_N1_right']
clip1.Value = 1.3376572416618493
# init the 'Plane' selected for 'ClipType'
clip1.ClipType.Origin = [0.5, 0.5, 0.0]
# init the 'Plane' selected for 'HyperTreeGridClipper'
clip1.HyperTreeGridClipper.Origin = [0.5, 0.5, 0.0]
# create a new 'Contour'
contour1 = Contour(Input=ogs_output)
contour1.ContourBy = ['POINTS', 'D1_left_bottom_N1_right']
contour1.Isosurfaces = [
1.0, 1.075034942591522, 1.1500698851830442, 1.2251048277745662,
1.3001397703660882, 1.3751747129576102, 1.4502096555491324,
1.5252445981406544, 1.6002795407321764, 1.6753144833236986
]
contour1.PointMergeMethod = 'Uniform Binning'
# ----------------------------------------------------------------
# finally, restore active source
SetActiveSource(clip1)
# ----------------------------------------------------------------
# Now any catalyst writers
xMLPUnstructuredGridWriter1 = servermanager.writers.XMLPUnstructuredGridWriter(
Input=clip1)
coprocessor.RegisterWriter(
xMLPUnstructuredGridWriter1,
filename='square_1e1_neumann_clip1_%t.pvtu',
freq=1,
paddingamount=0,
DataMode='Appended',
HeaderType='UInt64',
EncodeAppendedData=False,
CompressorType='None',
CompressionLevel='6')
xMLPPolyDataWriter1 = servermanager.writers.XMLPPolyDataWriter(
Input=contour1)
coprocessor.RegisterWriter(
xMLPPolyDataWriter1,
filename='square_1e1_neumann_contour1_%t.pvtp',
freq=1,
paddingamount=0,
DataMode='Appended',
HeaderType='UInt64',
EncodeAppendedData=False,
CompressorType='None',
CompressionLevel='6')
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.
freqs = {}
coprocessor.SetUpdateFrequencies(freqs)
coprocessor.SetInitialOutputOptions(timeStepToStartOutputAt,
forceOutputAtFirstCall)
if rootDirectory:
coprocessor.SetRootDirectory(rootDirectory)
if make_cinema_table:
coprocessor.EnableCinemaDTable()
return coprocessor
#--------------------------------------------------------------
# Global variable 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 and the update frequency
coprocessor.EnableLiveVisualization(False, 1)
# ---------------------- Data Selection method ----------------------
def RequestDataDescription(datadescription):
"Callback to populate the request for current timestep"
global coprocessor
# 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=rescale_lookuptable,
image_quality=0,
padding_amount=imageFileNamePadding)
# Live Visualization, if enabled.
coprocessor.DoLiveVisualization(datadescription, "localhost", 22222)
......@@ -130,4 +130,11 @@
</petsc>
</linear_solver>
</linear_solvers>
<insitu>
<scripts>
<script>
<name>square_1e1_neumann-insitu.py</name>
</script>
</scripts>
</insitu>
</OpenGeoSysProject>
Subproject commit cd0784969daf82732c5440206ca1b81a95084af0
Subproject commit 2741ecc8c4c92d73a1231ab53af0c14cae6b050e
{
"edition": "ogs",
"requires": [
"Extras"
],
"after": [
"Enable-Python",
"Extras"
],
"cmake":{
"cache":[
{ "name":"Module_vtkIOXML",
"type":"BOOL",
"value":"ON"
}
]
},
"modules":[
{ "name": "vtkIOXML",
"path": "VTK/IO/XML",
"include": [
{ "class": "vtkXMLUnstructuredGridReader" },
{ "class": "vtkXMLReader" },
{ "class": "vtkXMLUnstructuredDataReader" },
{ "class": "vtkXMLFileReadTester" },
{ "class": "vtkXMLDataReader" }
],
"cswrap": true,
"pythonwrap": true
}
]
}
# Generate OGS Catalyst edition
Clone ParaView: