summaryrefslogtreecommitdiff
path: root/var
diff options
context:
space:
mode:
Diffstat (limited to 'var')
-rw-r--r--var/spack/repos/builtin/packages/catalyst/package.py122
-rw-r--r--var/spack/repos/builtin/packages/catalyst/vtkm-catalyst-pv551.patch487
-rw-r--r--var/spack/repos/builtin/packages/of-catalyst/package.py48
-rw-r--r--var/spack/repos/builtin/packages/openfoam-com/package.py16
-rw-r--r--var/spack/repos/builtin/packages/paraview/package.py53
-rw-r--r--var/spack/repos/builtin/packages/paraview/vtkm-catalyst-pv551.patch510
6 files changed, 1176 insertions, 60 deletions
diff --git a/var/spack/repos/builtin/packages/catalyst/package.py b/var/spack/repos/builtin/packages/catalyst/package.py
index d9be7b17dd..03f9e4dbcf 100644
--- a/var/spack/repos/builtin/packages/catalyst/package.py
+++ b/var/spack/repos/builtin/packages/catalyst/package.py
@@ -7,6 +7,7 @@ from spack import *
import os
import subprocess
import llnl.util.tty as tty
+from spack.patch import absolute_path_for_package
class Catalyst(CMakePackage):
@@ -33,17 +34,31 @@ class Catalyst(CMakePackage):
variant('python', default=False, description='Enable Python support')
variant('essentials', default=False, description='Enable Essentials support')
variant('extras', default=False, description='Enable Extras support')
- variant('rendering', default=False, description='Enable Vtk Rendering support')
+ variant('rendering', default=False, description='Enable VTK Rendering support')
+ variant('osmesa', default=True, description='Use offscreen rendering')
- depends_on('git')
+ depends_on('git', type='build')
depends_on('mpi')
depends_on('python@2:2.8', when='+python', type=("build", "link", "run"))
depends_on('python', when='~python', type=("build"))
depends_on('mesa', when='+rendering')
- depends_on("libx11", when='+rendering')
- depends_on("libxt", when='+rendering')
+ depends_on('libx11', when='+rendering')
+ depends_on('libxt', when='+rendering')
depends_on('cmake@3.3:', type='build')
+ @when('@5.5.0:5.5.2')
+ def patch(self):
+ """Apply the patch (it should be fixed in Paraview 5.6)
+ at the package dir to the source code in
+ root_cmakelists_dir."""
+ patch_name = 'vtkm-catalyst-pv551.patch'
+ pkg_dir = os.path.dirname(absolute_path_for_package(self))
+ patch = which("patch", required=True)
+ with working_dir(self.root_cmakelists_dir):
+ patch('-s', '-p', '1', '-i',
+ join_path(pkg_dir, patch_name),
+ "-d", '.')
+
def url_for_version(self, version):
"""Handle ParaView version-based custom URLs."""
if version < Version('5.1.0'):
@@ -53,6 +68,30 @@ class Catalyst(CMakePackage):
else:
return self._urlfmt_xz.format(version.up_to(2), version, '')
+ @property
+ def paraview_subdir(self):
+ """The paraview subdirectory name as paraview-major.minor"""
+ return 'paraview-{0}'.format(self.spec.version.up_to(2))
+
+ @property
+ def editions(self):
+ """Transcribe spack variants into names of Catalyst Editions"""
+ selected = ['Base'] # Always required
+
+ if '+python' in self.spec:
+ selected.append('Enable-Python')
+
+ if '+essentials' in self.spec:
+ selected.append('Essentials')
+
+ if '+extras' in self.spec:
+ selected.append('Extras')
+
+ if '+rendering' in self.spec:
+ selected.append('Rendering-Base')
+
+ return selected
+
def do_stage(self, mirror_only=False):
"""Unpacks and expands the fetched tarball.
Then, generate the catalyst source files."""
@@ -62,38 +101,15 @@ class Catalyst(CMakePackage):
paraview_dir = os.path.join(self.stage.path,
'ParaView-v' + str(self.version))
catalyst_script = os.path.join(paraview_dir, 'Catalyst', 'catalyze.py')
+ editions_dir = os.path.join(paraview_dir, 'Catalyst', 'Editions')
catalyst_source_dir = os.path.abspath(self.root_cmakelists_dir)
command = ['python', catalyst_script,
- '-r', paraview_dir]
+ '-r', paraview_dir,
+ '-o', catalyst_source_dir]
- catalyst_edition = os.path.join(paraview_dir, 'Catalyst',
- 'Editions', 'Base')
- command.append('-i')
- command.append(catalyst_edition)
- if '+python' in self.spec:
- catalyst_edition = os.path.join(paraview_dir, 'Catalyst',
- 'Editions', 'Enable-Python')
- command.append('-i')
- command.append(catalyst_edition)
- if '+essentials' in self.spec:
- catalyst_edition = os.path.join(paraview_dir, 'Catalyst',
- 'Editions', 'Essentials')
- command.append('-i')
- command.append(catalyst_edition)
- if '+extras' in self.spec:
- catalyst_edition = os.path.join(paraview_dir, 'Catalyst',
- 'Editions', 'Extras')
- command.append('-i')
- command.append(catalyst_edition)
- if '+rendering' in self.spec:
- catalyst_edition = os.path.join(paraview_dir, 'Catalyst',
- 'Editions', 'Rendering-Base')
- command.append('-i')
- command.append(catalyst_edition)
-
- command.append('-o')
- command.append(catalyst_source_dir)
+ for edition in self.editions:
+ command.extend(['-i', os.path.join(editions_dir, edition)])
if not os.path.isdir(catalyst_source_dir):
os.mkdir(catalyst_source_dir)
@@ -104,15 +120,29 @@ class Catalyst(CMakePackage):
self.stage.path))
def setup_environment(self, spack_env, run_env):
+ # paraview 5.5 and later
+ # - cmake under lib/cmake/paraview-5.5
+ # - libs under lib
+ # - python bits under lib/python2.8/site-packages
if os.path.isdir(self.prefix.lib64):
lib_dir = self.prefix.lib64
else:
lib_dir = self.prefix.lib
- paraview_version = 'paraview-%s' % self.spec.version.up_to(2)
- run_env.prepend_path('LIBRARY_PATH', join_path(lib_dir,
- paraview_version))
- run_env.prepend_path('LD_LIBRARY_PATH', join_path(lib_dir,
- paraview_version))
+
+ if self.spec.version <= Version('5.4.1'):
+ lib_dir = join_path(lib_dir, paraview_subdir)
+ run_env.set('ParaView_DIR', self.prefix)
+ run_env.prepend_path('LIBRARY_PATH', lib_dir)
+ run_env.prepend_path('LD_LIBRARY_PATH', lib_dir)
+
+ if '+python' in self.spec:
+ python_version = self.spec['python'].version.up_to(2)
+ run_env.prepend_path('PYTHONPATH', join_path(lib_dir,
+ 'python{0}'.format(python_version),
+ 'site-packages'))
+
+ def setup_dependent_environment(self, spack_env, run_env, dependent_spec):
+ spack_env.set('ParaView_DIR', self.prefix)
@property
def root_cmakelists_dir(self):
@@ -136,8 +166,24 @@ class Catalyst(CMakePackage):
def cmake_args(self):
"""Populate cmake arguments for Catalyst."""
+ spec = self.spec
+
+ def variant_bool(feature, on='ON', off='OFF'):
+ """Ternary for spec variant to ON/OFF string"""
+ if feature in spec:
+ return on
+ return off
+
+ def nvariant_bool(feature):
+ """Negated ternary for spec variant to OFF/ON string"""
+ return variant_bool(feature, on='OFF', off='ON')
+
cmake_args = [
- '-DPARAVIEW_GIT_DESCRIBE=v%s' % str(self.version)
+ '-DPARAVIEW_GIT_DESCRIBE=v%s' % str(self.version),
+ '-DVTK_USE_SYSTEM_EXPAT:BOOL=ON',
+ '-DVTK_USE_X:BOOL=%s' % nvariant_bool('+osmesa'),
+ '-DVTK_USE_OFFSCREEN:BOOL=%s' % variant_bool('+osmesa'),
+ '-DVTK_OPENGL_HAS_OSMESA:BOOL=%s' % variant_bool('+osmesa')
]
return cmake_args
diff --git a/var/spack/repos/builtin/packages/catalyst/vtkm-catalyst-pv551.patch b/var/spack/repos/builtin/packages/catalyst/vtkm-catalyst-pv551.patch
new file mode 100644
index 0000000000..fa610722af
--- /dev/null
+++ b/var/spack/repos/builtin/packages/catalyst/vtkm-catalyst-pv551.patch
@@ -0,0 +1,487 @@
+# The catalyst changes (the working directory for output) are slated for
+# paraview-5.6.
+# They are API-compatible with paraview-5.5 but not ABI compatible.
+#
+# https://gitlab.kitware.com/paraview/paraview/merge_requests/2433
+# https://gitlab.kitware.com/paraview/paraview/merge_requests/2436
+#
+--- Catalyst-v5.5.0/CoProcessing/Catalyst/vtkCPProcessor.cxx.orig 2018-04-06 22:03:33.000000000 +0200
++++ Catalyst-v5.5.0/CoProcessing/Catalyst/vtkCPProcessor.cxx 2018-05-11 12:02:26.894772713 +0200
+@@ -38,6 +38,7 @@
+ #include "vtkStringArray.h"
+
+ #include <list>
++#include <vtksys/SystemTools.hxx>
+
+ struct vtkCPProcessorInternals
+ {
+@@ -47,12 +48,13 @@
+ };
+
+ vtkStandardNewMacro(vtkCPProcessor);
+-vtkMultiProcessController* vtkCPProcessor::Controller = NULL;
++vtkMultiProcessController* vtkCPProcessor::Controller = nullptr;
+ //----------------------------------------------------------------------------
+ vtkCPProcessor::vtkCPProcessor()
+ {
+ this->Internal = new vtkCPProcessorInternals;
+- this->InitializationHelper = NULL;
++ this->InitializationHelper = nullptr;
++ this->WorkingDirectory = nullptr;
+ }
+
+ //----------------------------------------------------------------------------
+@@ -61,14 +63,15 @@
+ if (this->Internal)
+ {
+ delete this->Internal;
+- this->Internal = NULL;
++ this->Internal = nullptr;
+ }
+
+ if (this->InitializationHelper)
+ {
+ this->InitializationHelper->Delete();
+- this->InitializationHelper = NULL;
++ this->InitializationHelper = nullptr;
+ }
++ this->SetWorkingDirectory(nullptr);
+ }
+
+ //----------------------------------------------------------------------------
+@@ -95,7 +98,7 @@
+ {
+ if (which < 0 || which >= this->GetNumberOfPipelines())
+ {
+- return NULL;
++ return nullptr;
+ }
+ int counter = 0;
+ vtkCPProcessorInternals::PipelineListIterator iter = this->Internal->Pipelines.begin();
+@@ -108,7 +111,7 @@
+ counter++;
+ iter++;
+ }
+- return NULL;
++ return nullptr;
+ }
+
+ //----------------------------------------------------------------------------
+@@ -130,17 +133,41 @@
+ }
+
+ //----------------------------------------------------------------------------
+-int vtkCPProcessor::Initialize()
++int vtkCPProcessor::Initialize(const char* workingDirectory)
+ {
+- if (this->InitializationHelper == NULL)
++ if (this->InitializationHelper == nullptr)
+ {
+ this->InitializationHelper = this->NewInitializationHelper();
+ }
++ // make sure the directory exists here so that we only do it once
++ if (workingDirectory)
++ {
++ vtkMultiProcessController* controller = vtkMultiProcessController::GetGlobalController();
++ int success = 1;
++ if (controller == nullptr || controller->GetLocalProcessId() == 0)
++ {
++ success = vtksys::SystemTools::MakeDirectory(workingDirectory) == true ? 1 : 0;
++ if (success == 0)
++ {
++ vtkWarningMacro("Could not make "
++ << workingDirectory << " directory. "
++ << "Results will be generated in current working directory instead.");
++ }
++ }
++ if (controller)
++ {
++ controller->Broadcast(&success, 1, 0);
++ }
++ if (success)
++ {
++ this->SetWorkingDirectory(workingDirectory);
++ }
++ }
+ return 1;
+ }
+
+ //----------------------------------------------------------------------------
+-int vtkCPProcessor::Initialize(vtkMPICommunicatorOpaqueComm& comm)
++int vtkCPProcessor::Initialize(vtkMPICommunicatorOpaqueComm& comm, const char* workingDirectory)
+ {
+ #ifdef PARAVIEW_USE_MPI
+ if (vtkCPProcessor::Controller)
+@@ -148,7 +175,7 @@
+ vtkErrorMacro("Can only initialize with a communicator once per process.");
+ return 0;
+ }
+- if (this->InitializationHelper == NULL)
++ if (this->InitializationHelper == nullptr)
+ {
+ vtkMPICommunicator* communicator = vtkMPICommunicator::New();
+ communicator->InitializeExternal(&comm);
+@@ -157,12 +184,12 @@
+ this->Controller = controller;
+ this->Controller->SetGlobalController(controller);
+ communicator->Delete();
+- return this->Initialize();
++ return this->Initialize(workingDirectory);
+ }
+ return 1;
+ #else
+ static_cast<void>(&comm); // get rid of variable not used warning
+- return this->Initialize();
++ return this->Initialize(workingDirectory);
+ #endif
+ }
+
+@@ -225,6 +252,13 @@
+ input->GetFieldData()->AddArray(catalystChannel);
+ }
+ }
++
++ std::string originalWorkingDirectory;
++ if (this->WorkingDirectory)
++ {
++ originalWorkingDirectory = vtksys::SystemTools::GetCurrentWorkingDirectory();
++ vtksys::SystemTools::ChangeDirectory(this->WorkingDirectory);
++ }
+ for (vtkCPProcessorInternals::PipelineListIterator iter = this->Internal->Pipelines.begin();
+ iter != this->Internal->Pipelines.end(); iter++)
+ {
+@@ -248,6 +282,10 @@
+ }
+ }
+ }
++ if (originalWorkingDirectory.empty() == false)
++ {
++ vtksys::SystemTools::ChangeDirectory(originalWorkingDirectory);
++ }
+ // we want to reset everything here to make sure that new information
+ // is properly passed in the next time.
+ dataDescription->ResetAll();
+@@ -259,7 +297,7 @@
+ {
+ if (this->Controller)
+ {
+- this->Controller->SetGlobalController(NULL);
++ this->Controller->SetGlobalController(nullptr);
+ this->Controller->Finalize(1);
+ this->Controller->Delete();
+ }
+--- Catalyst-v5.5.0/CoProcessing/Catalyst/vtkCPProcessor.h.orig 2018-04-06 22:03:33.000000000 +0200
++++ Catalyst-v5.5.0/CoProcessing/Catalyst/vtkCPProcessor.h 2018-05-11 12:02:26.894772713 +0200
+@@ -76,14 +76,16 @@
+ virtual void RemoveAllPipelines();
+
+ /// Initialize the co-processor. Returns 1 if successful and 0
+- /// otherwise.
+ /// otherwise. If Catalyst is built with MPI then Initialize()
+ /// can also be called with a specific MPI communicator if
+ /// MPI_COMM_WORLD isn't the proper one. Catalyst is initialized
+- /// to use MPI_COMM_WORLD by default.
+- virtual int Initialize();
++ /// to use MPI_COMM_WORLD by default. Both methods have an optional
++ /// workingDirectory argument which will set *WorkingDirectory* so
++ /// that files will be put relative to this directory.
++ virtual int Initialize(const char* workingDirectory = nullptr);
+ #ifndef __WRAP__
+- virtual int Initialize(vtkMPICommunicatorOpaqueComm& comm);
++ virtual int Initialize(
++ vtkMPICommunicatorOpaqueComm& comm, const char* workingDirectory = nullptr);
+ #endif
+
+ /// The Catalyst input field data string array name. This array will
+@@ -111,6 +113,13 @@
+ /// implementation an opportunity to clean up, before it is destroyed.
+ virtual int Finalize();
+
++ /// Get the current working directory for outputting Catalyst files.
++ /// If not set then Catalyst output files will be relative to the
++ /// current working directory. This will not affect where Catalyst
++ /// looks for Python scripts. *WorkingDirectory* gets set through
++ /// the *Initialize()* methods.
++ vtkGetStringMacro(WorkingDirectory);
++
+ protected:
+ vtkCPProcessor();
+ virtual ~vtkCPProcessor();
+@@ -118,6 +127,11 @@
+ /// Create a new instance of the InitializationHelper.
+ virtual vtkObject* NewInitializationHelper();
+
++ /// Set the current working directory for outputting Catalyst files.
++ /// This is a protected method since simulation code adaptors should
++ /// set this through the *Initialize()* methods.
++ vtkSetStringMacro(WorkingDirectory);
++
+ private:
+ vtkCPProcessor(const vtkCPProcessor&) = delete;
+ void operator=(const vtkCPProcessor&) = delete;
+@@ -125,6 +139,7 @@
+ vtkCPProcessorInternals* Internal;
+ vtkObject* InitializationHelper;
+ static vtkMultiProcessController* Controller;
++ char* WorkingDirectory;
+ };
+
+ #endif
+--- Catalyst-v5.5.0/CoProcessing/Catalyst/vtkCPXMLPWriterPipeline.cxx.orig 2018-04-06 22:03:33.000000000 +0200
++++ Catalyst-v5.5.0/CoProcessing/Catalyst/vtkCPXMLPWriterPipeline.cxx 2018-05-11 12:02:26.894772713 +0200
+@@ -31,6 +31,7 @@
+ #include <vtkSmartPointer.h>
+ #include <vtkUnstructuredGrid.h>
+
++#include <algorithm>
+ #include <sstream>
+ #include <string>
+
+@@ -174,7 +175,7 @@
+
+ for (unsigned int i = 0; i < dataDescription->GetNumberOfInputDescriptions(); i++)
+ {
+- const char* inputName = dataDescription->GetInputDescriptionName(i);
++ std::string inputName = dataDescription->GetInputDescriptionName(i);
+ vtkCPInputDataDescription* idd = dataDescription->GetInputDescription(i);
+ vtkDataObject* grid = idd->GetGrid();
+ if (grid == nullptr)
+@@ -206,6 +207,8 @@
+ vtkSMStringVectorProperty* fileName =
+ vtkSMStringVectorProperty::SafeDownCast(writer->GetProperty("FileName"));
+
++ // If we have a / in the channel name we take it out of the filename we're going to write to
++ inputName.erase(std::remove(inputName.begin(), inputName.end(), '/'), inputName.end());
+ std::ostringstream o;
+ if (this->Path.empty() == false)
+ {
+--- Catalyst-v5.5.0/Wrapping/Python/paraview/coprocessing.py.orig 2018-04-06 22:03:33.000000000 +0200
++++ Catalyst-v5.5.0/Wrapping/Python/paraview/coprocessing.py 2018-05-11 12:02:27.038772408 +0200
+@@ -11,22 +11,12 @@
+ from paraview.vtk.vtkPVVTKExtensionsCore import *
+ import math
+
+-# -----------------------------------------------------------------------------
+-def IsInModulo(timestep, frequencyArray):
+- """
+- Return True if the given timestep is in one of the provided frequency.
+- This can be interpreted as follow::
+-
+- isFM = IsInModulo(timestep, [2,3,7])
+-
+- is similar to::
++# If the user created a filename in a location that doesn't exist by default we'll
++# make the directory for them. This can be changed though by setting createDirectoriesIfNeeded
++# to False.
++createDirectoriesIfNeeded = True
+
+- isFM = (timestep % 2 == 0) or (timestep % 3 == 0) or (timestep % 7 == 0)
+- """
+- for frequency in frequencyArray:
+- if frequency > 0 and (timestep % frequency == 0):
+- return True
+- return False
++# -----------------------------------------------------------------------------
+
+ class CoProcessor(object):
+ """Base class for co-processing Pipelines.
+@@ -68,6 +58,9 @@
+ self.__CinemaTracks = {}
+ self.__InitialFrequencies = {}
+ self.__PrintEnsightFormatString = False
++ self.__TimeStepToStartOutputAt=0
++ self.__ForceOutputAtFirstCall=False
++ self.__FirstTimeStepIndex = None
+
+ def SetPrintEnsightFormatString(self, enable):
+ """If outputting ExodusII files with the purpose of reading them into
+@@ -87,6 +80,17 @@
+ "Incorrect argument type: %s, must be a dict" % type(frequencies))
+ self.__InitialFrequencies = frequencies
+
++ def SetInitialOutputOptions(self, timeStepToStartOutputAt, forceOutputAtFirstCall):
++ """Set the frequencies at which the pipeline needs to be updated.
++ Typically, this is called by the subclass once it has determined what
++ timesteps co-processing will be needed to be done.
++ frequencies is a map, with key->string name of for the simulation
++ input, and value is a list of frequencies.
++ """
++
++ self.__TimeStepToStartOutputAt=timeStepToStartOutputAt
++ self.__ForceOutputAtFirstCall=forceOutputAtFirstCall
++
+ def EnableLiveVisualization(self, enable, frequency = 1):
+ """Call this method to enable live-visualization. When enabled,
+ DoLiveVisualization() will communicate with ParaView server if possible
+@@ -115,7 +119,7 @@
+ # if this is a time step to do live then all of the inputs
+ # must be made available. note that we want the pipeline built
+ # before we do the actual first live connection.
+- if self.__EnableLiveVisualization and timestep % self.__LiveVisualizationFrequency == 0 \
++ if self.__EnableLiveVisualization and self.NeedToOutput(timestep, self.__LiveVisualizationFrequency) \
+ and self.__LiveVisualizationLink:
+ if self.__LiveVisualizationLink.Initialize(servermanager.ActiveConnection.Session.GetSessionProxyManager()):
+ num_inputs = datadescription.GetNumberOfInputDescriptions()
+@@ -132,13 +136,13 @@
+ # hasn't been set up yet). If we don't have live enabled
+ # we know that the output frequencies aren't changed and can
+ # just use the initial frequencies.
+- if self.__InitialFrequencies or not self.__EnableLiveVisualization:
++ if self.__ForceOutputAtFirstCall or self.__InitialFrequencies or not self.__EnableLiveVisualization:
+ num_inputs = datadescription.GetNumberOfInputDescriptions()
+ for cc in range(num_inputs):
+ input_name = datadescription.GetInputDescriptionName(cc)
+
+ freqs = self.__InitialFrequencies.get(input_name, [])
+- if self.__EnableLiveVisualization or ( self and IsInModulo(timestep, freqs) ):
++ if self.__EnableLiveVisualization or ( self and self.IsInModulo(timestep, freqs) ):
+ datadescription.GetInputDescription(cc).AllFieldsOn()
+ datadescription.GetInputDescription(cc).GenerateMeshOn()
+ else:
+@@ -149,15 +153,14 @@
+ for writer in self.__WritersList:
+ frequency = writer.parameters.GetProperty(
+ "WriteFrequency").GetElement(0)
+- if (timestep % frequency) == 0 or \
+- datadescription.GetForceOutput() == True:
++ if self.NeedToOutput(timestep, frequency) or datadescription.GetForceOutput() == True:
+ writerinputs = cpstate.locate_simulation_inputs(writer)
+ for writerinput in writerinputs:
+ datadescription.GetInputDescriptionByName(writerinput).AllFieldsOn()
+ datadescription.GetInputDescriptionByName(writerinput).GenerateMeshOn()
+
+ for view in self.__ViewsList:
+- if (view.cpFrequency and timestep % view.cpFrequency == 0) or \
++ if (view.cpFrequency and self.NeedToOutput(timestep, view.cpFrequency)) or \
+ datadescription.GetForceOutput() == True:
+ viewinputs = cpstate.locate_simulation_inputs_for_view(view)
+ for viewinput in viewinputs:
+@@ -192,8 +195,7 @@
+ for writer in self.__WritersList:
+ frequency = writer.parameters.GetProperty(
+ "WriteFrequency").GetElement(0)
+- if (timestep % frequency) == 0 or \
+- datadescription.GetForceOutput() == True:
++ if self.NeedToOutput(timestep, frequency) or datadescription.GetForceOutput() == True:
+ fileName = writer.parameters.GetProperty("FileName").GetElement(0)
+ paddingamount = writer.parameters.GetProperty("PaddingAmount").GetElement(0)
+ helperName = writer.GetXMLName()
+@@ -203,6 +205,23 @@
+ else:
+ ts = str(timestep).rjust(paddingamount, '0')
+ writer.FileName = fileName.replace("%t", ts)
++ if '/' in writer.FileName and createDirectoriesIfNeeded:
++ oktowrite = [1.]
++ import vtk
++ comm = vtk.vtkMultiProcessController.GetGlobalController()
++ if comm.GetLocalProcessId() == 0:
++ import os
++ newDir = writer.FileName[0:writer.FileName.rfind('/')]
++ try:
++ os.makedirs(newDir)
++ except OSError:
++ if not os.path.isdir(newDir):
++ print ("ERROR: Cannot make directory for", writer.FileName, ". No data will be written.")
++ oktowrite[0] = 0.
++ comm.Broadcast(oktowrite, 1, 0)
++ if oktowrite[0] == 0:
++ # we can't make the directory so no reason to update the pipeline
++ return
+ writer.UpdatePipeline(datadescription.GetTime())
+
+ def WriteImages(self, datadescription, rescale_lookuptable=False,
+@@ -240,7 +259,7 @@
+
+ cinema_dirs = []
+ for view in self.__ViewsList:
+- if (view.cpFrequency and timestep % view.cpFrequency == 0) or \
++ if (view.cpFrequency and self.NeedToOutput(timestep, view.cpFrequency)) or \
+ datadescription.GetForceOutput() == True:
+ fname = view.cpFileName
+ ts = str(timestep).rjust(padding_amount, '0')
+@@ -267,6 +286,24 @@
+ if dirname:
+ cinema_dirs.append(dirname)
+ else:
++ if '/' in fname and createDirectoriesIfNeeded:
++ oktowrite = [1.]
++ import vtk
++ comm = vtk.vtkMultiProcessController.GetGlobalController()
++ if comm.GetLocalProcessId() == 0:
++ import os
++ newDir = fname[0:fname.rfind('/')]
++ try:
++ os.makedirs(newDir)
++ except OSError:
++ if not os.path.isdir(newDir):
++ print ("ERROR: Cannot make directory for", fname, ". No image will be output.")
++ oktowrite[0] = 0.
++ comm.Broadcast(oktowrite, 1, 0)
++ if oktowrite[0] == 0:
++ # we can't make the directory so no reason to update the pipeline
++ return
++
+ if image_quality is None and fname.endswith('png'):
+ # for png quality = 0 means no compression. compression can be a potentially
+ # very costly serial operation on process 0
+@@ -307,7 +344,7 @@
+
+
+ timeStep = datadescription.GetTimeStep()
+- if self.__EnableLiveVisualization and timeStep % self.__LiveVisualizationFrequency == 0:
++ if self.__EnableLiveVisualization and self.NeedToOutput(timeStep, self.__LiveVisualizationFrequency):
+ if not self.__LiveVisualizationLink.Initialize(servermanager.ActiveConnection.Session.GetSessionProxyManager()):
+ return
+
+@@ -412,7 +449,7 @@
+ """
+ controller = servermanager.ParaViewPipelineController()
+ # assume that a client only proxy with the same name as a writer
+- # is available in "insitu_writer_paramters"
++ # is available in "insitu_writer_parameters"
+
+ # Since coprocessor sometimes pass writer as a custom object and not
+ # a proxy, we need to handle that. Just creating any arbitrary writer
+@@ -666,3 +703,42 @@
+ #restore what we showed
+ pv_introspect.restore_visibility(pxystate)
+ return os.path.basename(vfname)
++
++ def IsInModulo(self, timestep, frequencies):
++ """
++ Return True if the given timestep is in one of the provided frequency.
++ This can be interpreted as follow::
++
++ isFM = IsInModulo(timestep-timeStepToStartOutputAt, [2,3,7])
++
++ is similar to::
++
++ isFM = (timestep-timeStepToStartOutputAt % 2 == 0) or (timestep-timeStepToStartOutputAt % 3 == 0) or (timestep-timeStepToStartOutputAt % 7 == 0)
++
++ The timeStepToStartOutputAt is the first timestep that will potentially be output.
++ """
++ if timestep < self.__TimeStepToStartOutputAt and not self.__ForceOutputAtFirstCall:
++ return False
++ for frequency in frequencies:
++ if frequency > 0 and self.NeedToOutput(timestep, frequency):
++ return True
++
++ return False
++
++
++ def NeedToOutput(self, timestep, frequency):
++ """
++ Return True if we need to output based on the input timestep and frequency. Checks based
++ __FirstTimeStepIndex, __FirstTimeStepIndex, __ForceOutputAtFirstCall and __TimeStepToStartOutputAt
++ member variables.
++ """
++ if self.__FirstTimeStepIndex == None:
++ self.__FirstTimeStepIndex = timestep
++
++ if self.__ForceOutputAtFirstCall and self.__FirstTimeStepIndex == timestep:
++ return True
++
++ if self.__TimeStepToStartOutputAt <= timestep and (timestep-self.__TimeStepToStartOutputAt) % frequency == 0:
++ return True
++
++ return False
diff --git a/var/spack/repos/builtin/packages/of-catalyst/package.py b/var/spack/repos/builtin/packages/of-catalyst/package.py
new file mode 100644
index 0000000000..ac1caf654e
--- /dev/null
+++ b/var/spack/repos/builtin/packages/of-catalyst/package.py
@@ -0,0 +1,48 @@
+# Copyright 2013-2018 Lawrence Livermore National Security, LLC and other
+# Spack Project Developers. See the top-level COPYRIGHT file for details.
+#
+# SPDX-License-Identifier: (Apache-2.0 OR MIT)
+
+from spack import *
+
+
+class OfCatalyst(CMakePackage):
+ """Of-catalyst is a library for OpenFOAM that provides a runtime-selectable
+ function object for embedding ParaView Catalyst in-situ visualization
+ into arbitrary OpenFOAM simulations.
+ Supports in-situ conversion of the following types:
+ 1) finite volume meshes and fields, single or multi-region;
+ 2) finite area meshes and fields, single region;
+ 3) lagrangian (clouds), single or multiple clouds.
+ This offering is part of the community repository supported by OpenCFD Ltd,
+ producer and distributor of the OpenFOAM software via www.openfoam.com,
+ and owner of the OPENFOAM trademark.
+ OpenCFD Ltd has been developing and releasing OpenFOAM since its debut
+ in 2004.
+ """
+
+ # Currently only via git
+ homepage = "https://develop.openfoam.com/Community/catalyst"
+ git = "https://develop.openfoam.com/Community/catalyst.git"
+
+ version('develop', branch='develop')
+ version('1806', tag='v1806')
+
+ variant('full', default=False, description='Build against paraview (full) or catalyst (light)')
+
+ depends_on('openfoam-com@1806', when='@1806', type=('build', 'link', 'run'))
+ depends_on('openfoam-com@develop', when='@develop', type=('build', 'link', 'run'))
+ depends_on('catalyst@5.5:', when='~full')
+ depends_on('paraview@5.5:+osmesa~qt', when='+full')
+
+ root_cmakelists_dir = 'src/catalyst'
+
+ def cmake_args(self):
+ """Populate cmake arguments for ParaView."""
+ cmake_args = [
+ '-DCMAKE_LIBRARY_OUTPUT_DIRECTORY:PATH=%s' % join_path(
+ self.stage.source_path,
+ 'spack-build')
+ ]
+
+ return cmake_args
diff --git a/var/spack/repos/builtin/packages/openfoam-com/package.py b/var/spack/repos/builtin/packages/openfoam-com/package.py
index 33b9daf405..1cd5722003 100644
--- a/var/spack/repos/builtin/packages/openfoam-com/package.py
+++ b/var/spack/repos/builtin/packages/openfoam-com/package.py
@@ -405,16 +405,16 @@ class OpenfoamCom(Package):
# Unneeded bits
# -------------
- 'FOAM_SETTINGS', # Do not use with modules
- 'FOAM_INST_DIR', # Old
- 'FOAM_(APP|ETC|SRC|SOLVERS|UTILITIES)',
+ # 'FOAM_SETTINGS', # Do not use with modules
+ # 'FOAM_INST_DIR', # Old
+ # 'FOAM_(APP|ETC|SRC|SOLVERS|UTILITIES)',
# 'FOAM_TUTORIALS', # can be useful
- 'WM_OSTYPE', # Purely optional value
+ # 'WM_OSTYPE', # Purely optional value
# Third-party cruft - only used for orig compilation
# -----------------
'[A-Z].*_ARCH_PATH',
- '(KAHIP|METIS|SCOTCH)_VERSION',
+ # '(KAHIP|METIS|SCOTCH)_VERSION',
# User-specific
# -------------
@@ -426,6 +426,7 @@ class OpenfoamCom(Package):
])
run_env.extend(mods)
+ spack_env.extend(mods)
minimal = False
tty.info('OpenFOAM bashrc env: {0}'.format(bashrc))
except Exception:
@@ -436,8 +437,11 @@ class OpenfoamCom(Package):
tty.info('OpenFOAM minimal env {0}'.format(self.prefix))
run_env.set('FOAM_PROJECT_DIR', self.projectdir)
run_env.set('WM_PROJECT_DIR', self.projectdir)
+ spack_env.set('FOAM_PROJECT_DIR', self.projectdir)
+ spack_env.set('WM_PROJECT_DIR', self.projectdir)
for d in ['wmake', self.archbin]: # bin added automatically
run_env.prepend_path('PATH', join_path(self.projectdir, d))
+ spack_env.prepend_path('PATH', join_path(self.projectdir, d))
def setup_dependent_environment(self, spack_env, run_env, dependent_spec):
"""Location of the OpenFOAM project directory.
@@ -445,7 +449,7 @@ class OpenfoamCom(Package):
variable since it would mask the normal OpenFOAM cleanup of
previous versions.
"""
- spack_env.set('FOAM_PROJECT_DIR', self.projectdir)
+ self.setup_environment(spack_env, run_env)
@property
def projectdir(self):
diff --git a/var/spack/repos/builtin/packages/paraview/package.py b/var/spack/repos/builtin/packages/paraview/package.py
index e1c36a063f..aca00f3cde 100644
--- a/var/spack/repos/builtin/packages/paraview/package.py
+++ b/var/spack/repos/builtin/packages/paraview/package.py
@@ -59,7 +59,8 @@ class Paraview(CMakePackage):
depends_on('libpng')
depends_on('libtiff')
depends_on('libxml2')
- # depends_on('netcdf')
+ depends_on('netcdf')
+ depends_on('expat')
# depends_on('netcdf-cxx')
# depends_on('protobuf') # version mismatches?
# depends_on('sqlite') # external version not supported
@@ -74,6 +75,9 @@ class Paraview(CMakePackage):
# Broken installation (ui_pqExportStateWizard.h) - fixed in 5.2.0
patch('ui_pqExportStateWizard.patch', when='@:5.1.2')
+ # Broken vtk-m config. Upstream catalyst changes
+ patch('vtkm-catalyst-pv551.patch', when='@5.5.0:5.5.2')
+
def url_for_version(self, version):
"""Handle ParaView version-based custom URLs."""
if version < Version('5.1.0'):
@@ -81,34 +85,50 @@ class Paraview(CMakePackage):
else:
return self._urlfmt.format(version.up_to(2), version, '')
+ @property
+ def paraview_subdir(self):
+ """The paraview subdirectory name as paraview-major.minor"""
+ return 'paraview-{0}'.format(self.spec.version.up_to(2))
+
def setup_dependent_environment(self, spack_env, run_env, dependent_spec):
if os.path.isdir(self.prefix.lib64):
lib_dir = self.prefix.lib64
else:
lib_dir = self.prefix.lib
- paraview_version = 'paraview-%s' % self.spec.version.up_to(2)
+ spack_env.set('ParaView_DIR', self.prefix)
spack_env.set('PARAVIEW_VTK_DIR',
- join_path(lib_dir, 'cmake', paraview_version))
+ join_path(lib_dir, 'cmake', self.paraview_subdir))
def setup_environment(self, spack_env, run_env):
+ # paraview 5.5 and later
+ # - cmake under lib/cmake/paraview-5.5
+ # - libs under lib
+ # - python bits under lib/python2.8/site-packages
if os.path.isdir(self.prefix.lib64):
lib_dir = self.prefix.lib64
else:
lib_dir = self.prefix.lib
- paraview_version = 'paraview-%s' % self.spec.version.up_to(2)
- run_env.prepend_path('LIBRARY_PATH', join_path(lib_dir,
- paraview_version))
- run_env.prepend_path('LD_LIBRARY_PATH', join_path(lib_dir,
- paraview_version))
+
+ run_env.set('ParaView_DIR', self.prefix)
run_env.set('PARAVIEW_VTK_DIR',
- join_path(lib_dir, 'cmake', paraview_version))
+ join_path(lib_dir, 'cmake', self.paraview_subdir))
+
+ if self.spec.version <= Version('5.4.1'):
+ lib_dir = join_path(lib_dir, self.paraview_subdir)
+
+ run_env.prepend_path('LIBRARY_PATH', lib_dir)
+ run_env.prepend_path('LD_LIBRARY_PATH', lib_dir)
+
if '+python' in self.spec:
- run_env.prepend_path('PYTHONPATH', join_path(lib_dir,
- paraview_version))
- run_env.prepend_path('PYTHONPATH', join_path(lib_dir,
- paraview_version, 'site-packages'))
- run_env.prepend_path('PYTHONPATH', join_path(lib_dir,
- paraview_version, 'site-packages', 'vtk'))
+ if self.spec.version <= Version('5.4.1'):
+ pv_pydir = join_path(lib_dir, 'site-packages')
+ run_env.prepend_path('PYTHONPATH', pv_pydir)
+ run_env.prepend_path('PYTHONPATH', join_path(pv_pydir, 'vtk'))
+ else:
+ python_version = self.spec['python'].version.up_to(2)
+ run_env.prepend_path('PYTHONPATH', join_path(lib_dir,
+ 'python{0}'.format(python_version),
+ 'site-packages'))
def cmake_args(self):
"""Populate cmake arguments for ParaView."""
@@ -139,7 +159,8 @@ class Paraview(CMakePackage):
'-DVTK_USE_SYSTEM_HDF5:BOOL=%s' % variant_bool('+hdf5'),
'-DVTK_USE_SYSTEM_JPEG:BOOL=ON',
'-DVTK_USE_SYSTEM_LIBXML2:BOOL=ON',
- '-DVTK_USE_SYSTEM_NETCDF:BOOL=OFF',
+ '-DVTK_USE_SYSTEM_NETCDF:BOOL=ON',
+ '-DVTK_USE_SYSTEM_EXPAT:BOOL=ON',
'-DVTK_USE_SYSTEM_TIFF:BOOL=ON',
'-DVTK_USE_SYSTEM_ZLIB:BOOL=ON',
]
diff --git a/var/spack/repos/builtin/packages/paraview/vtkm-catalyst-pv551.patch b/var/spack/repos/builtin/packages/paraview/vtkm-catalyst-pv551.patch
new file mode 100644
index 0000000000..8c8883740d
--- /dev/null
+++ b/var/spack/repos/builtin/packages/paraview/vtkm-catalyst-pv551.patch
@@ -0,0 +1,510 @@
+# The VTK-m changes are slated for paraview-5.5.x
+#
+# The catalyst changes (the working directory for output) are slated for
+# paraview-5.6.
+# They are API-compatible with paraview-5.5 but not ABI compatible.
+#
+# https://gitlab.kitware.com/vtk/vtk-m/merge_requests/1166
+# https://gitlab.kitware.com/paraview/paraview/merge_requests/2433
+# https://gitlab.kitware.com/paraview/paraview/merge_requests/2436
+#
+--- ParaView-v5.5.0/VTK/ThirdParty/vtkm/vtk-m/CMake/VTKmDetermineVersion.cmake.orig 2018-04-06 22:03:33.000000000 +0200
++++ ParaView-v5.5.0/VTK/ThirdParty/vtkm/vtk-m/CMake/VTKmDetermineVersion.cmake 2018-04-23 12:00:23.708544206 +0200
+@@ -51,6 +51,8 @@
+ ERROR_QUIET
+ OUTPUT_STRIP_TRAILING_WHITESPACE
+ ERROR_STRIP_TRAILING_WHITESPACE)
++ else()
++ set(output)
+ endif()
+ else()
+ set(result 0)
+@@ -75,7 +77,7 @@
+
+ # Extracts components from a version string. See determine_version() for usage.
+ function(extract_version_components version_string var_prefix)
+- string(REGEX MATCH "([0-9]+)\\.([0-9]+)\\.([0-9]+)[-]*(.*)"
++ string(REGEX MATCH "^([0-9]+)\\.([0-9]+)\\.([0-9]+)[-]*(.*)$"
+ version_matches "${version_string}")
+ if(CMAKE_MATCH_0)
+ set(full ${CMAKE_MATCH_0})
+--- ParaView-v5.5.0/CoProcessing/Catalyst/vtkCPProcessor.cxx.orig 2018-04-06 22:03:33.000000000 +0200
++++ ParaView-v5.5.0/CoProcessing/Catalyst/vtkCPProcessor.cxx 2018-05-11 12:02:26.894772713 +0200
+@@ -38,6 +38,7 @@
+ #include "vtkStringArray.h"
+
+ #include <list>
++#include <vtksys/SystemTools.hxx>
+
+ struct vtkCPProcessorInternals
+ {
+@@ -47,12 +48,13 @@
+ };
+
+ vtkStandardNewMacro(vtkCPProcessor);
+-vtkMultiProcessController* vtkCPProcessor::Controller = NULL;
++vtkMultiProcessController* vtkCPProcessor::Controller = nullptr;
+ //----------------------------------------------------------------------------
+ vtkCPProcessor::vtkCPProcessor()
+ {
+ this->Internal = new vtkCPProcessorInternals;
+- this->InitializationHelper = NULL;
++ this->InitializationHelper = nullptr;
++ this->WorkingDirectory = nullptr;
+ }
+
+ //----------------------------------------------------------------------------
+@@ -61,14 +63,15 @@
+ if (this->Internal)
+ {
+ delete this->Internal;
+- this->Internal = NULL;
++ this->Internal = nullptr;
+ }
+
+ if (this->InitializationHelper)
+ {
+ this->InitializationHelper->Delete();
+- this->InitializationHelper = NULL;
++ this->InitializationHelper = nullptr;
+ }
++ this->SetWorkingDirectory(nullptr);
+ }
+
+ //----------------------------------------------------------------------------
+@@ -95,7 +98,7 @@
+ {
+ if (which < 0 || which >= this->GetNumberOfPipelines())
+ {
+- return NULL;
++ return nullptr;
+ }
+ int counter = 0;
+ vtkCPProcessorInternals::PipelineListIterator iter = this->Internal->Pipelines.begin();
+@@ -108,7 +111,7 @@
+ counter++;
+ iter++;
+ }
+- return NULL;
++ return nullptr;
+ }
+
+ //----------------------------------------------------------------------------
+@@ -130,17 +133,41 @@
+ }
+
+ //----------------------------------------------------------------------------
+-int vtkCPProcessor::Initialize()
++int vtkCPProcessor::Initialize(const char* workingDirectory)
+ {
+- if (this->InitializationHelper == NULL)
++ if (this->InitializationHelper == nullptr)
+ {
+ this->InitializationHelper = this->NewInitializationHelper();
+ }
++ // make sure the directory exists here so that we only do it once
++ if (workingDirectory)
++ {
++ vtkMultiProcessController* controller = vtkMultiProcessController::GetGlobalController();
++ int success = 1;
++ if (controller == nullptr || controller->GetLocalProcessId() == 0)
++ {
++ success = vtksys::SystemTools::MakeDirectory(workingDirectory) == true ? 1 : 0;
++ if (success == 0)
++ {
++ vtkWarningMacro("Could not make "
++ << workingDirectory << " directory. "
++ << "Results will be generated in current working directory instead.");
++ }
++ }
++ if (controller)
++ {
++ controller->Broadcast(&success, 1, 0);
++ }
++ if (success)
++ {
++ this->SetWorkingDirectory(workingDirectory);
++ }
++ }
+ return 1;
+ }
+
+ //----------------------------------------------------------------------------
+-int vtkCPProcessor::Initialize(vtkMPICommunicatorOpaqueComm& comm)
++int vtkCPProcessor::Initialize(vtkMPICommunicatorOpaqueComm& comm, const char* workingDirectory)
+ {
+ #ifdef PARAVIEW_USE_MPI
+ if (vtkCPProcessor::Controller)
+@@ -148,7 +175,7 @@
+ vtkErrorMacro("Can only initialize with a communicator once per process.");
+ return 0;
+ }
+- if (this->InitializationHelper == NULL)
++ if (this->InitializationHelper == nullptr)
+ {
+ vtkMPICommunicator* communicator = vtkMPICommunicator::New();
+ communicator->InitializeExternal(&comm);
+@@ -157,12 +184,12 @@
+ this->Controller = controller;
+ this->Controller->SetGlobalController(controller);
+ communicator->Delete();
+- return this->Initialize();
++ return this->Initialize(workingDirectory);
+ }
+ return 1;
+ #else
+ static_cast<void>(&comm); // get rid of variable not used warning
+- return this->Initialize();
++ return this->Initialize(workingDirectory);
+ #endif
+ }
+
+@@ -225,6 +252,13 @@
+ input->GetFieldData()->AddArray(catalystChannel);
+ }
+ }
++
++ std::string originalWorkingDirectory;
++ if (this->WorkingDirectory)
++ {
++ originalWorkingDirectory = vtksys::SystemTools::GetCurrentWorkingDirectory();
++ vtksys::SystemTools::ChangeDirectory(this->WorkingDirectory);
++ }
+ for (vtkCPProcessorInternals::PipelineListIterator iter = this->Internal->Pipelines.begin();
+ iter != this->Internal->Pipelines.end(); iter++)
+ {
+@@ -248,6 +282,10 @@
+ }
+ }
+ }
++ if (originalWorkingDirectory.empty() == false)
++ {
++ vtksys::SystemTools::ChangeDirectory(originalWorkingDirectory);
++ }
+ // we want to reset everything here to make sure that new information
+ // is properly passed in the next time.
+ dataDescription->ResetAll();
+@@ -259,7 +297,7 @@
+ {
+ if (this->Controller)
+ {
+- this->Controller->SetGlobalController(NULL);
++ this->Controller->SetGlobalController(nullptr);
+ this->Controller->Finalize(1);
+ this->Controller->Delete();
+ }
+--- ParaView-v5.5.0/CoProcessing/Catalyst/vtkCPProcessor.h.orig 2018-04-06 22:03:33.000000000 +0200
++++ ParaView-v5.5.0/CoProcessing/Catalyst/vtkCPProcessor.h 2018-05-11 12:02:26.894772713 +0200
+@@ -76,14 +76,16 @@
+ virtual void RemoveAllPipelines();
+
+ /// Initialize the co-processor. Returns 1 if successful and 0
+- /// otherwise.
+ /// otherwise. If Catalyst is built with MPI then Initialize()
+ /// can also be called with a specific MPI communicator if
+ /// MPI_COMM_WORLD isn't the proper one. Catalyst is initialized
+- /// to use MPI_COMM_WORLD by default.
+- virtual int Initialize();
++ /// to use MPI_COMM_WORLD by default. Both methods have an optional
++ /// workingDirectory argument which will set *WorkingDirectory* so
++ /// that files will be put relative to this directory.
++ virtual int Initialize(const char* workingDirectory = nullptr);
+ #ifndef __WRAP__
+- virtual int Initialize(vtkMPICommunicatorOpaqueComm& comm);
++ virtual int Initialize(
++ vtkMPICommunicatorOpaqueComm& comm, const char* workingDirectory = nullptr);
+ #endif
+
+ /// The Catalyst input field data string array name. This array will
+@@ -111,6 +113,13 @@
+ /// implementation an opportunity to clean up, before it is destroyed.
+ virtual int Finalize();
+
++ /// Get the current working directory for outputting Catalyst files.
++ /// If not set then Catalyst output files will be relative to the
++ /// current working directory. This will not affect where Catalyst
++ /// looks for Python scripts. *WorkingDirectory* gets set through
++ /// the *Initialize()* methods.
++ vtkGetStringMacro(WorkingDirectory);
++
+ protected:
+ vtkCPProcessor();
+ virtual ~vtkCPProcessor();
+@@ -118,6 +127,11 @@
+ /// Create a new instance of the InitializationHelper.
+ virtual vtkObject* NewInitializationHelper();
+
++ /// Set the current working directory for outputting Catalyst files.
++ /// This is a protected method since simulation code adaptors should
++ /// set this through the *Initialize()* methods.
++ vtkSetStringMacro(WorkingDirectory);
++
+ private:
+ vtkCPProcessor(const vtkCPProcessor&) = delete;
+ void operator=(const vtkCPProcessor&) = delete;
+@@ -125,6 +139,7 @@
+ vtkCPProcessorInternals* Internal;
+ vtkObject* InitializationHelper;
+ static vtkMultiProcessController* Controller;
++ char* WorkingDirectory;
+ };
+
+ #endif
+--- ParaView-v5.5.0/CoProcessing/Catalyst/vtkCPXMLPWriterPipeline.cxx.orig 2018-04-06 22:03:33.000000000 +0200
++++ ParaView-v5.5.0/CoProcessing/Catalyst/vtkCPXMLPWriterPipeline.cxx 2018-05-11 12:02:26.894772713 +0200
+@@ -31,6 +31,7 @@
+ #include <vtkSmartPointer.h>
+ #include <vtkUnstructuredGrid.h>
+
++#include <algorithm>
+ #include <sstream>
+ #include <string>
+
+@@ -174,7 +175,7 @@
+
+ for (unsigned int i = 0; i < dataDescription->GetNumberOfInputDescriptions(); i++)
+ {
+- const char* inputName = dataDescription->GetInputDescriptionName(i);
++ std::string inputName = dataDescription->GetInputDescriptionName(i);
+ vtkCPInputDataDescription* idd = dataDescription->GetInputDescription(i);
+ vtkDataObject* grid = idd->GetGrid();
+ if (grid == nullptr)
+@@ -206,6 +207,8 @@
+ vtkSMStringVectorProperty* fileName =
+ vtkSMStringVectorProperty::SafeDownCast(writer->GetProperty("FileName"));
+
++ // If we have a / in the channel name we take it out of the filename we're going to write to
++ inputName.erase(std::remove(inputName.begin(), inputName.end(), '/'), inputName.end());
+ std::ostringstream o;
+ if (this->Path.empty() == false)
+ {
+--- ParaView-v5.5.0/Wrapping/Python/paraview/coprocessing.py.orig 2018-04-06 22:03:33.000000000 +0200
++++ ParaView-v5.5.0/Wrapping/Python/paraview/coprocessing.py 2018-05-11 12:02:27.038772408 +0200
+@@ -11,22 +11,12 @@
+ from paraview.vtk.vtkPVVTKExtensionsCore import *
+ import math
+
+-# -----------------------------------------------------------------------------
+-def IsInModulo(timestep, frequencyArray):
+- """
+- Return True if the given timestep is in one of the provided frequency.
+- This can be interpreted as follow::
+-
+- isFM = IsInModulo(timestep, [2,3,7])
+-
+- is similar to::
++# If the user created a filename in a location that doesn't exist by default we'll
++# make the directory for them. This can be changed though by setting createDirectoriesIfNeeded
++# to False.
++createDirectoriesIfNeeded = True
+
+- isFM = (timestep % 2 == 0) or (timestep % 3 == 0) or (timestep % 7 == 0)
+- """
+- for frequency in frequencyArray:
+- if frequency > 0 and (timestep % frequency == 0):
+- return True
+- return False
++# -----------------------------------------------------------------------------
+
+ class CoProcessor(object):
+ """Base class for co-processing Pipelines.
+@@ -68,6 +58,9 @@
+ self.__CinemaTracks = {}
+ self.__InitialFrequencies = {}
+ self.__PrintEnsightFormatString = False
++ self.__TimeStepToStartOutputAt=0
++ self.__ForceOutputAtFirstCall=False
++ self.__FirstTimeStepIndex = None
+
+ def SetPrintEnsightFormatString(self, enable):
+ """If outputting ExodusII files with the purpose of reading them into
+@@ -87,6 +80,17 @@
+ "Incorrect argument type: %s, must be a dict" % type(frequencies))
+ self.__InitialFrequencies = frequencies
+
++ def SetInitialOutputOptions(self, timeStepToStartOutputAt, forceOutputAtFirstCall):
++ """Set the frequencies at which the pipeline needs to be updated.
++ Typically, this is called by the subclass once it has determined what
++ timesteps co-processing will be needed to be done.
++ frequencies is a map, with key->string name of for the simulation
++ input, and value is a list of frequencies.
++ """
++
++ self.__TimeStepToStartOutputAt=timeStepToStartOutputAt
++ self.__ForceOutputAtFirstCall=forceOutputAtFirstCall
++
+ def EnableLiveVisualization(self, enable, frequency = 1):
+ """Call this method to enable live-visualization. When enabled,
+ DoLiveVisualization() will communicate with ParaView server if possible
+@@ -115,7 +119,7 @@
+ # if this is a time step to do live then all of the inputs
+ # must be made available. note that we want the pipeline built
+ # before we do the actual first live connection.
+- if self.__EnableLiveVisualization and timestep % self.__LiveVisualizationFrequency == 0 \
++ if self.__EnableLiveVisualization and self.NeedToOutput(timestep, self.__LiveVisualizationFrequency) \
+ and self.__LiveVisualizationLink:
+ if self.__LiveVisualizationLink.Initialize(servermanager.ActiveConnection.Session.GetSessionProxyManager()):
+ num_inputs = datadescription.GetNumberOfInputDescriptions()
+@@ -132,13 +136,13 @@
+ # hasn't been set up yet). If we don't have live enabled
+ # we know that the output frequencies aren't changed and can
+ # just use the initial frequencies.
+- if self.__InitialFrequencies or not self.__EnableLiveVisualization:
++ if self.__ForceOutputAtFirstCall or self.__InitialFrequencies or not self.__EnableLiveVisualization:
+ num_inputs = datadescription.GetNumberOfInputDescriptions()
+ for cc in range(num_inputs):
+ input_name = datadescription.GetInputDescriptionName(cc)
+
+ freqs = self.__InitialFrequencies.get(input_name, [])
+- if self.__EnableLiveVisualization or ( self and IsInModulo(timestep, freqs) ):
++ if self.__EnableLiveVisualization or ( self and self.IsInModulo(timestep, freqs) ):
+ datadescription.GetInputDescription(cc).AllFieldsOn()
+ datadescription.GetInputDescription(cc).GenerateMeshOn()
+ else:
+@@ -149,15 +153,14 @@
+ for writer in self.__WritersList:
+ frequency = writer.parameters.GetProperty(
+ "WriteFrequency").GetElement(0)
+- if (timestep % frequency) == 0 or \
+- datadescription.GetForceOutput() == True:
++ if self.NeedToOutput(timestep, frequency) or datadescription.GetForceOutput() == True:
+ writerinputs = cpstate.locate_simulation_inputs(writer)
+ for writerinput in writerinputs:
+ datadescription.GetInputDescriptionByName(writerinput).AllFieldsOn()
+ datadescription.GetInputDescriptionByName(writerinput).GenerateMeshOn()
+
+ for view in self.__ViewsList:
+- if (view.cpFrequency and timestep % view.cpFrequency == 0) or \
++ if (view.cpFrequency and self.NeedToOutput(timestep, view.cpFrequency)) or \
+ datadescription.GetForceOutput() == True:
+ viewinputs = cpstate.locate_simulation_inputs_for_view(view)
+ for viewinput in viewinputs:
+@@ -192,8 +195,7 @@
+ for writer in self.__WritersList:
+ frequency = writer.parameters.GetProperty(
+ "WriteFrequency").GetElement(0)
+- if (timestep % frequency) == 0 or \
+- datadescription.GetForceOutput() == True:
++ if self.NeedToOutput(timestep, frequency) or datadescription.GetForceOutput() == True:
+ fileName = writer.parameters.GetProperty("FileName").GetElement(0)
+ paddingamount = writer.parameters.GetProperty("PaddingAmount").GetElement(0)
+ helperName = writer.GetXMLName()
+@@ -203,6 +205,23 @@
+ else:
+ ts = str(timestep).rjust(paddingamount, '0')
+ writer.FileName = fileName.replace("%t", ts)
++ if '/' in writer.FileName and createDirectoriesIfNeeded:
++ oktowrite = [1.]
++ import vtk
++ comm = vtk.vtkMultiProcessController.GetGlobalController()
++ if comm.GetLocalProcessId() == 0:
++ import os
++ newDir = writer.FileName[0:writer.FileName.rfind('/')]
++ try:
++ os.makedirs(newDir)
++ except OSError:
++ if not os.path.isdir(newDir):
++ print ("ERROR: Cannot make directory for", writer.FileName, ". No data will be written.")
++ oktowrite[0] = 0.
++ comm.Broadcast(oktowrite, 1, 0)
++ if oktowrite[0] == 0:
++ # we can't make the directory so no reason to update the pipeline
++ return
+ writer.UpdatePipeline(datadescription.GetTime())
+
+ def WriteImages(self, datadescription, rescale_lookuptable=False,
+@@ -240,7 +259,7 @@
+
+ cinema_dirs = []
+ for view in self.__ViewsList:
+- if (view.cpFrequency and timestep % view.cpFrequency == 0) or \
++ if (view.cpFrequency and self.NeedToOutput(timestep, view.cpFrequency)) or \
+ datadescription.GetForceOutput() == True:
+ fname = view.cpFileName
+ ts = str(timestep).rjust(padding_amount, '0')
+@@ -267,6 +286,24 @@
+ if dirname:
+ cinema_dirs.append(dirname)
+ else:
++ if '/' in fname and createDirectoriesIfNeeded:
++ oktowrite = [1.]
++ import vtk
++ comm = vtk.vtkMultiProcessController.GetGlobalController()
++ if comm.GetLocalProcessId() == 0:
++ import os
++ newDir = fname[0:fname.rfind('/')]
++ try:
++ os.makedirs(newDir)
++ except OSError:
++ if not os.path.isdir(newDir):
++ print ("ERROR: Cannot make directory for", fname, ". No image will be output.")
++ oktowrite[0] = 0.
++ comm.Broadcast(oktowrite, 1, 0)
++ if oktowrite[0] == 0:
++ # we can't make the directory so no reason to update the pipeline
++ return
++
+ if image_quality is None and fname.endswith('png'):
+ # for png quality = 0 means no compression. compression can be a potentially
+ # very costly serial operation on process 0
+@@ -307,7 +344,7 @@
+
+
+ timeStep = datadescription.GetTimeStep()
+- if self.__EnableLiveVisualization and timeStep % self.__LiveVisualizationFrequency == 0:
++ if self.__EnableLiveVisualization and self.NeedToOutput(timeStep, self.__LiveVisualizationFrequency):
+ if not self.__LiveVisualizationLink.Initialize(servermanager.ActiveConnection.Session.GetSessionProxyManager()):
+ return
+
+@@ -412,7 +449,7 @@
+ """
+ controller = servermanager.ParaViewPipelineController()
+ # assume that a client only proxy with the same name as a writer
+- # is available in "insitu_writer_paramters"
++ # is available in "insitu_writer_parameters"
+
+ # Since coprocessor sometimes pass writer as a custom object and not
+ # a proxy, we need to handle that. Just creating any arbitrary writer
+@@ -666,3 +703,42 @@
+ #restore what we showed
+ pv_introspect.restore_visibility(pxystate)
+ return os.path.basename(vfname)
++
++ def IsInModulo(self, timestep, frequencies):
++ """
++ Return True if the given timestep is in one of the provided frequency.
++ This can be interpreted as follow::
++
++ isFM = IsInModulo(timestep-timeStepToStartOutputAt, [2,3,7])
++
++ is similar to::
++
++ isFM = (timestep-timeStepToStartOutputAt % 2 == 0) or (timestep-timeStepToStartOutputAt % 3 == 0) or (timestep-timeStepToStartOutputAt % 7 == 0)
++
++ The timeStepToStartOutputAt is the first timestep that will potentially be output.
++ """
++ if timestep < self.__TimeStepToStartOutputAt and not self.__ForceOutputAtFirstCall:
++ return False
++ for frequency in frequencies:
++ if frequency > 0 and self.NeedToOutput(timestep, frequency):
++ return True
++
++ return False
++
++
++ def NeedToOutput(self, timestep, frequency):
++ """
++ Return True if we need to output based on the input timestep and frequency. Checks based
++ __FirstTimeStepIndex, __FirstTimeStepIndex, __ForceOutputAtFirstCall and __TimeStepToStartOutputAt
++ member variables.
++ """
++ if self.__FirstTimeStepIndex == None:
++ self.__FirstTimeStepIndex = timestep
++
++ if self.__ForceOutputAtFirstCall and self.__FirstTimeStepIndex == timestep:
++ return True
++
++ if self.__TimeStepToStartOutputAt <= timestep and (timestep-self.__TimeStepToStartOutputAt) % frequency == 0:
++ return True
++
++ return False