Skip to content

Commit

Permalink
refs #5744. Make abstract class more generic
Browse files Browse the repository at this point in the history
  • Loading branch information
OwenArnold committed Aug 14, 2012
1 parent fa100cd commit 12b36a5
Show file tree
Hide file tree
Showing 6 changed files with 215 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ namespace API
virtual bool checkGroups();
/// Overriden from Algorithm base.
virtual bool processGroups();
/// Method to provide the name for the input workspace property.
virtual std::string fetchInputPropertyName() const = 0;

std::string createFormattedInputWorkspaceNames(const size_t& periodIndex) const;
void validateMultiPeriodGroupInputs(const size_t& nInputWorkspaces) const;
Expand Down
43 changes: 34 additions & 9 deletions Code/Mantid/Framework/API/src/MultiPeriodGroupAlgorithm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ void MultiPeriodGroupAlgorithm::validateMultiPeriodGroupInputs(const size_t& nIn
const size_t multiPeriodGroupsSize = m_multiPeriodGroups.size();
if(multiPeriodGroupsSize != 0 && multiPeriodGroupsSize != nInputWorkspaces)
{
std::string msg = "MergeRuns can either process complete array of MatrixWorkspaces or Multi-period-groups, but mixing of types is not permitted.";
std::string msg = "MultiPeriodGroupAlgorithms can either process complete array of MatrixWorkspaces or Multi-period-groups, but mixing of types is not permitted.";
throw std::runtime_error(msg);
}

Expand All @@ -48,7 +48,7 @@ void MultiPeriodGroupAlgorithm::validateMultiPeriodGroupInputs(const size_t& nIn
{
MatrixWorkspace_const_sptr currentNestedWS = boost::dynamic_pointer_cast<const MatrixWorkspace>(currentGroup->getItem(j));
Property* nPeriodsProperty = currentNestedWS->run().getLogData("nperiods");
size_t nPeriods = atoi(nPeriodsProperty->value().c_str());
size_t nPeriods = atoi(nPeriodsProperty->value().c_str());
if(nPeriods != benchMarkGroupSize)
{
throw std::runtime_error("Missmatch between nperiods log and the number of workspaces in the input group: " + m_multiPeriodGroups[i]->name());
Expand Down Expand Up @@ -79,8 +79,16 @@ bool MultiPeriodGroupAlgorithm::checkGroups()
{
typedef std::vector<std::string> WorkspaceNameType;

// Perform a check that the input property is the correct type.
Property* inputProperty = this->getProperty(this->fetchInputPropertyName());
if(!dynamic_cast<ArrayProperty<std::string>*>(inputProperty))
{
throw std::runtime_error("Support for input workspaces that are not string Arrays are not currently supported.");
/*Note that we could extend this algorithm to cover other input property types if required, but we don't need that funtionality now.*/
}

m_multiPeriodGroups.clear();
WorkspaceNameType workspaces = this->getProperty("InputWorkspaces");
WorkspaceNameType workspaces = this->getProperty(this->fetchInputPropertyName());
WorkspaceNameType::iterator it = workspaces.begin();

// Inspect all the input workspaces in the ArrayProperty input.
Expand Down Expand Up @@ -127,7 +135,7 @@ This requires a little explanation, because this is the reason that this algorit
Say you have two multiperiod group workspaces A and B and an output workspace C. A contains matrix workspaces A_1 and A_2, and B contains matrix workspaces B_1 and B2. Because this
is multiperiod data. A_1 and B_1 share the same period, as do A_2 and B_2. So merging must be with respect to workspaces of equivalent periods. Therefore,
merging must be A_1 + B_1 = C_1 and A_2 + B_2 = C_2. This method constructs the inputs for a nested call to MergeRuns in this manner.
merging must be A_1 + B_1 = C_1 and A_2 + B_2 = C_2. This method constructs the inputs for a nested call to MultiPeriodGroupAlgorithm in this manner.
@param periodIndex : zero based index denoting the period.
@return comma separated string of input workspaces.
Expand Down Expand Up @@ -165,18 +173,20 @@ bool MultiPeriodGroupAlgorithm::processGroups()
{
return Algorithm::processGroups();
}
// Get the name of the property identified as the input workspace property.
Property* inputProperty = this->getProperty(this->fetchInputPropertyName());

Property* outputWorkspaceProperty = this->getProperty("OutputWorkspace");
const std::string outName = outputWorkspaceProperty->value();

size_t nPeriods = m_multiPeriodGroups[0]->size();
const bool doObserveADSNotifications = true;
WorkspaceGroup_sptr outputWS = boost::make_shared<WorkspaceGroup>(!doObserveADSNotifications);
// Loop through all the periods.
// Loop through all the periods. Create spawned algorithms of the same type as this to process pairs from the input groups.
for(size_t i = 0; i < nPeriods; ++i)
{
// Create a formatted input workspace list. As this is the usual input (ArrayProperty) to the MergeRuns algorithm.
const std::string inputWorkspaces = createFormattedInputWorkspaceNames(i);
// Create a formatted input workspace list for the spawned algorithm containing the names of the pairs of workspaces to merge. Note that this feature is setup for string array input workspace properties.
const std::string inputWorkspaces = createFormattedInputWorkspaceNames(i);

Algorithm_sptr alg_sptr = API::AlgorithmManager::Instance().createUnmanaged(this->name(), this->version());
IAlgorithm* alg = alg_sptr.get();
Expand All @@ -186,15 +196,30 @@ bool MultiPeriodGroupAlgorithm::processGroups()
throw std::runtime_error("Algorithm creation failed.");
}
alg->initialize();
alg->setPropertyValue("InputWorkspaces", inputWorkspaces);
// Copy all properties over except for input and output workspace properties.
std::vector<Property*> props = this->getProperties();
for (size_t i=0; i < props.size(); i++)
{
Property * prop = props[i];
if (prop)
{
if (prop != inputProperty)
{
this->setOtherProperties(alg, prop->name(), prop->value(), i+1);
}
}
}
// Set the input workspace property.
alg->setPropertyValue(this->fetchInputPropertyName(), inputWorkspaces);
// Create a name for the output workspace based upon the requested name for the overall output group workspace.
const std::string outName_i = outName + "_" + Strings::toString(i+1);
alg->setPropertyValue("OutputWorkspace", outName_i);

// Run the spawned algorithm.
if (!alg->execute())
{
throw std::runtime_error("Execution of " + this->name() + " for group entry " + Strings::toString(i+1) + " failed.");
}
// Add the output workpace from the spawned algorithm to the group.
outputWS->add(outName_i);
}
outputWS->observeADSNotifications(doObserveADSNotifications);
Expand Down
167 changes: 163 additions & 4 deletions Code/Mantid/Framework/API/test/MultiPeriodGroupAlgorithmTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,182 @@
#define MANTID_API_MultiPeriodGroupAlgorithmTEST_H_

#include <cxxtest/TestSuite.h>

#include "MantidAPI/MultiPeriodGroupAlgorithm.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidTestHelpers/FakeObjects.h"

using namespace Mantid::API;
using namespace Mantid::Kernel;

using Mantid::API::MultiPeriodGroupAlgorithm;
// ------------------------------------------------------------------
// Working, concrete MultiPeriodGroupAlgorithm
class TestAlgorithm : public MultiPeriodGroupAlgorithm
{
public:
TestAlgorithm(){}
virtual const std::string name() const {return "TestAlgorithm";}
virtual int version() const {return 1;}
virtual void init()
{
declareProperty(new ArrayProperty<std::string>("MyInputWorkspaces"));
declareProperty(new WorkspaceProperty<>("OutputWorkspace","",Direction::Output), "");
}
virtual void exec()
{
setProperty("OutputWorkspace", Workspace_sptr(new WorkspaceTester));
}
virtual std::string fetchInputPropertyName() const
{
return "MyInputWorkspaces";
}
virtual ~TestAlgorithm()
{
}
};
DECLARE_ALGORITHM( TestAlgorithm)
// ------------------------------------------------------------------
// End class dec

class MultiPeriodGroupAlgorithmTest : public CxxTest::TestSuite
{
private:

// Helper method to add multiperiod logs to make a workspacegroup look like a real multiperiod workspace group.
void add_periods_logs(WorkspaceGroup_sptr ws)
{
int nperiods = static_cast<int>(ws->size());
for(size_t i = 0; i < ws->size(); ++i)
{
MatrixWorkspace_sptr currentWS = boost::dynamic_pointer_cast<MatrixWorkspace>(ws->getItem(i));
PropertyWithValue<int>* nperiodsProp = new PropertyWithValue<int>("nperiods", nperiods);
currentWS->mutableRun().addLogData(nperiodsProp);
PropertyWithValue<int>* currentPeriodsProp = new PropertyWithValue<int>("current_period", static_cast<int>(i+1));
currentWS->mutableRun().addLogData(currentPeriodsProp);
}
}
/// Helper to fabricate a workspace group consisting of equal sized matrixworkspaces.
WorkspaceGroup_sptr create_good_multiperiod_workspace_group(const std::string name)
{
MatrixWorkspace_sptr a = MatrixWorkspace_sptr(new WorkspaceTester);
MatrixWorkspace_sptr b = MatrixWorkspace_sptr(new WorkspaceTester);
a->setName(name + "_1");
b->setName(name + "_2");
WorkspaceGroup_sptr group = boost::make_shared<WorkspaceGroup>();
group->setName(name);
group->addWorkspace(a);
group->addWorkspace(b);
add_periods_logs(group);
AnalysisDataService::Instance().addOrReplace(a->name(), a);
AnalysisDataService::Instance().addOrReplace(b->name(), b);
AnalysisDataService::Instance().addOrReplace(group->name(), group);
return group;
}

public:

// This pair of boilerplate methods prevent the suite being created statically
// This means the constructor isn't called when running other tests
static MultiPeriodGroupAlgorithmTest *createSuite() { return new MultiPeriodGroupAlgorithmTest(); }
static void destroySuite( MultiPeriodGroupAlgorithmTest *suite ) { delete suite; }

// Note that we may wish to retire this test if we support other input property types in the future.
void test_input_property_not_string_array_throws()
{
// ------------------------------------------------------------------
// Test Algorithm with input property that is not an array.
class BrokenAlgorithm : public MultiPeriodGroupAlgorithm
{
public:
virtual const std::string name() const {return "BrokenAlgorithm";}
virtual int version() const {return 1;}
virtual void init()
{
declareProperty(new WorkspaceProperty<WorkspaceGroup>("InputWorkspaces","",Direction::Input), "");
declareProperty(new WorkspaceProperty<>("OutputWorkspace","",Direction::Output), "");
}
virtual void exec()
{
setProperty("OutputWorkspace", Workspace_sptr(new WorkspaceTester));
}
virtual std::string fetchInputPropertyName() const
{
return "InputWorkspaces";
}
virtual ~BrokenAlgorithm()
{
}
};
// ------------------------------------------------------------------
// End class dec

WorkspaceGroup_sptr testInput = create_good_multiperiod_workspace_group("test");

BrokenAlgorithm alg;
alg.setRethrows(true);
alg.initialize();
alg.setProperty("InputWorkspaces", testInput);
alg.setPropertyValue("OutputWorkspace", "outWS");
TSM_ASSERT_THROWS("Should throw because fetchInputPropertyName is returning the name of a property which doesn't exist.", alg.execute(), std::runtime_error);
}


void test_input_property_doesnt_exist_throws()
{
// ------------------------------------------------------------------
// Test Algorithm with fetchInputPropertyName incorrectly wired-up.
class BrokenAlgorithm : public MultiPeriodGroupAlgorithm
{
public:
virtual const std::string name() const {return "BrokenAlgorithm";}
virtual int version() const {return 1;}
virtual void init()
{
declareProperty(new ArrayProperty<std::string>("InputWorkspaces"));
declareProperty(new WorkspaceProperty<>("OutputWorkspace","",Direction::Output), "Name of the output workspace" );
}
virtual void exec()
{
setProperty("OutputWorkspace", Workspace_sptr(new WorkspaceTester));
}
virtual std::string fetchInputPropertyName() const
{
return "made_up_property_name";
}
virtual ~BrokenAlgorithm()
{
}
};
// ------------------------------------------------------------------
// End class dec

WorkspaceGroup_sptr a = create_good_multiperiod_workspace_group("a");
WorkspaceGroup_sptr b = create_good_multiperiod_workspace_group("b");

void test_Something()
BrokenAlgorithm alg;
alg.setRethrows(true);
alg.initialize();
alg.setPropertyValue("InputWorkspaces", "a, b");
alg.setPropertyValue("OutputWorkspace", "outWS");
TSM_ASSERT_THROWS("Should throw because fetchInputPropertyName is returning the name of a property which doesn't exist.", alg.execute(), Kernel::Exception::NotFoundError);
}

void test_process_groups()
{
// TSM_ASSERT( "You forgot to write a test!", 0);
WorkspaceGroup_sptr a = create_good_multiperiod_workspace_group("a");
WorkspaceGroup_sptr b = create_good_multiperiod_workspace_group("b");
WorkspaceGroup_sptr c = create_good_multiperiod_workspace_group("c");

TestAlgorithm alg;
alg.setRethrows(true);
alg.initialize();
alg.setPropertyValue("MyInputWorkspaces", "a, b, c");
alg.setPropertyValue("OutputWorkspace", "outWS");
TS_ASSERT_THROWS_NOTHING(alg.execute());
TS_ASSERT(alg.isExecuted());

WorkspaceGroup_sptr wsgroup = Mantid::API::AnalysisDataService::Instance().retrieveWS<WorkspaceGroup>("outWS");
TS_ASSERT(wsgroup != NULL);
TS_ASSERT_EQUALS(a->size(), wsgroup->size());
}


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ class DLLExport MergeRuns : public API::MultiPeriodGroupAlgorithm
void exec();
void execEvent();
void buildAdditionTables();

// Overriden MultiPeriodGroupAlgorithm method.
std::string fetchInputPropertyName() const;

/// An addition table is a list of pairs: First int = workspace index in the EW being added, Second int = workspace index to which it will be added in the OUTPUT EW. -1 if it should add a new entry at the end.
typedef std::vector< std::pair<int, int> > AdditionTable;
Expand Down
7 changes: 7 additions & 0 deletions Code/Mantid/Framework/Algorithms/src/MergeRuns.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,13 @@ void MergeRuns::init()
"Name of the output workspace" );
}

//------------------------------------------------------------------------------------------------
// @return the name of the property used to supply in input workspace(s).
std::string MergeRuns::fetchInputPropertyName() const
{
return "InputWorkspaces";
}

//------------------------------------------------------------------------------------------------
/** Executes the algorithm
* @throw Exception::NotFoundError If an input workspace doesn't exist
Expand Down
8 changes: 7 additions & 1 deletion Code/Mantid/Framework/Algorithms/test/MergeRunsTest.h
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ class MergeRunsTest : public CxxTest::TestSuite
do_test_validation_throws(aCorrupted, a);
}

void test_with_multiperiod_data(WorkspaceGroup_sptr input)
void do_test_with_multiperiod_data(WorkspaceGroup_sptr input)
{
// Extract some internal information from the nested workspaces in order to run test asserts later.
const size_t expectedNumHistograms = boost::dynamic_pointer_cast<MatrixWorkspace>(input->getItem(0))->getNumberHistograms();
Expand Down Expand Up @@ -771,6 +771,12 @@ class MergeRunsTest : public CxxTest::TestSuite
do_test_treat_as_non_period_groups(input);
}

void test_with_multiperiod_data()
{
WorkspaceGroup_sptr input = create_good_multiperiod_workspace_group();
do_test_with_multiperiod_data(input);
}

private:
MergeRuns merge;
};
Expand Down

0 comments on commit 12b36a5

Please sign in to comment.