Commit 4d8d370d authored by Doucet, Mathieu's avatar Doucet, Mathieu
Browse files

Re #5051 Added EQSANS reduction example for mpi/live

parent e2d89c58
......@@ -54,8 +54,9 @@ protected:
/// MPI option. If false, we will use one job event if MPI is available
bool m_useMPI;
Workspace_sptr assemble(const std::string &partialWSName, const std::string &outputWSName);
void saveNexus(const std::string &outputWSName,
const std::string &outputFile);
void saveNexus(const std::string &outputWSName, const std::string &outputFile);
bool isMainThread();
int getNThreads();
private:
/// The name of the algorithm to invoke when loading data
......
......@@ -8,6 +8,7 @@
#include "MantidAPI/PropertyManagerDataService.h"
#include "MantidKernel/PropertyManager.h"
#include <stdexcept>
#include "Poco/Path.h"
#ifdef MPI_BUILD
#include <boost/mpi.hpp>
#endif
......@@ -24,7 +25,7 @@ namespace API
//----------------------------------------------------------------------------------------------
/** Constructor
*/
DataProcessorAlgorithm::DataProcessorAlgorithm()
DataProcessorAlgorithm::DataProcessorAlgorithm() : API::Algorithm()
{
m_loadAlg = "Load";
m_accumulateAlg = "Plus";
......@@ -66,22 +67,28 @@ namespace API
Workspace_sptr DataProcessorAlgorithm::assemble(const std::string &partialWSName, const std::string &outputWSName)
{
std::string threadOutput = partialWSName;
#ifdef MPI_BUILD
Workspace_sptr partialWS = AnalysisDataService::Instance().retrieve(partialWSName);
IAlgorithm_sptr gatherAlg = createSubAlgorithm("GatherWorkspaces");
gatherAlg->setLogging(true);
gatherAlg->setAlwaysStoreInADS(true);
gatherAlg->setProperty("InputWorkspace", partialWS);
gatherAlg->setProperty("PreserveEvents", true);
gatherAlg->setPropertyValue("OutputWorkspace", outputWSName);
gatherAlg->execute();
if (isMainThread()) threadOutput = outputWSName;
#else
UNUSED_ARG(outputWSName)
#endif
Workspace_sptr outputWS = AnalysisDataService::Instance().retrieve(outputWSName);
Workspace_sptr outputWS = AnalysisDataService::Instance().retrieve(threadOutput);
return outputWS;
}
void DataProcessorAlgorithm::saveNexus(const std::string &outputWSName,
const std::string &outputFile)
{
bool saveOutput = true;
#ifdef MPI_BUILD
if(boost::mpi::communicator().rank()>0) saveOutput = false;
......@@ -96,17 +103,32 @@ namespace API
}
}
bool DataProcessorAlgorithm::isMainThread()
{
bool mainThread = true;
#ifdef MPI_BUILD
mainThread = (boost::mpi::communicator().rank()==0);
#endif
return mainThread;
}
int DataProcessorAlgorithm::getNThreads()
{
#ifdef MPI_BUILD
return boost::mpi::communicator().size();
#else
return 1;
#endif
}
/// Determine what kind of input data we have and load it
//TODO: Chunking, MPI, etc...
Workspace_sptr DataProcessorAlgorithm::load(const std::string &inputData)
{
Workspace_sptr inputWS;
std::string outputWSName = inputData;
// First, check whether we have the name of an existing workspace
if (AnalysisDataService::Instance().doesExist(inputData))
{
Workspace_sptr inputWS = AnalysisDataService::Instance().retrieve(inputData);
inputWS = AnalysisDataService::Instance().retrieve(inputData);
}
else
{
......@@ -121,6 +143,9 @@ namespace API
if (!foundFile.empty())
{
Poco::Path p(foundFile);
const std::string outputWSName = p.getBaseName();
IAlgorithm_sptr loadAlg = createSubAlgorithm(m_loadAlg);
loadAlg->setProperty("Filename", foundFile);
loadAlg->setAlwaysStoreInADS(true);
......@@ -144,8 +169,10 @@ namespace API
#endif
loadAlg->execute();
Workspace_sptr inputMatrixWS = AnalysisDataService::Instance().retrieve(outputWSName);
inputWS = AnalysisDataService::Instance().retrieve(outputWSName);
}
else
throw std::runtime_error("DataProcessorAlgorithm::load could process any data");
}
return inputWS;
}
......
......@@ -43,6 +43,15 @@ class RunPythonScript(PythonAlgorithm):
# 1. get parameter values
wsInputName = self.getPropertyValue("InputWorkspace")
wsOutputName = self.getPropertyValue("OutputWorkspace")
# Output workspace properties of sub-algorithms are given the name 'ChildAlgOutput'
# by default. In the case of python algorithms, it means that such a workspace
# will be put in the ADS if another name is not given. Catch that case here:
if wsOutputName=="ChildAlgOutput":
wsOutputName = "__RunPythonScriptOutput"
self.setPropertyValue("OutputWorkspace", wsOutputName)
# Get the code to be run
code = self.getPropertyValue("Code")
# Prepare variables expected in the script code
......@@ -61,14 +70,20 @@ class RunPythonScript(PythonAlgorithm):
if mtd.workspaceExists(wsOutputName):
# The script did create the workspace; use it
wsOut = mtd[wsOutputName]
elif len(wsOutputName)>0:
elif len(wsOutputName)>0 and len(wsInputName)>0:
# The script did NOT create it
# So we take care of cloning it so that the output is valid
CloneWorkspace(InputWorkspace=wsInputName, OutputWorkspace=wsOutputName)
wsOut = mtd[wsOutputName]
else:
# We don't have an input workspace to work with
# Create a dummy workspace so that the output is valid
#TODO: would be best if output workspace properties
# could be optional
CreateSingleValuedWorkspace(OutputWorkspace=wsOutputName,
DataValue=1.0, ErrorValue=0.0)
wsOut = mtd[wsOutputName]
if len(wsOutputName)>0:
self.setProperty("OutputWorkspace",wsOut)
self.setProperty("OutputWorkspace",wsOut)
return
......
......@@ -5,7 +5,8 @@ set ( SRC_FILES
src/EQSANSInstrument.cpp
src/EQSANSLoad.cpp
src/EQSANSPatchSensitivity.cpp
src/EQSANSQ2D.cpp
src/EQSANSQ2D.cpp
src/EQSANSReduce.cpp
src/HFIRDarkCurrentSubtraction.cpp
src/HFIRInstrument.cpp
src/HFIRLoad.cpp
......@@ -28,7 +29,8 @@ set ( INC_FILES
inc/MantidWorkflowAlgorithms/EQSANSInstrument.h
inc/MantidWorkflowAlgorithms/EQSANSLoad.h
inc/MantidWorkflowAlgorithms/EQSANSPatchSensitivity.h
inc/MantidWorkflowAlgorithms/EQSANSQ2D.h
inc/MantidWorkflowAlgorithms/EQSANSQ2D.h
inc/MantidWorkflowAlgorithms/EQSANSReduce.h
inc/MantidWorkflowAlgorithms/HFIRDarkCurrentSubtraction.h
inc/MantidWorkflowAlgorithms/HFIRInstrument.h
inc/MantidWorkflowAlgorithms/HFIRLoad.h
......
#ifndef MANTID_WORKFLOWALGORITHMS_EQSANSREDUCE_H_
#define MANTID_WORKFLOWALGORITHMS_EQSANSREDUCE_H_
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidAPI/DataProcessorAlgorithm.h"
#include "MantidAPI/MatrixWorkspace.h"
namespace Mantid
{
namespace WorkflowAlgorithms
{
class DLLExport EQSANSReduce : public API::DataProcessorAlgorithm
{
public:
/// (Empty) Constructor
EQSANSReduce() : API::DataProcessorAlgorithm() {}
/// Virtual destructor
virtual ~EQSANSReduce() {}
/// Algorithm's name
virtual const std::string name() const { return "EQSANSReduce"; }
/// Algorithm's version
virtual int version() const { return (1); }
/// Algorithm's category for identification
virtual const std::string category() const { return "Workflow\\SANS"; }
private:
/// Sets documentation strings for this algorithm
virtual void initDocs();
/// Initialisation code
void init();
/// Execution code
void exec();
void initializeReduction();
void performReduction(API::Workspace_sptr workspace);
API::Workspace_sptr postProcess(API::Workspace_sptr workspace);
API::Workspace_sptr loadInputData();
};
} // namespace WorkflowAlgorithms
} // namespace Mantid
#endif /*MANTID_WORKFLOWALGORITHMS_EQSANSREDUCE_H_*/
#ifndef MANTID_WORKFLOWALGORITHM_REDUCTION_PROCESSOR_H_
#define MANTID_WORKFLOWALGORITHM_REDUCTION_PROCESSOR_H_
#include "MantidKernel/System.h"
#include "MantidAPI/DataProcessorAlgorithm.h"
#include <vector>
namespace Mantid
{
namespace WorkflowAlgorithms
{
/** DataProcessorAlgorithm : TODO: DESCRIPTION
@date 2012-04-04
Copyright &copy; 2012 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
This file is part of Mantid.
Mantid 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.
Mantid 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 this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
class DLLExport ReductionProcessor: public API::DataProcessorAlgorithm
{
public:
/// Algorithm's name
virtual const std::string name() const { return "ReductionProcessor"; }
/// Algorithm's version
virtual int version() const { return (1); }
/// Algorithm's category for identification
virtual const std::string category() const { return "Workflow"; }
private:
/// Sets documentation strings for this algorithm
virtual void initDocs();
/// Initialisation code
void init();
/// Execution code
void exec();
};
} // namespace WorkflowAlgorithms
} // namespace Mantid
#endif /* MANTID_WORKFLOWALGORITHM_REDUCTION_PROCESSOR_H_ */
......@@ -7,6 +7,7 @@
#include "MantidAPI/Algorithm.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidKernel/PropertyManager.h"
namespace Mantid
{
......@@ -59,6 +60,7 @@ private:
/// Execution code
void exec();
std::string _findFile(std::string dataRun);
void initializeReduction(boost::shared_ptr<Kernel::PropertyManager> reductionManager);
};
......
/*WIKI*
Perform EQSANS reduction. This algorithm is used for live reduction
and can handle MPI.
*WIKI*/
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include "MantidWorkflowAlgorithms/EQSANSReduce.h"
#include "MantidAPI/FileProperty.h"
#include "MantidAPI/PropertyManagerDataService.h"
#include "MantidKernel/PropertyManager.h"
#include "MantidKernel/EnabledWhenProperty.h"
#include "MantidAPI/MatrixWorkspace.h"
namespace Mantid
{
namespace WorkflowAlgorithms
{
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(EQSANSReduce)
/// Sets documentation strings for this algorithm
void EQSANSReduce::initDocs()
{
this->setWikiSummary("Workflow to perform EQSANS reduction.");
this->setOptionalMessage("Workflow to perform EQSANS reduction.");
}
using namespace Kernel;
using namespace API;
using namespace Geometry;
void EQSANSReduce::init()
{
declareProperty(new API::FileProperty("Filename", "", API::FileProperty::OptionalLoad, "_event.nxs"),
"File containing the data to reduce");
declareProperty(new WorkspaceProperty<>("InputWorkspace", "", Direction::Input, PropertyMode::Optional),
"Workspace to be reduced");
declareProperty("ReductionProcess", true,
"If true, both the reduction and the post-processing will be run");
setPropertySettings("Filename", new EnabledWhenProperty("ReductionProcess", IS_EQUAL_TO, "1") );
declareProperty("PostProcess", false,
"If true, I(q) will be computed from the input workspace");
declareProperty(new API::FileProperty("LogDataFile", "", API::FileProperty::OptionalLoad, ".nxs"),
"For testing: optional file containing the sample logs");
setPropertySettings("LogDataFile", new EnabledWhenProperty("ReductionProcess", IS_EQUAL_TO, "1") );
declareProperty("ReductionProperties", "__eqsans_reduction_properties", Direction::Input);
declareProperty(new WorkspaceProperty<>("OutputWorkspace", "", Direction::Output),
"Workspace containing the sensitivity correction.");
declareProperty(new FileProperty("OutputFile", "", FileProperty::OptionalSave, ".nxs"),
"File path for the output nexus file");
}
Workspace_sptr EQSANSReduce::loadInputData()
{
setLoadAlg("LoadEventNexus");
Workspace_sptr inputWS;
std::string inputData = getPropertyValue("Filename");
const std::string inputWSName = getPropertyValue("InputWorkspace");
if (inputWSName.size() > 0 && inputData.size() > 0)
throw std::runtime_error("EQSANSReduce: Either the Filename property or InputWorkspace property must be provided, NOT BOTH");
else if (inputWSName.size() > 0)
inputWS = load(inputWSName);
else if (inputData.size() > 0)
inputWS = load(inputData);
else
throw std::runtime_error("EQSANSReduce: Either the Filename property or InputWorkspace property must be provided");
return inputWS;
}
void EQSANSReduce::performReduction(Workspace_sptr workspace)
{
if (!workspace)
throw std::runtime_error("EQSANSReduce.performReduction was passed a pointer to no workspace");
// For testing the live reduction, we may need to load some
// logs from another file
const std::string logFile = getPropertyValue("LogDataFile");
if (logFile.size()>0)
{
IAlgorithm_sptr alg = this->createSubAlgorithm("LoadNexusLogs");
alg->setLogging(false);
alg->setProperty("Workspace", workspace);
alg->setPropertyValue("Filename", logFile);
alg->setProperty("OverwriteLogs", true);
alg->execute();
}
// Write the Reducer python script to be executed
std::string script = "import reduction.instruments.sans.sns_command_interface as cmd\n";
script += "cmd.AppendDataFile([\"" + workspace->name() + "\"])\n";
script += "cmd.Reduce1D()\n";
// Run a snippet of python
IAlgorithm_sptr alg = this->createSubAlgorithm("RunPythonScript");
alg->setLogging(true);
alg->setPropertyValue("Code", script);
alg->execute();
}
Workspace_sptr EQSANSReduce::postProcess(Workspace_sptr workspace)
{
// Construct the script's output workspace name
const std::string outputIq = workspace->name() + "_Iq";
const std::string outputWSName = getPropertyValue("OutputWorkspace");
// Write the Reducer python script to be executed
std::string script = "import reduction.instruments.sans.sns_command_interface as cmd\n";
script += "from reduction.instruments.sans.sns_reduction_steps import AzimuthalAverageByFrame\n";
script += "averager = AzimuthalAverageByFrame()\n";
script += "output = \"" + outputIq + "\"\n";
script += "averager.execute(cmd.ReductionSingleton(),\"" + workspace->name() + "\")\n";
// Run a snippet of python
IAlgorithm_sptr scriptAlg = this->createSubAlgorithm("RunPythonScript");
scriptAlg->setLogging(true);
scriptAlg->setPropertyValue("Code", script);
scriptAlg->setPropertyValue("OutputWorkspace", outputIq);
scriptAlg->execute();
Workspace_sptr outputWS = AnalysisDataService::Instance().retrieve(outputIq);
MatrixWorkspace_sptr matrixWS = boost::dynamic_pointer_cast<MatrixWorkspace>(outputWS);
matrixWS *= getNThreads();
return outputWS;
}
void EQSANSReduce::exec()
{
// Check the validity of the input data and load as appropriate
Workspace_sptr workspace = loadInputData();
// Reduce the data
const bool doReduction = getProperty("ReductionProcess");
const bool doPostProcessing = getProperty("PostProcess");
const std::string outputFile = getPropertyValue("OutputFile");
if (doReduction) performReduction(workspace);
// Assemble parts (MPI jobs only)
std::string outputWSName = workspace->name();
Workspace_sptr assembledWS = assemble(outputWSName, outputWSName);
if (doPostProcessing)
{
if (isMainThread())
{
workspace = postProcess(assembledWS);
saveNexus(workspace->name(), outputFile);
}
setProperty("OutputWorkspace", workspace);
}
else if (doReduction)
{
setProperty("OutputWorkspace", workspace);
}
else
g_log.error() << "EQSANSReduce: The ReductionProcess and PostProcess properties are set to false: nothing to do" << std::endl;
}
} // namespace WorkflowAlgorithms
} // namespace Mantid
#include "MantidWorkflowAlgorithms/ReductionProcessor.h"
#include "MantidAPI/AlgorithmProperty.h"
#include "MantidAPI/AnalysisDataService.h"
#include "MantidAPI/IEventWorkspace.h"
#include "MantidKernel/System.h"
#include "MantidAPI/FileFinder.h"
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/FileProperty.h"
#include <stdexcept>
#ifdef MPI_BUILD
#include <boost/mpi.hpp>
#endif
using namespace Mantid::Kernel;
using namespace Mantid::API;
namespace Mantid
{
namespace WorkflowAlgorithms
{
// Register the algorithm into the AlgorithmFactory
DECLARE_ALGORITHM(ReductionProcessor)
/// Sets documentation strings for this algorithm
void ReductionProcessor::initDocs()
{
this->setWikiSummary("Data processor algorithm.");
this->setOptionalMessage("Data processor algorithm.");
}
void ReductionProcessor::init()
{
// Input data object (File or Workspace)
declareProperty("InputData", "", "Input data, either as a file path or a workspace name");
declareProperty("LoadAlgorithm", "LoadEventNexus");
declareProperty("ProcessingAlgorithm", "");
declareProperty(new WorkspaceProperty<>("OutputWorkspace","",Direction::Output));
declareProperty(new FileProperty("OutputFile", "", FileProperty::OptionalSave, ".nxs"),
"File path for the output nexus file");
}
void ReductionProcessor::exec()
{
// Set the data loader
const std::string loader = getProperty("LoadAlgorithm");
setLoadAlg(loader);
// Load the data
const std::string inputData = getProperty("InputData");
Workspace_sptr inputWS = load(inputData);
// Process the data
std::string outputWSName = getPropertyValue("OutputWorkspace");
const std::string procAlgName = getProperty("ProcessingAlgorithm");
IAlgorithm_sptr procAlg = createSubAlgorithm(procAlgName);
procAlg->setPropertyValue("InputWorkspace", inputData);
procAlg->setAlwaysStoreInADS(true);
procAlg->setPropertyValue("OutputWorkspace", outputWSName);
procAlg->execute();
Workspace_sptr outputWS = AnalysisDataService::Instance().retrieve(outputWSName);
// Assemble
outputWS = assemble(outputWSName, outputWSName);
setProperty("OutputWorkspace", outputWS);
// Save as necessary
const std::string outputFile = getPropertyValue("OutputFile");
saveNexus(outputWSName, outputFile);
}
}
}
......@@ -15,6 +15,7 @@ See [http://www.mantidproject.org/Reduction_for_HFIR_SANS SANS Reduction] docume
#include "MantidAPI/AlgorithmProperty.h"
#include "MantidAPI/PropertyManagerDataService.h"
#include "MantidKernel/PropertyManager.h"
#include "Poco/NumberFormatter.h"
namespace Mantid
{
......@@ -128,6 +129,20 @@ void SetupEQSANSReduction::init()
setPropertyGroup("SensitivityBeamCenterY", eff_grp);
setPropertyGroup("OutputSensitivityWorkspace", eff_grp);
declareProperty("SetupReducer",false, "If true, a Reducer object will be created");
declareProperty("TransmissionValue", EMPTY_DBL(), "If set, this value will be used as the transmission");
declareProperty(new API::FileProperty("TransmissionDirectBeam", "",
API::FileProperty::OptionalLoad, "_event.nxs"),
"Direct beam data file used to compute transmission");
declareProperty(new API::FileProperty("TransmissionEmptyBeam", "",
API::FileProperty::OptionalLoad, "_event.nxs"),
"Empty beam data file used to compute transmission");
setPropertySettings("TransmissionValue", new EnabledWhenProperty("SetupReducer", IS_EQUAL_TO, "1") );
setPropertySettings("TransmissionDirectBeam", new EnabledWhenProperty("SetupReducer", IS_EQUAL_TO, "1") );
setPropertySettings("TransmissionEmptyBeam", new EnabledWhenProperty("SetupReducer", IS_EQUAL_TO, "1") );
// Outputs
declareProperty("OutputMessage","",Direction::Output);
declareProperty("ReductionProperties","__sans_reduction_properties", Direction::Input);
......@@ -266,6 +281,105 @@ void SetupEQSANSReduction::exec()
}
setPropertyValue("OutputMessage", "EQSANS reduction options set");
// Create a python reduction singleton as needed
const bool setupReducer = getProperty("SetupReducer");
if (setupReducer) initializeReduction(reductionManager);
}
/*
* For backward compatibility, we have the option of creating a
* python ReductionSingleton object.
*/
void SetupEQSANSReduction::initializeReduction(boost::shared_ptr<PropertyManager> reductionManager)