diff --git a/Code/Mantid/Framework/CurveFitting/CMakeLists.txt b/Code/Mantid/Framework/CurveFitting/CMakeLists.txt
index 7d6e399fe93b..c60bcf76181e 100644
--- a/Code/Mantid/Framework/CurveFitting/CMakeLists.txt
+++ b/Code/Mantid/Framework/CurveFitting/CMakeLists.txt
@@ -54,6 +54,7 @@ set ( SRC_FILES
src/GaussianComptonProfile.cpp
src/GramCharlierComptonProfile.cpp
src/IkedaCarpenterPV.cpp
+ src/LatticeDomainCreator.cpp
src/LatticeFunction.cpp
src/LeBailFit.cpp
src/LeBailFunction.cpp
@@ -171,6 +172,7 @@ set ( INC_FILES
inc/MantidCurveFitting/GramCharlierComptonProfile.h
inc/MantidCurveFitting/IkedaCarpenterPV.h
inc/MantidCurveFitting/Jacobian.h
+ inc/MantidCurveFitting/LatticeDomainCreator.h
inc/MantidCurveFitting/LatticeFunction.h
inc/MantidCurveFitting/LeBailFit.h
inc/MantidCurveFitting/LeBailFunction.h
@@ -279,6 +281,7 @@ set ( TEST_FILES
IPeakFunctionCentreParameterNameTest.h
IPeakFunctionIntensityTest.h
IkedaCarpenterPVTest.h
+ LatticeDomainCreatorTest.h
LatticeFunctionTest.h
LeBailFitTest.h
LeBailFunctionTest.h
diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/LatticeDomainCreator.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/LatticeDomainCreator.h
new file mode 100644
index 000000000000..290a8347fd0e
--- /dev/null
+++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/LatticeDomainCreator.h
@@ -0,0 +1,81 @@
+#ifndef MANTID_CURVEFITTING_LATTICEDOMAINCREATOR_H_
+#define MANTID_CURVEFITTING_LATTICEDOMAINCREATOR_H_
+
+#include "MantidKernel/System.h"
+#include "MantidAPI/IDomainCreator.h"
+#include "MantidAPI/Workspace.h"
+#include "MantidAPI/IPeaksWorkspace.h"
+
+namespace Mantid {
+namespace CurveFitting {
+
+/** LatticeDomainCreator
+
+ Domain creator for LatticeDomain, which processes IPeaksWorkspace or
+ an ITableWorkspace with certain columns (HKL, d).
+
+ @author Michael Wedel, Paul Scherrer Institut - SINQ
+ @date 15/04/2015
+
+ Copyright © 2015 PSI-NXMM
+
+ 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 .
+
+ File change history is stored at:
+ Code Documentation is available at:
+*/
+class DLLExport LatticeDomainCreator : public API::IDomainCreator {
+public:
+ LatticeDomainCreator(Kernel::IPropertyManager *manager,
+ const std::string &workspacePropertyName,
+ DomainType domainType = Simple);
+
+ virtual ~LatticeDomainCreator() {}
+
+ virtual void createDomain(boost::shared_ptr &domain,
+ boost::shared_ptr &values,
+ size_t i0);
+
+ virtual API::Workspace_sptr createOutputWorkspace(
+ const std::string &baseName,
+ API::IFunction_sptr function,
+ boost::shared_ptr domain,
+ boost::shared_ptr values,
+ const std::string &outputWorkspacePropertyName);
+
+ virtual size_t getDomainSize() const;
+
+protected:
+ void setWorkspaceFromPropertyManager();
+ void
+ createDomainFromPeaksWorkspace(const API::IPeaksWorkspace_sptr &workspace,
+ boost::shared_ptr &domain,
+ boost::shared_ptr &values,
+ size_t i0);
+
+ void createDomainFromPeakTable(const API::ITableWorkspace_sptr &workspace,
+ boost::shared_ptr &domain,
+ boost::shared_ptr &values,
+ size_t i0);
+
+ std::string m_workspacePropertyName;
+ API::Workspace_sptr m_workspace;
+};
+
+} // namespace CurveFitting
+} // namespace Mantid
+
+#endif /* MANTID_CURVEFITTING_LATTICEDOMAINCREATOR_H_ */
diff --git a/Code/Mantid/Framework/CurveFitting/src/LatticeDomainCreator.cpp b/Code/Mantid/Framework/CurveFitting/src/LatticeDomainCreator.cpp
new file mode 100644
index 000000000000..35ed74b6ac65
--- /dev/null
+++ b/Code/Mantid/Framework/CurveFitting/src/LatticeDomainCreator.cpp
@@ -0,0 +1,221 @@
+#include "MantidCurveFitting/LatticeDomainCreator.h"
+
+#include "MantidAPI/IPeak.h"
+#include "MantidAPI/LatticeDomain.h"
+#include "MantidAPI/TableRow.h"
+#include "MantidAPI/WorkspaceFactory.h"
+
+#include "MantidDataObjects/Peak.h"
+#include "MantidCurveFitting/PawleyFit.h"
+
+namespace Mantid {
+namespace CurveFitting {
+
+using namespace API;
+using namespace Kernel;
+using namespace DataObjects;
+
+LatticeDomainCreator::LatticeDomainCreator(
+ Kernel::IPropertyManager *manager, const std::string &workspacePropertyName,
+ API::IDomainCreator::DomainType domainType)
+ : IDomainCreator(manager,
+ std::vector(1, workspacePropertyName),
+ domainType),
+ m_workspace() {
+ m_workspacePropertyName = m_workspacePropertyNames.front();
+}
+
+void LatticeDomainCreator::createDomain(
+ boost::shared_ptr &domain,
+ boost::shared_ptr &values, size_t i0) {
+ setWorkspaceFromPropertyManager();
+
+ API::IPeaksWorkspace_sptr peaksWorkspace =
+ boost::dynamic_pointer_cast(m_workspace);
+ if (peaksWorkspace) {
+ createDomainFromPeaksWorkspace(peaksWorkspace, domain, values, i0);
+ } else {
+ API::ITableWorkspace_sptr tableWorkspace =
+ boost::dynamic_pointer_cast(m_workspace);
+ if (tableWorkspace) {
+ createDomainFromPeakTable(tableWorkspace, domain, values, i0);
+ }
+ }
+}
+
+Workspace_sptr LatticeDomainCreator::createOutputWorkspace(
+ const std::string &baseName, IFunction_sptr function,
+ boost::shared_ptr domain,
+ boost::shared_ptr values,
+ const std::string &outputWorkspacePropertyName) {
+ boost::shared_ptr latticeDomain =
+ boost::dynamic_pointer_cast(domain);
+
+ if (!latticeDomain) {
+ throw std::invalid_argument("LatticeDomain is required.");
+ }
+
+ ITableWorkspace_sptr tableWorkspace =
+ WorkspaceFactory::Instance().createTable();
+
+ if (tableWorkspace) {
+ tableWorkspace->addColumn("V3D", "HKL");
+ tableWorkspace->addColumn("double", "d(obs)");
+ tableWorkspace->addColumn("double", "d(calc)");
+ tableWorkspace->addColumn("double", "d(obs) - d(calc)");
+
+ for (size_t i = 0; i < values->size(); ++i) {
+ double dObs = values->getFitData(i);
+ double dCalc = values->getCalculated(i);
+
+ TableRow newRow = tableWorkspace->appendRow();
+ newRow << (*latticeDomain)[i] << dObs << dCalc << dObs - dCalc;
+ }
+ }
+
+ if (m_manager && !outputWorkspacePropertyName.empty()) {
+ declareProperty(
+ new WorkspaceProperty(outputWorkspacePropertyName, "",
+ Kernel::Direction::Output),
+ "Result workspace");
+
+ m_manager->setPropertyValue(outputWorkspacePropertyName,
+ baseName + "Workspace");
+ m_manager->setProperty(outputWorkspacePropertyName, tableWorkspace);
+ }
+
+ return tableWorkspace;
+}
+
+size_t LatticeDomainCreator::getDomainSize() const {
+ API::IPeaksWorkspace_sptr peaksWorkspace =
+ boost::dynamic_pointer_cast(m_workspace);
+ if (peaksWorkspace) {
+ return peaksWorkspace->getNumberPeaks();
+ }
+
+ API::ITableWorkspace_sptr tableWorkspace =
+ boost::dynamic_pointer_cast(m_workspace);
+ if (tableWorkspace) {
+ return tableWorkspace->rowCount();
+ }
+
+ return 0;
+}
+
+void LatticeDomainCreator::setWorkspaceFromPropertyManager() {
+ if (!m_manager) {
+ throw std::invalid_argument(
+ "PropertyManager not set in LatticeDomainCreator.");
+ }
+
+ try {
+ m_workspace = m_manager->getProperty(m_workspacePropertyName);
+ }
+ catch (Kernel::Exception::NotFoundError) {
+ throw std::invalid_argument(
+ "Could not extract workspace from PropertyManager.");
+ }
+}
+
+void LatticeDomainCreator::createDomainFromPeaksWorkspace(
+ const API::IPeaksWorkspace_sptr &workspace,
+ boost::shared_ptr &domain,
+ boost::shared_ptr &values, size_t i0) {
+ if (!workspace) {
+ throw std::invalid_argument(
+ "This function only works on an IPeaksWorkspace-object.");
+ }
+
+ size_t peakCount = workspace->getNumberPeaks();
+
+ if (peakCount < 1) {
+ throw std::range_error("Cannot create a domain for 0 peaks.");
+ }
+
+ std::vector hkls;
+ hkls.reserve(peakCount);
+
+ std::vector dSpacings;
+ dSpacings.reserve(peakCount);
+
+ for (size_t i = 0; i < peakCount; ++i) {
+ IPeak *currentPeak = workspace->getPeakPtr(static_cast(i));
+ hkls.push_back(currentPeak->getHKL());
+ dSpacings.push_back(currentPeak->getDSpacing());
+ }
+
+ LatticeDomain *latticeDomain = new LatticeDomain(hkls);
+ domain.reset(latticeDomain);
+
+ if (!values) {
+ FunctionValues *functionValues = new FunctionValues(*domain);
+ values.reset(functionValues);
+ } else {
+ values->expand(i0 + latticeDomain->size());
+ }
+
+ values->setFitData(dSpacings);
+
+ // Set unit weights.
+ values->setFitWeights(1.0);
+}
+
+void LatticeDomainCreator::createDomainFromPeakTable(
+ const ITableWorkspace_sptr &workspace,
+ boost::shared_ptr &domain,
+ boost::shared_ptr &values, size_t i0) {
+
+ size_t peakCount = workspace->rowCount();
+
+ if (peakCount < 1) {
+ throw std::range_error("Cannot create a domain for 0 peaks.");
+ }
+
+ try {
+ Column_const_sptr hklColumn = workspace->getColumn("HKL");
+ Column_const_sptr dColumn = workspace->getColumn("d");
+
+ std::vector hkls;
+ hkls.reserve(peakCount);
+
+ std::vector dSpacings;
+ dSpacings.reserve(peakCount);
+
+ V3DFromHKLColumnExtractor extractor;
+
+ for (size_t i = 0; i < peakCount; ++i) {
+ try {
+ hkls.push_back(extractor(hklColumn, i));
+
+ double d = (*dColumn)[i];
+ dSpacings.push_back(d);
+ }
+ catch (std::bad_alloc) {
+ // do nothing.
+ }
+ }
+
+ LatticeDomain *latticeDomain = new LatticeDomain(hkls);
+ domain.reset(latticeDomain);
+
+ if (!values) {
+ FunctionValues *functionValues = new FunctionValues(*domain);
+ values.reset(functionValues);
+ } else {
+ values->expand(i0 + latticeDomain->size());
+ }
+
+ values->setFitData(dSpacings);
+
+ values->setFitWeights(1.0);
+ }
+ catch (std::runtime_error) {
+ // Column does not exist
+ throw std::runtime_error("Can not process table, the following columns are "
+ "required: HKL, d.");
+ }
+}
+
+} // namespace CurveFitting
+} // namespace Mantid
diff --git a/Code/Mantid/Framework/CurveFitting/test/LatticeDomainCreatorTest.h b/Code/Mantid/Framework/CurveFitting/test/LatticeDomainCreatorTest.h
new file mode 100644
index 000000000000..c1ae02b191ad
--- /dev/null
+++ b/Code/Mantid/Framework/CurveFitting/test/LatticeDomainCreatorTest.h
@@ -0,0 +1,202 @@
+#ifndef MANTID_CURVEFITTING_LATTICEDOMAINCREATORTEST_H_
+#define MANTID_CURVEFITTING_LATTICEDOMAINCREATORTEST_H_
+
+#include
+
+#include "MantidAPI/LatticeDomain.h"
+#include "MantidCurveFitting/LatticeDomainCreator.h"
+#include "MantidDataObjects/PeaksWorkspace.h"
+#include "MantidTestHelpers/WorkspaceCreationHelper.h"
+
+using Mantid::CurveFitting::LatticeDomainCreator;
+using Mantid::Kernel::V3D;
+
+using namespace Mantid::API;
+using namespace Mantid::DataObjects;
+
+class LatticeDomainCreatorTest : 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 LatticeDomainCreatorTest *createSuite() {
+ return new LatticeDomainCreatorTest();
+ }
+ static void destroySuite(LatticeDomainCreatorTest *suite) { delete suite; }
+
+ void testGetDomainSizeIPeaksWorkspace() {
+ Workspace_sptr peaksWs = boost::dynamic_pointer_cast(
+ WorkspaceCreationHelper::createPeaksWorkspace(5));
+
+ TestableLatticeDomainCreator dc;
+ TS_ASSERT_THROWS_NOTHING(dc.setWorkspace(peaksWs));
+
+ TS_ASSERT_EQUALS(dc.getDomainSize(), 5);
+ }
+
+ void testGetDomainSizeTableWorkspace() {
+ ITableWorkspace_sptr table = getValidTableWs();
+
+ TestableLatticeDomainCreator dc;
+ TS_ASSERT_THROWS_NOTHING(dc.setWorkspace(table));
+
+ TS_ASSERT_EQUALS(dc.getDomainSize(), 3);
+ }
+
+ void testCreateDomainTableWs() {
+ ITableWorkspace_sptr table = getValidTableWs();
+
+ TestableLatticeDomainCreator dc;
+
+ FunctionDomain_sptr domain;
+ FunctionValues_sptr values;
+
+ TS_ASSERT_THROWS_NOTHING(
+ dc.createDomainFromPeakTable(table, domain, values, 0));
+
+ TS_ASSERT_EQUALS(domain->size(), 3);
+ TS_ASSERT_EQUALS(values->size(), 3);
+
+ boost::shared_ptr latticeDomain =
+ boost::dynamic_pointer_cast(domain);
+
+ TS_ASSERT(latticeDomain);
+
+ TS_ASSERT_EQUALS((*latticeDomain)[0], V3D(1, 1, 1));
+ TS_ASSERT_EQUALS((*latticeDomain)[1], V3D(2, 2, 0));
+ TS_ASSERT_EQUALS((*latticeDomain)[2], V3D(3, 1, 1));
+
+ TS_ASSERT_EQUALS(values->getFitData(0), 3.135702);
+ TS_ASSERT_EQUALS(values->getFitData(1), 1.920217);
+ TS_ASSERT_EQUALS(values->getFitData(2), 1.637567);
+
+ TS_ASSERT_EQUALS(values->getFitWeight(0), 1.0);
+ TS_ASSERT_EQUALS(values->getFitWeight(1), 1.0);
+ TS_ASSERT_EQUALS(values->getFitWeight(2), 1.0);
+ }
+
+ void testCreateDomainTableWsInvalid() {
+ ITableWorkspace_sptr invalid = getInvalidTableWs();
+ ITableWorkspace_sptr empty = getEmptyTableWs();
+
+ TestableLatticeDomainCreator dc;
+
+ FunctionDomain_sptr domain;
+ FunctionValues_sptr values;
+
+ TS_ASSERT_THROWS(dc.createDomainFromPeakTable(invalid, domain, values, 0),
+ std::runtime_error);
+
+ TS_ASSERT_THROWS(dc.createDomainFromPeakTable(empty, domain, values, 0),
+ std::range_error);
+ }
+
+ void testCreateDomainPeaksWorkspace() {
+ IPeaksWorkspace_sptr peaksWs = boost::dynamic_pointer_cast(
+ WorkspaceCreationHelper::createPeaksWorkspace(2));
+ peaksWs->getPeak(0).setHKL(V3D(1, 1, 1));
+ peaksWs->getPeak(1).setHKL(V3D(2, 2, 0));
+
+ TestableLatticeDomainCreator dc;
+
+ FunctionDomain_sptr domain;
+ FunctionValues_sptr values;
+
+ TS_ASSERT_THROWS_NOTHING(
+ dc.createDomainFromPeaksWorkspace(peaksWs, domain, values, 0));
+
+ TS_ASSERT_EQUALS(domain->size(), 2);
+ TS_ASSERT_EQUALS(values->size(), 2);
+
+ boost::shared_ptr latticeDomain =
+ boost::dynamic_pointer_cast(domain);
+
+ TS_ASSERT(latticeDomain);
+
+ TS_ASSERT_EQUALS((*latticeDomain)[0], V3D(1, 1, 1));
+ TS_ASSERT_EQUALS((*latticeDomain)[1], V3D(2, 2, 0));
+
+ TS_ASSERT_EQUALS(values->getFitData(0), peaksWs->getPeak(0).getDSpacing());
+ TS_ASSERT_EQUALS(values->getFitData(1), peaksWs->getPeak(1).getDSpacing());
+
+ TS_ASSERT_EQUALS(values->getFitWeight(0), 1.0);
+ TS_ASSERT_EQUALS(values->getFitWeight(1), 1.0);
+ }
+
+ void testCreateOutputWorkspace() {
+ ITableWorkspace_sptr table = getValidTableWs();
+
+ IFunction_sptr fn;
+ FunctionDomain_sptr domain;
+ FunctionValues_sptr values;
+
+ // Make domain and values
+ TestableLatticeDomainCreator dc;
+ dc.createDomainFromPeakTable(table, domain, values, 0);
+
+ values->setCalculated(0, 3.125702);
+ values->setCalculated(1, 1.930217);
+ values->setCalculated(2, 1.627567);
+
+ Workspace_sptr outputWs;
+ TS_ASSERT_THROWS_NOTHING(
+ outputWs = dc.createOutputWorkspace("base", fn, domain, values, "pp"));
+
+ ITableWorkspace_sptr tableWs =
+ boost::dynamic_pointer_cast(outputWs);
+ TS_ASSERT(tableWs);
+
+ TS_ASSERT_EQUALS(tableWs->rowCount(), 3);
+ TS_ASSERT_DELTA(tableWs->cell(0, 3), 0.01, 1e-6);
+ TS_ASSERT_DELTA(tableWs->cell(1, 3), -0.01, 1e-6);
+ TS_ASSERT_DELTA(tableWs->cell(2, 3), 0.01, 1e-6);
+ }
+
+private:
+ ITableWorkspace_sptr getValidTableWs() {
+ ITableWorkspace_sptr table = WorkspaceFactory::Instance().createTable();
+ table->addColumn("V3D", "HKL");
+ table->addColumn("double", "d");
+
+ TableRow newRow = table->appendRow();
+ newRow << V3D(1, 1, 1) << 3.135702;
+ newRow = table->appendRow();
+ newRow << V3D(2, 2, 0) << 1.920217;
+ newRow = table->appendRow();
+ newRow << V3D(3, 1, 1) << 1.637567;
+
+ return table;
+ }
+
+ ITableWorkspace_sptr getEmptyTableWs() {
+ ITableWorkspace_sptr table = WorkspaceFactory::Instance().createTable();
+ table->addColumn("V3D", "HKL");
+ table->addColumn("double", "d");
+
+ return table;
+ }
+
+ ITableWorkspace_sptr getInvalidTableWs() {
+ ITableWorkspace_sptr table = WorkspaceFactory::Instance().createTable();
+ table->addColumn("V3D", "HKL");
+
+ TableRow newRow = table->appendRow();
+ newRow << V3D(1, 1, 1);
+ newRow = table->appendRow();
+ newRow << V3D(2, 2, 0);
+ newRow = table->appendRow();
+ newRow << V3D(3, 1, 1);
+
+ return table;
+ }
+
+ class TestableLatticeDomainCreator : public LatticeDomainCreator {
+ friend class LatticeDomainCreatorTest;
+
+ public:
+ TestableLatticeDomainCreator() : LatticeDomainCreator(NULL, "") {}
+
+ void setWorkspace(const Workspace_sptr &ws) { m_workspace = ws; }
+ };
+};
+
+#endif /* MANTID_CURVEFITTING_LATTICEDOMAINCREATORTEST_H_ */