Commit c59d317b authored by Dominik Arominski's avatar Dominik Arominski
Browse files

Merge branch 'master' of https://github.com/mantidproject/mantid into d16_hotfixes

parents 5ef9ff60 2b37feda
......@@ -30,6 +30,7 @@ set(SRC_FILES
src/CostFunctionFactory.cpp
src/DataProcessorAlgorithm.cpp
src/DeprecatedAlgorithm.cpp
src/DeprecatedAlias.cpp
src/DetectorSearcher.cpp
src/DistributedAlgorithm.cpp
src/DomainCreatorFactory.cpp
......@@ -202,6 +203,7 @@ set(INC_FILES
inc/MantidAPI/DataProcessorAlgorithm.h
inc/MantidAPI/DeclareUserAlg.h
inc/MantidAPI/DeprecatedAlgorithm.h
inc/MantidAPI/DeprecatedAlias.h
inc/MantidAPI/DetectorSearcher.h
inc/MantidAPI/DistributedAlgorithm.h
inc/MantidAPI/DomainCreatorFactory.h
......
......@@ -171,8 +171,11 @@ public:
/// Function to return all of the seeAlso (these are not validated) algorithms
/// related to this algorithm.A default implementation is provided.
const std::vector<std::string> seeAlso() const override { return {}; };
/// function to return any aliases to the algorithm; A default implementation is provided
/// Function to return any aliases to the algorithm; A default implementation
/// is provided
const std::string alias() const override { return ""; }
/// Flag to indicate if the algorithm is called by its alias.
bool calledByAlias = false;
/// function to return URL for algorithm documentation; A default
/// implementation is provided.
......
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright &copy; 2021 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source,
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#pragma once
#include "MantidAPI/Algorithm.h"
#include "MantidAPI/DllConfig.h"
#include "MantidAPI/IAlgorithm.h"
#include <string>
namespace Mantid {
namespace API {
/** DeprecatedAlias :
* Class for making algorithm with deprecated names (aliases).
*
* This class will ensure that if an algorithm is invoke with a deprecated name,
* - a warning will be throw to inform the users that
* - the algorithm name is deprecated
* - the deprecation date
* - the new algorithm name the user is recommended to use instead
*
* All algorithms with deprecated alias need to inherit from this class.
*
* The recommended algorithm naming pattern should be
* [Technique][Facility/Instrument]ActionTarget
* For example: the calibration routine of panel detector for single crystal diffraction
* beamline can be named as SCDCalibratePanels
*/
class MANTID_API_DLL DeprecatedAlias {
public:
DeprecatedAlias();
virtual ~DeprecatedAlias();
std::string deprecationMessage(const IAlgorithm *);
void setDeprecationDate(const std::string &date);
private:
/// Deprecation date
std::string m_deprecationDate;
};
} // namespace API
} // namespace Mantid
......@@ -10,6 +10,7 @@
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/AnalysisDataService.h"
#include "MantidAPI/DeprecatedAlgorithm.h"
#include "MantidAPI/DeprecatedAlias.h"
#include "MantidAPI/IWorkspaceProperty.h"
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidAPI/WorkspaceHistory.h"
......@@ -512,12 +513,21 @@ bool Algorithm::executeInternal() {
Timer timer;
bool algIsExecuted = false;
AlgorithmManager::Instance().notifyAlgorithmStarting(this->getAlgorithmID());
// runtime check for deprecation warning
{
auto *depo = dynamic_cast<DeprecatedAlgorithm *>(this);
if (depo != nullptr)
getLogger().error(depo->deprecationMsg(this));
}
// runtime check for deprecated alias warning
{
auto *da_alg = dynamic_cast<DeprecatedAlias *>(this);
if ((da_alg != nullptr) && (this->calledByAlias))
getLogger().warning(da_alg->deprecationMessage(this));
}
// Register clean up tasks that should happen regardless of the route
// out of the algorithm. These tasks will get run after this method
// finishes.
......
......@@ -34,8 +34,8 @@ AlgorithmFactoryImpl::AlgorithmFactoryImpl() : Kernel::DynamicFactory<Algorithm>
AlgorithmFactoryImpl::~AlgorithmFactoryImpl() = default;
/** Creates an instance of an algorithm
* @param name :: the name of the Algrorithm to create
* @param version :: the version of the algroithm to create
* @param name :: the name of the Algorithm to create
* @param version :: the version of the algorithm to create
* @returns a shared pointer to the created algorithm
*/
std::shared_ptr<Algorithm> AlgorithmFactoryImpl::create(const std::string &name, const int &version) const {
......@@ -57,7 +57,9 @@ std::shared_ptr<Algorithm> AlgorithmFactoryImpl::create(const std::string &name,
if (realName) {
// Try create algorithm again with real name
try {
return this->createAlgorithm(realName.get(), local_version);
auto alg = this->createAlgorithm(realName.get(), local_version);
alg->calledByAlias = true;
return alg;
} catch (Kernel::Exception::NotFoundError &) {
// Get highest registered version
const auto hVersion = highestVersion(realName.get()); // Throws if not found
......@@ -209,7 +211,7 @@ const std::vector<std::string> AlgorithmFactoryImpl::getKeys(bool includeHidden)
/**
* @param alias The name of the algorithm to look up in the alias map
* @return Real name of algroithm if found
* @return Real name of algorithm if found
*/
boost::optional<std::string> AlgorithmFactoryImpl::getRealNameFromAlias(const std::string &alias) const noexcept {
auto a_it = m_amap.find(alias);
......
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright &copy; 2021 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source,
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#include "MantidAPI/DeprecatedAlias.h"
#include "MantidAPI/AlgorithmFactory.h"
#include "MantidKernel/Logger.h"
#include "MantidTypes/Core/DateAndTimeHelpers.h"
namespace Mantid {
namespace API {
namespace {
Kernel::Logger g_log("DeprecatedAlias");
} // namespace
/// Constructor to ensure the compiler is happy
DeprecatedAlias::DeprecatedAlias() : m_deprecationDate() {}
/// Destructor to ensure the compiler is happy
DeprecatedAlias::~DeprecatedAlias() = default;
/**
* @brief Set the deprecation date which will be used to inform the users
*
* @param date : deprecation date in ISO8601 format
*/
void DeprecatedAlias::setDeprecationDate(const std::string &date) {
m_deprecationDate = "";
if ((!date.empty()) && (Types::Core::DateAndTimeHelpers::stringIsISO8601(date))) {
m_deprecationDate = date;
} else {
throw std::invalid_argument("DeprecatedAlias::DeprecationDate(): deprecation date must follow ISO8601.");
}
}
/**
* @brief Construct and return a full deprecation message
*
* @param algo
* @return std::string
*/
std::string DeprecatedAlias::deprecationMessage(const IAlgorithm *algo) {
std::stringstream msg;
auto alias = algo->alias();
if (alias.empty()) {
throw std::runtime_error("Cannot find the deprecated alias for this algorithm.");
} else {
msg << "The algorithm '" << alias << "' is deprecated on " << m_deprecationDate << "."
<< "Please use '" << algo->name() << "' instead.";
}
return msg.str();
}
} // namespace API
} // namespace Mantid
......@@ -161,7 +161,6 @@ set(SRC_FILES
src/GeneratePythonFitScript.cpp
src/GeneratePythonScript.cpp
src/GetAllEi.cpp
src/GetDetOffsetsMultiPeaks.cpp
src/GetDetectorOffsets.cpp
src/GetEi.cpp
src/GetEi2.cpp
......@@ -505,7 +504,6 @@ set(INC_FILES
inc/MantidAlgorithms/GeneratePythonFitScript.h
inc/MantidAlgorithms/GeneratePythonScript.h
inc/MantidAlgorithms/GetAllEi.h
inc/MantidAlgorithms/GetDetOffsetsMultiPeaks.h
inc/MantidAlgorithms/GetDetectorOffsets.h
inc/MantidAlgorithms/GetEi.h
inc/MantidAlgorithms/GetEi2.h
......@@ -863,7 +861,6 @@ set(TEST_FILES
GeneratePythonFitScriptTest.h
GeneratePythonScriptTest.h
GetAllEiTest.h
GetDetOffsetsMultiPeaksTest.h
GetDetectorOffsetsTest.h
GetEiMonDet3Test.h
GetEiTest.h
......@@ -1091,4 +1088,4 @@ mtd_install_targets(TARGETS
INSTALL_DIRS
${PLUGINS_DIR}
${WORKBENCH_PLUGINS_DIR})
endif()
\ No newline at end of file
endif()
......@@ -36,7 +36,7 @@ public:
/// Algorithm's version for identification overriding a virtual method
int version() const override { return 1; }
const std::vector<std::string> seeAlso() const override { return {"AlignComponents", "GetDetOffsetsMultiPeaks"}; }
const std::vector<std::string> seeAlso() const override { return {"AlignComponents"}; }
/// Algorithm's category for identification overriding a virtual method
const std::string category() const override {
return "Diffraction\\Calibration;"
......
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright &copy; 2009 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source,
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#pragma once
#include "MantidAPI/Algorithm.h"
#include "MantidAPI/DeprecatedAlgorithm.h"
#include "MantidAPI/MatrixWorkspace_fwd.h"
#include "MantidAlgorithms/DllConfig.h"
#include "MantidDataObjects/EventWorkspace.h"
#include "MantidDataObjects/OffsetsWorkspace.h"
#include "MantidDataObjects/TableWorkspace.h"
namespace Mantid {
namespace Algorithms {
struct FitPeakOffsetResult {
double mask;
double offset;
double chi2;
/// fit sum from GSL optimizer as offset's error
double fitSum;
/// summation of chi-square
double chisqSum;
/// Number of peaks with successful fitting
double peakPosFittedSize;
int numpeakstofit;
int numpeaksfitted;
int numpeaksindrange;
std::string fitoffsetstatus;
/// Highest peak position
double highestpeakpos;
/// Highest peak deviation after calibrated by offset
double highestpeakdev;
/// Average resolution delta(d)/d
double resolution;
/// Standard devation of the resolution
double dev_resolution;
};
/**
Find the offsets for each detector
@author Vickie Lynch, SNS
@date 12/12/2011
*/
class MANTID_ALGORITHMS_DLL GetDetOffsetsMultiPeaks : public API::Algorithm, public API::DeprecatedAlgorithm {
public:
/// Default constructorMatrix
GetDetOffsetsMultiPeaks();
/// Algorithm's name for identification overriding a virtual method
const std::string name() const override { return "GetDetOffsetsMultiPeaks"; }
/// Algorithm's version for identification overriding a virtual method
int version() const override { return 1; }
const std::vector<std::string> seeAlso() const override { return {"GetDetectorOffsets"}; }
/// Algorithm's category for identification overriding a virtual method
const std::string category() const override { return "Diffraction\\Calibration"; }
/// Summary of algorithms purpose
const std::string summary() const override {
return "Creates an OffsetsWorkspace containing offsets for each detector. "
"You can then save these to a .cal file using SaveCalFile.";
}
private:
// Overridden Algorithm methods
void init() override;
void exec() override;
void processProperties();
/// Create workspaces for fitting information
void createInformationWorkspaces();
/// Main function to calculate all detectors' offsets
void calculateDetectorsOffsets();
void importFitWindowTableWorkspace(const DataObjects::TableWorkspace_sptr &windowtablews);
/// Call Gaussian as a Child Algorithm to fit the peak in a spectrum
int fitSpectra(const int64_t wi, const API::MatrixWorkspace_sptr &inputW, const std::vector<double> &peakPositions,
const std::vector<double> &fitWindows, size_t &nparams, double &minD, double &maxD,
std::vector<double> &peakPosToFit, std::vector<double> &peakPosFitted, std::vector<double> &chisq,
std::vector<double> &peakHeights, int &i_highestpeak, double &resolution, double &dev_resolution);
/// Add peak fitting and offset calculation information to information table
/// workspaces per spectrum
void addInfoToReportWS(int wi, const FitPeakOffsetResult &offsetresult, const std::vector<double> &tofitpeakpositions,
const std::vector<double> &fittedpeakpositions);
/// Generate a list of peaks to calculate detectors' offset
void generatePeaksList(const API::ITableWorkspace_sptr &peakslist, int wi, const std::vector<double> &peakPositionRef,
std::vector<double> &peakPosToFit, std::vector<double> &peakPosFitted,
std::vector<double> &peakHeightFitted, std::vector<double> &chisq, bool useFitWindows,
const std::vector<double> &fitWindowsToUse, const double minD, const double maxD,
double &deltaDovD, double &dev_deltaDovD);
FitPeakOffsetResult calculatePeakOffset(const int wi, std::vector<double> &vec_peakPosFitted,
std::vector<double> &vec_peakPosRef);
/// Calculate a spectrum's offset by optimizing offset
void fitPeaksOffset(const size_t inpnparams, const double minD, const double maxD,
const std::vector<double> &vec_peakPosRef, const std::vector<double> &vec_peakPosFitted,
const std::vector<double> &vec_peakHeights, FitPeakOffsetResult &fitresult);
/// Make a summary on all fit
void makeFitSummary();
/// Remove rows without offset calculated from offset table workspace
void removeEmptyRowsFromPeakOffsetTable();
/// Input workspace
API::MatrixWorkspace_sptr m_inputWS;
/// Input EventWorkspace (from m_inputWS)
DataObjects::EventWorkspace_const_sptr m_eventW;
bool m_isEvent;
/// Background type
std::string m_backType;
/// Peak profile type
std::string m_peakType;
/// Criterias for fitting peak
std::string m_minimizer;
double m_maxChiSq;
double m_minPeakHeight;
double m_leastMaxObsY;
double m_maxOffset;
std::vector<double> m_peakPositions;
std::vector<double> m_fitWindows;
/// Input resolution
API::MatrixWorkspace_const_sptr m_inputResolutionWS;
/// Flag of use input resolution
bool m_hasInputResolution;
/// Lower boundary of allowed peak width as resolution
double m_minResFactor;
/// Upper boundary of allowed peak width as resolution
double m_maxResFactor;
DataObjects::OffsetsWorkspace_sptr m_outputW;
/// Output workspace for debugging purpose
DataObjects::OffsetsWorkspace_sptr m_outputNP;
/// Output Mask workspace
API::MatrixWorkspace_sptr m_maskWS;
DataObjects::TableWorkspace_sptr m_infoTableWS;
DataObjects::TableWorkspace_sptr m_peakOffsetTableWS;
/// Workspace for calculated detector resolution
API::MatrixWorkspace_sptr m_resolutionWS;
/// Flag to use fit window from TableWorkspace per spectrum
bool m_useFitWindowTable;
/// Vector of fit windows (also in vector)
std::vector<std::vector<double>> m_vecFitWindow;
};
} // namespace Algorithms
} // namespace Mantid
......@@ -34,9 +34,7 @@ public:
/// Algorithm's version for identification overriding a virtual method
int version() const override { return 1; }
const std::vector<std::string> seeAlso() const override {
return {"GetDetOffsetsMultiPeaks", "CalibrateRectangularDetectors", "AlignComponents"};
}
const std::vector<std::string> seeAlso() const override { return {"AlignComponents"}; }
/// Algorithm's category for identification overriding a virtual method
const std::string category() const override { return "Diffraction\\Calibration"; }
......
......@@ -26,7 +26,7 @@ public:
const std::string name() const override;
int version() const override;
const std::vector<std::string> seeAlso() const override { return {"CalibrateRectangularDetectors"}; }
const std::vector<std::string> seeAlso() const override { return {}; }
const std::string category() const override;
const std::string summary() const override;
......
This diff is collapsed.
// Mantid Repository : https://github.com/mantidproject/mantid
//
// Copyright &copy; 2018 ISIS Rutherford Appleton Laboratory UKRI,
// NScD Oak Ridge National Laboratory, European Spallation Source,
// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
// SPDX - License - Identifier: GPL - 3.0 +
#pragma once
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/AnalysisDataService.h"
#include "MantidAPI/Axis.h"
#include "MantidAPI/FrameworkManager.h"
#include "MantidAPI/TableRow.h"
#include "MantidAlgorithms/GetDetOffsetsMultiPeaks.h"
#include "MantidDataObjects/OffsetsWorkspace.h"
#include "MantidDataObjects/TableWorkspace.h"
#include "MantidGeometry/Instrument/DetectorInfo.h"
#include "MantidKernel/UnitFactory.h"
#include "MantidTestHelpers/WorkspaceCreationHelper.h"
using namespace Mantid::API;
using namespace Mantid::DataObjects;
using Mantid::Algorithms::GetDetOffsetsMultiPeaks;
using Mantid::DataObjects::OffsetsWorkspace_sptr;
class GetDetOffsetsMultiPeaksTest : public CxxTest::TestSuite {
public:
// This pair of boilerplate methods prevent the suite being created statically
// This means the constructor isn't called when running other tests
static GetDetOffsetsMultiPeaksTest *createSuite() { return new GetDetOffsetsMultiPeaksTest(); }
static void destroySuite(GetDetOffsetsMultiPeaksTest *suite) { delete suite; }
GetDetOffsetsMultiPeaksTest() { Mantid::API::FrameworkManager::Instance(); }
void testTheBasics() {
TS_ASSERT_EQUALS(offsets.name(), "GetDetOffsetsMultiPeaks");
TS_ASSERT_EQUALS(offsets.version(), 1);
}
void testInit() {
TS_ASSERT_THROWS_NOTHING(offsets.initialize());
TS_ASSERT(offsets.isInitialized());
}
void testExec() {
// ---- Create the simple workspace -------
MatrixWorkspace_sptr WS = WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(1, 200);
AnalysisDataService::Instance().addOrReplace("temp_event_ws", WS);
WS->getAxis(0)->unit() = Mantid::Kernel::UnitFactory::Instance().create("dSpacing");
auto xvals = WS->points(0);
std::transform(xvals.cbegin(), xvals.cend(), WS->mutableY(0).begin(),
[](const double x) { return 5.1 * exp(-0.5 * pow((x - 10) / 1.0, 2)); });
auto &E = WS->mutableE(0);
E.assign(E.size(), 0.001);
// ---- Run algo -----
if (!offsets.isInitialized())
offsets.initialize();
TS_ASSERT_THROWS_NOTHING(offsets.setProperty("InputWorkspace", "temp_event_ws"));
std::string outputWS("offsetsped");
std::string maskWS("masksped");
TS_ASSERT_THROWS_NOTHING(offsets.setPropertyValue("OutputWorkspace", outputWS));
TS_ASSERT_THROWS_NOTHING(offsets.setPropertyValue("MaskWorkspace", maskWS));
TS_ASSERT_THROWS_NOTHING(offsets.setPropertyValue("DReference", "9.98040"));
TS_ASSERT_THROWS_NOTHING(offsets.setPropertyValue("SpectraFitInfoTableWorkspace", "FitInfoTable"));
TS_ASSERT_THROWS_NOTHING(offsets.execute());
TS_ASSERT(offsets.isExecuted());
MatrixWorkspace_const_sptr output;
TS_ASSERT_THROWS_NOTHING(output = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(outputWS));
if (!output)
return;
TS_ASSERT_DELTA(output->y(0)[0], -0.002, 0.0002);
AnalysisDataService::Instance().remove(outputWS);
MatrixWorkspace_const_sptr mask;
TS_ASSERT_THROWS_NOTHING(mask = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(maskWS));
if (!mask)
return;
TS_ASSERT(!mask->detectorInfo().isMasked(0));
}
//----------------------------------------------------------------------------------------------
/** Test the feature to ... ...
*/
void testExecWithGroup() {
// --------- Workspace with summed spectra -------
MatrixWorkspace_sptr WS = WorkspaceCreationHelper::createGroupedWorkspace2D(3, 200, 1.0);
WS->getAxis(0)->unit() = Mantid::Kernel::UnitFactory::Instance().create("dSpacing");
auto xvals = WS->points(0);
std::transform(xvals.cbegin(), xvals.cend(), WS->mutableY(0).begin(),
[](const double x) { return exp(-0.5 * pow((x - 10) / 1.0, 2)); });
auto &E = WS->mutableE(0);
E.assign(E.size(), 0.001);
AnalysisDataService::Instance().addOrReplace("temp_event_ws3", WS);
// ---- Run algo -----
GetDetOffsetsMultiPeaks offsets;
offsets.initialize();
TS_ASSERT_THROWS_NOTHING(offsets.setProperty("InputWorkspace", "temp_event_ws3"));
std::string outputWS("offsetsped");
std::string maskWS("masksped");
TS_ASSERT_THROWS_NOTHING(offsets.setPropertyValue("OutputWorkspace", outputWS));
TS_ASSERT_THROWS_NOTHING(offsets.setPropertyValue("MaskWorkspace", maskWS));
TS_ASSERT_THROWS_NOTHING(offsets.setPropertyValue("DReference", "9.98040"));
TS_ASSERT_THROWS_NOTHING(offsets.setPropertyValue("SpectraFitInfoTableWorkspace", "FitInfoTable"));
TS_ASSERT_THROWS_NOTHING(offsets.execute());
TS_ASSERT(offsets.isExecuted());
OffsetsWorkspace_sptr output = offsets.getProperty("OutputWorkspace");
if (!output)
return;
TS_ASSERT_DELTA(output->getValue(1), -0.00196, 0.0002);
TS_ASSERT_EQUALS(output->getValue(1), output->getValue(2));
TS_ASSERT_EQUALS(output->getValue(1), output->getValue(3));
AnalysisDataService::Instance().remove(outputWS);
MatrixWorkspace_const_sptr mask;
TS_ASSERT_THROWS_NOTHING(mask = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(maskWS));
if (!mask)
return;
TS_ASSERT(!mask->detectorInfo().isMasked(0));
}
//----------------------------------------------------------------------------------------------
/** Test the feature to import fit windows for each spectrum from table
* workspace
*/
void testExecFitWindowTable() {
// ---- (Re-)Create the simple workspace -------
MatrixWorkspace_sptr WS = WorkspaceCreationHelper::create2DWorkspaceWithFullInstrument(1, 200);
AnalysisDataService::Instance().addOrReplace("temp_event_ws", WS);
WS->getAxis(0)->unit() = Mantid::Kernel::UnitFactory::Instance().create("dSpacing");
auto xvals = WS->points(0);
std::transform(xvals.cbegin(), xvals.cend(), WS->mutableY(0).begin(),
[](const double x) { return 5.1 * exp(-0.5 * pow((x - 10) / 1.0, 2)); });
auto &E = WS->mutableE(0);
E.assign(E.size(), 0.001);
// Create table workspace
TableWorkspace_sptr fitWindowWS = std::make_shared<TableWorkspace>();
fitWindowWS->addColumn("int", "spectrum");
fitWindowWS->addColumn("double", "peak0_left");
fitWindowWS->addColumn("double", "peak0_right");
TableRow newrow = fitWindowWS->appendRow();
newrow << 0 << 9.9 << 11.0;
AnalysisDataService::Instance().addOrReplace("PeakFitRangeTableWS", fitWindowWS);
// ---- Run algo -----
offsets.initialize();
TS_ASSERT_THROWS_NOTHING(offsets.setProperty("InputWorkspace", "temp_event_ws"));
TS_ASSERT_THROWS_NOTHING(offsets.setProperty("FitwindowTableWorkspace", fitWindowWS));
std::string outputWS("offsetsped");
std::string maskWS("masksped");
TS_ASSERT_THROWS_NOTHING(offsets.setPropertyValue("OutputWorkspace", outputWS));