From 3b2e6faedaac9821e16d214009ca321b12b61a80 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Fri, 3 Oct 2014 17:18:29 +0200 Subject: [PATCH 01/48] Refs #10281. Added Group and CyclicGroup --- Code/Mantid/Framework/Geometry/CMakeLists.txt | 6 + .../inc/MantidGeometry/Crystal/CyclicGroup.h | 63 +++++++ .../inc/MantidGeometry/Crystal/Group.h | 85 +++++++++ .../Geometry/src/Crystal/CyclicGroup.cpp | 35 ++++ .../Framework/Geometry/src/Crystal/Group.cpp | 121 ++++++++++++ .../Framework/Geometry/test/CyclicGroupTest.h | 101 ++++++++++ .../Framework/Geometry/test/GroupTest.h | 177 ++++++++++++++++++ 7 files changed, 588 insertions(+) create mode 100644 Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h create mode 100644 Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h create mode 100644 Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp create mode 100644 Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp create mode 100644 Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h create mode 100644 Code/Mantid/Framework/Geometry/test/GroupTest.h diff --git a/Code/Mantid/Framework/Geometry/CMakeLists.txt b/Code/Mantid/Framework/Geometry/CMakeLists.txt index 8cc9ac1578df..8d9a6e1fab06 100644 --- a/Code/Mantid/Framework/Geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/Geometry/CMakeLists.txt @@ -2,6 +2,8 @@ set ( SRC_FILES src/ComponentParser.cpp src/Crystal/ConventionalCell.cpp src/Crystal/CrystalStructure.cpp + src/Crystal/CyclicGroup.cpp + src/Crystal/Group.cpp src/Crystal/IndexingUtils.cpp src/Crystal/NiggliCell.cpp src/Crystal/OrientedLattice.cpp @@ -107,6 +109,8 @@ set ( INC_FILES inc/MantidGeometry/ComponentParser.h inc/MantidGeometry/Crystal/ConventionalCell.h inc/MantidGeometry/Crystal/CrystalStructure.h + inc/MantidGeometry/Crystal/CyclicGroup.h + inc/MantidGeometry/Crystal/Group.h inc/MantidGeometry/Crystal/IndexingUtils.h inc/MantidGeometry/Crystal/NiggliCell.h inc/MantidGeometry/Crystal/OrientedLattice.h @@ -215,12 +219,14 @@ set ( TEST_FILES ConventionalCellTest.h ConvexPolygonTest.h CrystalStructureTest.h + CyclicGroupTest.h CylinderTest.h DetectorGroupTest.h DetectorTest.h FitParameterTest.h GeneralTest.h GoniometerTest.h + GroupTest.h IDFObjectTest.h IMDDimensionFactoryTest.h IMDDimensionTest.h diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h new file mode 100644 index 000000000000..e75c0a86cf4f --- /dev/null +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h @@ -0,0 +1,63 @@ +#ifndef MANTID_GEOMETRY_CYCLICGROUP_H_ +#define MANTID_GEOMETRY_CYCLICGROUP_H_ + +#include "MantidGeometry/DllConfig.h" +#include "MantidGeometry/Crystal/Group.h" + +#include +#include + +namespace Mantid +{ +namespace Geometry +{ + +/** CyclicGroup : + + A class that represents a cyclic group of symmetry operations. + It just constructs a Group by multiplying itself "order" times. + + @author Michael Wedel, Paul Scherrer Institut - SINQ + @date 03/10/2014 + + Copyright © 2014 PSI-MSS + + 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 MANTID_GEOMETRY_DLL CyclicGroup : public Group +{ +public: + CyclicGroup(const SymmetryOperation &symmetryOperation); + virtual ~CyclicGroup() { } + + static Group_const_sptr create(const std::string &symmetryOperation); + +protected: + std::vector generateAllOperations(const SymmetryOperation &operation) const; + +}; + +typedef boost::shared_ptr CyclicGroup_sptr; +typedef boost::shared_ptr CyclicGroup_const_sptr; + + +} // namespace Geometry +} // namespace Mantid + +#endif /* MANTID_GEOMETRY_CYCLICGROUP_H_ */ diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h new file mode 100644 index 000000000000..d09ca06b77ae --- /dev/null +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h @@ -0,0 +1,85 @@ +#ifndef MANTID_GEOMETRY_GROUP_H_ +#define MANTID_GEOMETRY_GROUP_H_ + +#include "MantidGeometry/DllConfig.h" +#include "MantidGeometry/Crystal/SymmetryOperation.h" + +#include +#include + +#include + +namespace Mantid +{ +namespace Geometry +{ + +/** Group : + + A class representing a group of symmetry operations. + + @author Michael Wedel, Paul Scherrer Institut - SINQ + @date 03/10/2014 + + Copyright © 2014 PSI-MSS + + 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 MANTID_GEOMETRY_DLL Group +{ +public: + Group(); + Group(const std::vector &symmetryOperations); + Group(const Group &other); + Group &operator =(const Group &other); + + virtual ~Group() { } + + size_t order() const; + std::vector getSymmetryOperations() const; + + Group operator *(const Group &other) const; + + std::vector operator *(const Kernel::V3D &vector) const; + + bool operator ==(const Group &other) const; + bool operator !=(const Group &other) const; + + static int m_numOps; + +protected: + void setSymmetryOperations(const std::vector &symmetryOperations); + + std::vector m_allOperations; + std::set m_operationSet; +}; + +typedef boost::shared_ptr Group_sptr; +typedef boost::shared_ptr Group_const_sptr; + +MANTID_GEOMETRY_DLL Group_const_sptr operator *(const Group_const_sptr &lhs, const Group_const_sptr &rhs); +MANTID_GEOMETRY_DLL std::vector operator *(const Group_const_sptr &lhs, const Kernel::V3D &rhs); +MANTID_GEOMETRY_DLL bool operator ==(const Group_const_sptr &lhs, const Group_const_sptr &rhs); +MANTID_GEOMETRY_DLL bool operator !=(const Group_const_sptr &lhs, const Group_const_sptr &rhs); + + +} // namespace Geometry +} // namespace Mantid + +#endif /* MANTID_GEOMETRY_GROUP_H_ */ diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp new file mode 100644 index 000000000000..74909f36dc66 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp @@ -0,0 +1,35 @@ +#include "MantidGeometry/Crystal/CyclicGroup.h" +#include "MantidGeometry/Crystal/SymmetryOperationFactory.h" +#include + +namespace Mantid +{ +namespace Geometry +{ + +/// Construct cyclic group from one symmetry operation by applying it to itself until identity is obtained. +CyclicGroup::CyclicGroup(const SymmetryOperation &symmetryOperation) : + Group(generateAllOperations(symmetryOperation)) +{ +} + +Group_const_sptr CyclicGroup::create(const std::string &symmetryOperation) +{ + return boost::make_shared(SymmetryOperationFactory::Instance().createSymOp(symmetryOperation)); +} + +std::vector CyclicGroup::generateAllOperations(const SymmetryOperation &operation) const +{ + std::vector symOps(1, operation); + + for(size_t i = 1; i < operation.order(); ++i) { + symOps.push_back(operation * symOps.back()); + } + + return symOps; +} + + + +} // namespace Geometry +} // namespace Mantid diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp new file mode 100644 index 000000000000..5b5fcc7aab25 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp @@ -0,0 +1,121 @@ +#include "MantidGeometry/Crystal/Group.h" +#include + +namespace Mantid +{ +namespace Geometry +{ + +int Group::m_numOps = 0; + +/// Construct a group group from a given set of operations. This is +Group::Group() : + m_allOperations(), + m_operationSet() +{ + std::vector operation(1); + setSymmetryOperations(operation); +} + +Group::Group(const std::vector &symmetryOperations) : + m_allOperations(), + m_operationSet() +{ + setSymmetryOperations(symmetryOperations); +} + +Group::Group(const Group &other) : + m_allOperations(other.m_allOperations), + m_operationSet(other.m_operationSet) +{ + +} + +Group &Group::operator =(const Group &other) +{ + m_allOperations = other.m_allOperations; + m_operationSet = other.m_operationSet; + + return *this; +} + +size_t Group::order() const +{ + return m_allOperations.size(); +} + +std::vector Group::getSymmetryOperations() const +{ + return m_allOperations; +} + +Group Group::operator *(const Group &other) const +{ + std::vector result; + result.reserve(order() * other.order()); + + for(auto selfOp = m_allOperations.begin(); selfOp != m_allOperations.end(); ++selfOp) { + for(auto otherOp = other.m_allOperations.begin(); otherOp != other.m_allOperations.end(); ++otherOp) { + result.push_back((*selfOp) * (*otherOp)); + ++m_numOps; + } + } + + return Group(result); +} + +std::vector Group::operator *(const Kernel::V3D &vector) const +{ + std::set result; + + for(auto op = m_allOperations.begin(); op != m_allOperations.end(); ++op) { + result.insert(Geometry::getWrappedVector((*op) * vector)); + } + + return std::vector(result.begin(), result.end()); +} + +bool Group::operator ==(const Group &other) const +{ + return m_operationSet == other.m_operationSet; +} + +bool Group::operator !=(const Group &other) const +{ + return !(this->operator ==(other)); +} + +void Group::setSymmetryOperations(const std::vector &symmetryOperations) +{ + if(symmetryOperations.size() < 1) { + throw std::invalid_argument("Group needs at least one element."); + } + + m_operationSet = std::set(symmetryOperations.begin(), symmetryOperations.end()); + m_allOperations = std::vector(m_operationSet.begin(), m_operationSet.end()); +} + +Group_const_sptr operator *(const Group_const_sptr &lhs, const Group_const_sptr &rhs) +{ + return boost::make_shared((*lhs) * (*rhs)); +} + +std::vector operator *(const Group_const_sptr &lhs, const Kernel::V3D &rhs) +{ + return (*lhs) * rhs; +} + + +bool operator ==(const Group_const_sptr &lhs, const Group_const_sptr &rhs) +{ + return (*lhs) == (*rhs); +} + +bool operator !=(const Group_const_sptr &lhs, const Group_const_sptr &rhs) +{ + return !(operator ==(lhs, rhs)); +} + + +} // namespace Geometry +} // namespace Mantid diff --git a/Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h b/Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h new file mode 100644 index 000000000000..4697ff5b6427 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h @@ -0,0 +1,101 @@ +#ifndef MANTID_GEOMETRY_CYCLICGROUPTEST_H_ +#define MANTID_GEOMETRY_CYCLICGROUPTEST_H_ + +#include + +#include "MantidGeometry/Crystal/CyclicGroup.h" +#include "MantidGeometry/Crystal/SymmetryOperationFactory.h" + +#include + +using namespace Mantid::Geometry; +using namespace Mantid::Kernel; + +class CyclicGroupTest : 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 CyclicGroupTest *createSuite() { return new CyclicGroupTest(); } + static void destroySuite( CyclicGroupTest *suite ) { delete suite; } + + + void testConstructor() + { + CyclicGroup_const_sptr group = boost::make_shared(SymmetryOperationFactory::Instance().createSymOp("-x,-y,-z")); + + TS_ASSERT_EQUALS(group->order(), 2); + } + + void testCreate() + { + Group_const_sptr group = CyclicGroup::create("-x,-y,-z"); + TS_ASSERT(boost::dynamic_pointer_cast(group)); + + TS_ASSERT_EQUALS(group->order(), 2); + } + + void testMultiplication() + { + /* Even though this is part of Group, it's a good example and basically + * the method used to generate point groups. + */ + + Group_const_sptr groupOne = CyclicGroup::create("-x,-y,z"); + Group_const_sptr groupTwo = CyclicGroup::create("x,-y,-z"); + + // This is in fact point group 222 + Group_const_sptr groupThree = groupOne * groupTwo; + + TS_ASSERT_EQUALS(groupThree->order(), 4); + + Group_const_sptr groupFour = CyclicGroup::create("-x,-y,-z"); + + // Which becomes mmm, if inversion is added. + Group_const_sptr groupFive = groupFour * groupThree; + TS_ASSERT_EQUALS(groupFive->order(), 8); + } + + void testSpaceGroup() + { + // Small test, constructing Fm-3m (225) from the generators listed in ITA + Group_const_sptr group1 = CyclicGroup::create("-x,-y,z"); + Group_const_sptr group2 = CyclicGroup::create("-x,y,-z"); + Group_const_sptr group3 = CyclicGroup::create("z,x,y"); + Group_const_sptr group4 = CyclicGroup::create("y,x,-z"); + Group_const_sptr group5 = CyclicGroup::create("-x,-y,-z"); + + // Make a translation group "F" + std::vector lops; + lops.push_back(SymmetryOperation("x,y,z")); + lops.push_back(SymmetryOperation("x,y+1/2,z+1/2")); + lops.push_back(SymmetryOperation("x+1/2,y+1/2,z")); + lops.push_back(SymmetryOperation("x+1/2,y,z+1/2")); + + Group_const_sptr translationGroup = boost::make_shared(lops); + + // Generate space group by multiplying the generating groups + Group_const_sptr fm3barm = group1 * group2 * group3 * group4 * group5 * translationGroup; + + // Output the symmetry operations including Centering in x,y,z-form + std::cout << std::endl; + std::cout << "Order: " << fm3barm->order() << std::endl; + std::vector ops = fm3barm->getSymmetryOperations(); + for(auto it = ops.begin(); it != ops.end(); ++it) { + std::cout << (*it).identifier() << std::endl; + } + + V3D w2a(0, 0, 0); + std::vector w2at = fm3barm * w2a; + std::cout << "Equivalents of " << w2a << ":" << std::endl; + for(auto it = w2at.begin(); it != w2at.end(); ++it) { + std::cout << " " << (*it) << std::endl; + } + } + + + +}; + + +#endif /* MANTID_GEOMETRY_CYCLICGROUPTEST_H_ */ diff --git a/Code/Mantid/Framework/Geometry/test/GroupTest.h b/Code/Mantid/Framework/Geometry/test/GroupTest.h new file mode 100644 index 000000000000..0aefe9165e8d --- /dev/null +++ b/Code/Mantid/Framework/Geometry/test/GroupTest.h @@ -0,0 +1,177 @@ +#ifndef MANTID_GEOMETRY_GROUPTEST_H_ +#define MANTID_GEOMETRY_GROUPTEST_H_ + +#include + +#include "MantidGeometry/Crystal/Group.h" +#include "MantidGeometry/Crystal/SymmetryOperationFactory.h" +#include + +using namespace Mantid::Geometry; + +class GroupTest : 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 GroupTest *createSuite() { return new GroupTest(); } + static void destroySuite( GroupTest *suite ) { delete suite; } + + void testDefaultConstructor() + { + Group group; + TS_ASSERT_EQUALS(group.order(), 1); + TS_ASSERT(group.getSymmetryOperations().front().isIdentity()); + } + + + void testConstructor() + { + std::vector symOps; + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("x,y,z")); + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("-x,-y,-z")); + + TS_ASSERT_THROWS_NOTHING(Group group(symOps)); + + Group group(symOps); + + std::vector groupOps = group.getSymmetryOperations(); + TS_ASSERT_EQUALS(groupOps.size(), 2); + + std::vector empty; + TS_ASSERT_THROWS(Group group(empty), std::invalid_argument); + } + + void testCopyConstructor() + { + std::vector symOps; + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("x,y,z")); + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("-x,-y,-z")); + + Group group(symOps); + Group otherGroup(group); + + TS_ASSERT_EQUALS(group.order(), otherGroup.order()); + TS_ASSERT_EQUALS(group.getSymmetryOperations(), otherGroup.getSymmetryOperations()); + } + + void testAssignmentOperator() + { + std::vector symOps; + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("x,y,z")); + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("-x,-y,-z")); + + Group otherGroup(symOps); + + Group group; + TS_ASSERT_DIFFERS(group.order(), otherGroup.order()); + TS_ASSERT_DIFFERS(group.getSymmetryOperations(), otherGroup.getSymmetryOperations()); + + group = otherGroup; + TS_ASSERT_EQUALS(group.order(), otherGroup.order()); + TS_ASSERT_EQUALS(group.getSymmetryOperations(), otherGroup.getSymmetryOperations()); + } + + void testOrder() + { + Group defaultGroup; + TS_ASSERT_EQUALS(defaultGroup.order(), 1); + + // Making a group of two operations gives order 2 + std::vector symOps; + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("x,y,z")); + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("-x,-y,-z")); + + Group biggerGroup(symOps); + TS_ASSERT_EQUALS(biggerGroup.order(), 2); + + // Adding another one results in 3 + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("-x,y,z")); + Group evenBiggerGroup(symOps); + TS_ASSERT_EQUALS(evenBiggerGroup.order(), 3); + + // Multiple occurences of the same operation do not count + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("-x,-y,-z")); + Group sameAsBefore(symOps); + TS_ASSERT_EQUALS(sameAsBefore.order(), 3); + } + + void testComparison() + { + std::vector symOps; + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("x,y,z")); + symOps.push_back(SymmetryOperationFactory::Instance().createSymOp("-x,-y,-z")); + + Group groupOne(symOps); + Group groupTwo(symOps); + + TS_ASSERT(groupOne == groupTwo); + TS_ASSERT(groupTwo == groupOne); + + Group defaultGroup; + TS_ASSERT(!(groupOne == defaultGroup)); + TS_ASSERT(!(defaultGroup == groupOne)); + TS_ASSERT(groupOne != defaultGroup); + TS_ASSERT(defaultGroup != groupOne); + } + + void testMultiplicationOperator() + { + // We take pointgroup -1 + std::vector inversion; + inversion.push_back(SymmetryOperationFactory::Instance().createSymOp("x,y,z")); + inversion.push_back(SymmetryOperationFactory::Instance().createSymOp("-x,-y,-z")); + + // And 2 (b-axis unique) + std::vector twoFoldY; + twoFoldY.push_back(SymmetryOperationFactory::Instance().createSymOp("x,y,z")); + twoFoldY.push_back(SymmetryOperationFactory::Instance().createSymOp("-x,y,-z")); + + Group one(inversion); + Group two(twoFoldY); + + // Multiplication results in 2/m + Group three = one * two; + TS_ASSERT_EQUALS(three.order(), 4); + + // The multiplication created m perpendicular to b (x,-y,-z) + SymmetryOperation mirrorY = SymmetryOperationFactory::Instance().createSymOp("x,-y,z"); + std::vector opsOfThree = three.getSymmetryOperations(); + + // Check that it is found in the list of symmetry operations of the new group + TS_ASSERT_DIFFERS(std::find(opsOfThree.begin(), opsOfThree.end(), mirrorY), opsOfThree.end()); + + Group four = two * one; + TS_ASSERT(three == four); + } + + void testSmartPointerOperators() + { + // We take pointgroup -1 + std::vector inversion; + inversion.push_back(SymmetryOperationFactory::Instance().createSymOp("x,y,z")); + inversion.push_back(SymmetryOperationFactory::Instance().createSymOp("-x,-y,-z")); + + // And 2 (b-axis unique) + std::vector twoFoldY; + twoFoldY.push_back(SymmetryOperationFactory::Instance().createSymOp("x,y,z")); + twoFoldY.push_back(SymmetryOperationFactory::Instance().createSymOp("-x,y,-z")); + + Group_const_sptr one = boost::make_shared(inversion); + Group_const_sptr two = boost::make_shared(twoFoldY); + + Group_const_sptr three = one * two; + TS_ASSERT_EQUALS(three->order(), 4); + + SymmetryOperation mirrorY = SymmetryOperationFactory::Instance().createSymOp("x,-y,z"); + std::vector opsOfThree = three->getSymmetryOperations(); + + // Check that it is found in the list of symmetry operations of the new group + TS_ASSERT_DIFFERS(std::find(opsOfThree.begin(), opsOfThree.end(), mirrorY), opsOfThree.end()); + } + + +}; + + +#endif /* MANTID_GEOMETRY_GROUPTEST_H_ */ From d281ab24182a7b4a05dcc5bec24752b606ceb8c1 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Tue, 7 Oct 2014 09:20:43 +0200 Subject: [PATCH 02/48] Refs #10281. Removed counting variable from Group. It was only used for testing purposes (counting number of required operations). --- .../Framework/Geometry/inc/MantidGeometry/Crystal/Group.h | 2 -- Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp | 3 --- 2 files changed, 5 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h index d09ca06b77ae..f850e3b39b64 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h @@ -61,8 +61,6 @@ class MANTID_GEOMETRY_DLL Group bool operator ==(const Group &other) const; bool operator !=(const Group &other) const; - static int m_numOps; - protected: void setSymmetryOperations(const std::vector &symmetryOperations); diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp index 5b5fcc7aab25..cc8e28e41a3a 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp @@ -6,8 +6,6 @@ namespace Mantid namespace Geometry { -int Group::m_numOps = 0; - /// Construct a group group from a given set of operations. This is Group::Group() : m_allOperations(), @@ -57,7 +55,6 @@ Group Group::operator *(const Group &other) const for(auto selfOp = m_allOperations.begin(); selfOp != m_allOperations.end(); ++selfOp) { for(auto otherOp = other.m_allOperations.begin(); otherOp != other.m_allOperations.end(); ++otherOp) { result.push_back((*selfOp) * (*otherOp)); - ++m_numOps; } } From c2cbd2b5a9cbb780fde0ed0cf3f14186e0db3dcd Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Tue, 7 Oct 2014 13:25:22 +0200 Subject: [PATCH 03/48] Refs #10281. Added simple factory function for Group-objects. Since groups can be constructed from strings only and are probably not used directly in code so much, this solution seems simpler than a complete factory. --- .../inc/MantidGeometry/Crystal/CyclicGroup.h | 3 +-- .../inc/MantidGeometry/Crystal/Group.h | 9 +++++++++ .../Geometry/src/Crystal/CyclicGroup.cpp | 9 +++++---- .../Framework/Geometry/test/CyclicGroupTest.h | 18 +++++++++--------- 4 files changed, 24 insertions(+), 15 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h index e75c0a86cf4f..8b3817a57330 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h @@ -43,11 +43,10 @@ namespace Geometry class MANTID_GEOMETRY_DLL CyclicGroup : public Group { public: + CyclicGroup(const std::string &symmetryOperationString); CyclicGroup(const SymmetryOperation &symmetryOperation); virtual ~CyclicGroup() { } - static Group_const_sptr create(const std::string &symmetryOperation); - protected: std::vector generateAllOperations(const SymmetryOperation &operation) const; diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h index f850e3b39b64..725bc359922b 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h @@ -8,6 +8,7 @@ #include #include +#include namespace Mantid { @@ -71,6 +72,14 @@ class MANTID_GEOMETRY_DLL Group typedef boost::shared_ptr Group_sptr; typedef boost::shared_ptr Group_const_sptr; +namespace GroupFactory { + template + Group_const_sptr create(const std::string &initializationString) + { + return boost::make_shared(initializationString); + } +} + MANTID_GEOMETRY_DLL Group_const_sptr operator *(const Group_const_sptr &lhs, const Group_const_sptr &rhs); MANTID_GEOMETRY_DLL std::vector operator *(const Group_const_sptr &lhs, const Kernel::V3D &rhs); MANTID_GEOMETRY_DLL bool operator ==(const Group_const_sptr &lhs, const Group_const_sptr &rhs); diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp index 74909f36dc66..b9bb8302672b 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp @@ -8,14 +8,15 @@ namespace Geometry { /// Construct cyclic group from one symmetry operation by applying it to itself until identity is obtained. -CyclicGroup::CyclicGroup(const SymmetryOperation &symmetryOperation) : - Group(generateAllOperations(symmetryOperation)) +CyclicGroup::CyclicGroup(const std::string &symmetryOperationString) : + Group(generateAllOperations(SymmetryOperationFactory::Instance().createSymOp(symmetryOperationString))) { + } -Group_const_sptr CyclicGroup::create(const std::string &symmetryOperation) +CyclicGroup::CyclicGroup(const SymmetryOperation &symmetryOperation) : + Group(generateAllOperations(symmetryOperation)) { - return boost::make_shared(SymmetryOperationFactory::Instance().createSymOp(symmetryOperation)); } std::vector CyclicGroup::generateAllOperations(const SymmetryOperation &operation) const diff --git a/Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h b/Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h index 4697ff5b6427..70e21bd53093 100644 --- a/Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h +++ b/Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h @@ -29,7 +29,7 @@ class CyclicGroupTest : public CxxTest::TestSuite void testCreate() { - Group_const_sptr group = CyclicGroup::create("-x,-y,-z"); + Group_const_sptr group = GroupFactory::create("-x,-y,-z"); TS_ASSERT(boost::dynamic_pointer_cast(group)); TS_ASSERT_EQUALS(group->order(), 2); @@ -41,15 +41,15 @@ class CyclicGroupTest : public CxxTest::TestSuite * the method used to generate point groups. */ - Group_const_sptr groupOne = CyclicGroup::create("-x,-y,z"); - Group_const_sptr groupTwo = CyclicGroup::create("x,-y,-z"); + Group_const_sptr groupOne = GroupFactory::create("-x,-y,z"); + Group_const_sptr groupTwo = GroupFactory::create("x,-y,-z"); // This is in fact point group 222 Group_const_sptr groupThree = groupOne * groupTwo; TS_ASSERT_EQUALS(groupThree->order(), 4); - Group_const_sptr groupFour = CyclicGroup::create("-x,-y,-z"); + Group_const_sptr groupFour = GroupFactory::create("-x,-y,-z"); // Which becomes mmm, if inversion is added. Group_const_sptr groupFive = groupFour * groupThree; @@ -59,11 +59,11 @@ class CyclicGroupTest : public CxxTest::TestSuite void testSpaceGroup() { // Small test, constructing Fm-3m (225) from the generators listed in ITA - Group_const_sptr group1 = CyclicGroup::create("-x,-y,z"); - Group_const_sptr group2 = CyclicGroup::create("-x,y,-z"); - Group_const_sptr group3 = CyclicGroup::create("z,x,y"); - Group_const_sptr group4 = CyclicGroup::create("y,x,-z"); - Group_const_sptr group5 = CyclicGroup::create("-x,-y,-z"); + Group_const_sptr group1 = GroupFactory::create("-x,-y,z"); + Group_const_sptr group2 = GroupFactory::create("-x,y,-z"); + Group_const_sptr group3 = GroupFactory::create("z,x,y"); + Group_const_sptr group4 = GroupFactory::create("y,x,-z"); + Group_const_sptr group5 = GroupFactory::create("-x,-y,-z"); // Make a translation group "F" std::vector lops; From 30c4756704cd1dda86f3efd5c11e9078619f3744 Mon Sep 17 00:00:00 2001 From: Alex Buts Date: Tue, 7 Oct 2014 13:04:23 +0100 Subject: [PATCH 04/48] refs #10309 LoadNexusMonitors falls back to isis vms compartibility data to got instrument name from incorrectly formatted ISIS nexus file --- .../inc/MantidDataHandling/LoadEventNexus.h | 3 + .../DataHandling/src/LoadEventNexus.cpp | 4661 +++++++++-------- .../DataHandling/src/LoadNexusMonitors.cpp | 35 +- 3 files changed, 2375 insertions(+), 2324 deletions(-) diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadEventNexus.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadEventNexus.h index 1fb267994473..6f149bf5747a 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadEventNexus.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/LoadEventNexus.h @@ -129,6 +129,9 @@ namespace DataHandling static void loadSampleDataISIScompatibility(::NeXus::File& file, Mantid::API::MatrixWorkspace_sptr WS); + /// method used to return instrument name for some old ISIS files where it is not written properly within the instrument + static std::string readInstrumentFromISIS_VMSCompat(::NeXus::File &hFile); + public: /// The name and path of the input file diff --git a/Code/Mantid/Framework/DataHandling/src/LoadEventNexus.cpp b/Code/Mantid/Framework/DataHandling/src/LoadEventNexus.cpp index ef501b221f70..cbbf619213de 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadEventNexus.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadEventNexus.cpp @@ -30,2016 +30,2054 @@ using namespace ::NeXus; namespace Mantid { -namespace DataHandling -{ + namespace DataHandling + { - DECLARE_NEXUS_FILELOADER_ALGORITHM(LoadEventNexus) + DECLARE_NEXUS_FILELOADER_ALGORITHM(LoadEventNexus) - using namespace Kernel; - using namespace Geometry; - using namespace API; - using namespace DataObjects; + using namespace Kernel; + using namespace Geometry; + using namespace API; + using namespace DataObjects; - //=============================================================================================== - // BankPulseTimes - //=============================================================================================== + //=============================================================================================== + // BankPulseTimes + //=============================================================================================== + + //---------------------------------------------------------------------------------------------- + /** Constructor. Loads the pulse times from the bank entry of the file + * + * @param file :: nexus file open in the right bank entry + */ + BankPulseTimes::BankPulseTimes(::NeXus::File & file) + { + file.openData("event_time_zero"); + // Read the offset (time zero) + file.getAttr("offset", startTime); + DateAndTime start(startTime); + // Load the seconds offsets + std::vector seconds; + file.getData(seconds); + file.closeData(); + // Now create the pulseTimes + numPulses = seconds.size(); + if (numPulses == 0) + throw std::runtime_error("event_time_zero field has no data!"); + pulseTimes = new DateAndTime[numPulses]; + for (size_t i=0; i & times) + { + numPulses = times.size(); + pulseTimes = NULL; + if (numPulses == 0) + return; + pulseTimes = new DateAndTime[numPulses]; + for (size_t i=0; ipulseTimes; + } - //---------------------------------------------------------------------------------------------- - /** Constructor. Loads the pulse times from the bank entry of the file - * - * @param file :: nexus file open in the right bank entry - */ - BankPulseTimes::BankPulseTimes(::NeXus::File & file) - { - file.openData("event_time_zero"); - // Read the offset (time zero) - file.getAttr("offset", startTime); - DateAndTime start(startTime); - // Load the seconds offsets - std::vector seconds; - file.getData(seconds); - file.closeData(); - // Now create the pulseTimes - numPulses = seconds.size(); - if (numPulses == 0) - throw std::runtime_error("event_time_zero field has no data!"); - pulseTimes = new DateAndTime[numPulses]; - for (size_t i=0; i & times) - { - numPulses = times.size(); - pulseTimes = NULL; - if (numPulses == 0) - return; - pulseTimes = new DateAndTime[numPulses]; - for (size_t i=0; ipulseTimes; - } - - //---------------------------------------------------------------------------------------------- - /** Comparison. Is this bank's pulse times array the same as another one. - * - * @param otherNumPulse :: number of pulses in the OTHER bank event_time_zero. - * @param otherStartTime :: "offset" attribute of the OTHER bank event_time_zero. - * @return true if the pulse times are the same and so don't need to be reloaded. - */ - bool BankPulseTimes::equals(size_t otherNumPulse, std::string otherStartTime) - { - return ((this->startTime == otherStartTime) && (this->numPulses == otherNumPulse)); - } - - //============================================================================================== - // Class ProcessBankData - //============================================================================================== - /** This task does the disk IO from loading the NXS file, - * and so will be on a disk IO mutex */ - class ProcessBankData : public Task - { - public: //---------------------------------------------------------------------------------------------- - /** Constructor - * - * @param alg :: LoadEventNexus - * @param entry_name :: name of the bank - * @param prog :: Progress reporter - * @param scheduler :: ThreadScheduler running this task - * @param event_id :: array with event IDs - * @param event_time_of_flight :: array with event TOFS - * @param numEvents :: how many events in the arrays - * @param startAt :: index of the first event from event_index - * @param event_index :: vector of event index (length of # of pulses) - * @param thisBankPulseTimes :: ptr to the pulse times for this particular bank. - * @param have_weight :: flag for handling simulated files - * @param event_weight :: array with weights for events - * @param min_event_id ;: minimum detector ID to load - * @param max_event_id :: maximum detector ID to load - * @return - */ - ProcessBankData(LoadEventNexus * alg, std::string entry_name, - Progress * prog, ThreadScheduler * scheduler, - boost::shared_array event_id, - boost::shared_array event_time_of_flight, - size_t numEvents, size_t startAt, - boost::shared_ptr > event_index, - boost::shared_ptr thisBankPulseTimes, - bool have_weight, boost::shared_array event_weight, - detid_t min_event_id, detid_t max_event_id) - : Task(), alg(alg), entry_name(entry_name), pixelID_to_wi_vector(alg->pixelID_to_wi_vector), + /** Comparison. Is this bank's pulse times array the same as another one. + * + * @param otherNumPulse :: number of pulses in the OTHER bank event_time_zero. + * @param otherStartTime :: "offset" attribute of the OTHER bank event_time_zero. + * @return true if the pulse times are the same and so don't need to be reloaded. + */ + bool BankPulseTimes::equals(size_t otherNumPulse, std::string otherStartTime) + { + return ((this->startTime == otherStartTime) && (this->numPulses == otherNumPulse)); + } + + //============================================================================================== + // Class ProcessBankData + //============================================================================================== + /** This task does the disk IO from loading the NXS file, + * and so will be on a disk IO mutex */ + class ProcessBankData : public Task + { + public: + //---------------------------------------------------------------------------------------------- + /** Constructor + * + * @param alg :: LoadEventNexus + * @param entry_name :: name of the bank + * @param prog :: Progress reporter + * @param scheduler :: ThreadScheduler running this task + * @param event_id :: array with event IDs + * @param event_time_of_flight :: array with event TOFS + * @param numEvents :: how many events in the arrays + * @param startAt :: index of the first event from event_index + * @param event_index :: vector of event index (length of # of pulses) + * @param thisBankPulseTimes :: ptr to the pulse times for this particular bank. + * @param have_weight :: flag for handling simulated files + * @param event_weight :: array with weights for events + * @param min_event_id ;: minimum detector ID to load + * @param max_event_id :: maximum detector ID to load + * @return + */ + ProcessBankData(LoadEventNexus * alg, std::string entry_name, + Progress * prog, ThreadScheduler * scheduler, + boost::shared_array event_id, + boost::shared_array event_time_of_flight, + size_t numEvents, size_t startAt, + boost::shared_ptr > event_index, + boost::shared_ptr thisBankPulseTimes, + bool have_weight, boost::shared_array event_weight, + detid_t min_event_id, detid_t max_event_id) + : Task(), alg(alg), entry_name(entry_name), pixelID_to_wi_vector(alg->pixelID_to_wi_vector), pixelID_to_wi_offset(alg->pixelID_to_wi_offset), prog(prog), scheduler(scheduler), event_id(event_id), event_time_of_flight(event_time_of_flight), numEvents(numEvents), startAt(startAt), event_index(event_index), thisBankPulseTimes(thisBankPulseTimes), have_weight(have_weight), event_weight(event_weight), m_min_id(min_event_id), m_max_id(max_event_id) - { - // Cost is approximately proportional to the number of events to process. - m_cost = static_cast(numEvents); - } + { + // Cost is approximately proportional to the number of events to process. + m_cost = static_cast(numEvents); + } - //---------------------------------------------------------------------------------------------- - /** Run the data processing + //---------------------------------------------------------------------------------------------- + /** Run the data processing */ - void run() - { - //Local tof limits - double my_shortest_tof = static_cast(std::numeric_limits::max()) * 0.1; - double my_longest_tof = 0.; - // A count of "bad" TOFs that were too high - size_t badTofs = 0; - size_t my_discarded_events(0); + void run() + { + //Local tof limits + double my_shortest_tof = static_cast(std::numeric_limits::max()) * 0.1; + double my_longest_tof = 0.; + // A count of "bad" TOFs that were too high + size_t badTofs = 0; + size_t my_discarded_events(0); - prog->report(entry_name + ": precount"); + prog->report(entry_name + ": precount"); - // ---- Pre-counting events per pixel ID ---- - auto & outputWS = *(alg->WS); + // ---- Pre-counting events per pixel ID ---- + auto & outputWS = *(alg->WS); - if (alg->precount) - { - std::vector counts(m_max_id-m_min_id+1, 0); - for (size_t i=0; i < numEvents; i++) + if (alg->precount) { - detid_t thisId = detid_t(event_id[i]); - if (thisId >= m_min_id && thisId <= m_max_id) - counts[thisId-m_min_id]++; - } + std::vector counts(m_max_id-m_min_id+1, 0); + for (size_t i=0; i < numEvents; i++) + { + detid_t thisId = detid_t(event_id[i]); + if (thisId >= m_min_id && thisId <= m_max_id) + counts[thisId-m_min_id]++; + } - // Now we pre-allocate (reserve) the vectors of events in each pixel counted - const size_t numEventLists = outputWS.getNumberHistograms(); - for (detid_t pixID = m_min_id; pixID <= m_max_id; pixID++) - { - if (counts[pixID-m_min_id] > 0) + // Now we pre-allocate (reserve) the vectors of events in each pixel counted + const size_t numEventLists = outputWS.getNumberHistograms(); + for (detid_t pixID = m_min_id; pixID <= m_max_id; pixID++) { - //Find the the workspace index corresponding to that pixel ID - size_t wi = pixelID_to_wi_vector[pixID+pixelID_to_wi_offset]; - // Allocate it - if ( wi < numEventLists ) + if (counts[pixID-m_min_id] > 0) { - outputWS.getEventList(wi).reserve( counts[pixID-m_min_id] ); + //Find the the workspace index corresponding to that pixel ID + size_t wi = pixelID_to_wi_vector[pixID+pixelID_to_wi_offset]; + // Allocate it + if ( wi < numEventLists ) + { + outputWS.getEventList(wi).reserve( counts[pixID-m_min_id] ); + } + if (alg->getCancel()) break; // User cancellation } - if (alg->getCancel()) break; // User cancellation } } - } - // Check for cancelled algorithm - if (alg->getCancel()) - { - return; - } + // Check for cancelled algorithm + if (alg->getCancel()) + { + return; + } - //Default pulse time (if none are found) - Mantid::Kernel::DateAndTime pulsetime; - Mantid::Kernel::DateAndTime lastpulsetime(0); + //Default pulse time (if none are found) + Mantid::Kernel::DateAndTime pulsetime; + Mantid::Kernel::DateAndTime lastpulsetime(0); - bool pulsetimesincreasing = true; + bool pulsetimesincreasing = true; - // Index into the pulse array - int pulse_i = 0; + // Index into the pulse array + int pulse_i = 0; - // And there are this many pulses - int numPulses = static_cast(thisBankPulseTimes->numPulses); - if (numPulses > static_cast(event_index->size())) - { - alg->getLogger().warning() << "Entry " << entry_name << "'s event_index vector is smaller than the event_time_zero field. This is inconsistent, so we cannot find pulse times for this entry.\n"; - //This'll make the code skip looking for any pulse times. - pulse_i = numPulses + 1; - } + // And there are this many pulses + int numPulses = static_cast(thisBankPulseTimes->numPulses); + if (numPulses > static_cast(event_index->size())) + { + alg->getLogger().warning() << "Entry " << entry_name << "'s event_index vector is smaller than the event_time_zero field. This is inconsistent, so we cannot find pulse times for this entry.\n"; + //This'll make the code skip looking for any pulse times. + pulse_i = numPulses + 1; + } - prog->report(entry_name + ": filling events"); + prog->report(entry_name + ": filling events"); - // Will we need to compress? - bool compress = (alg->compressTolerance >= 0); + // Will we need to compress? + bool compress = (alg->compressTolerance >= 0); - // Which detector IDs were touched? - only matters if compress is on - std::vector usedDetIds; - if (compress) usedDetIds.assign(m_max_id-m_min_id+1, false); + // Which detector IDs were touched? - only matters if compress is on + std::vector usedDetIds; + if (compress) usedDetIds.assign(m_max_id-m_min_id+1, false); - //Go through all events in the list - for (std::size_t i = 0; i < numEvents; i++) - { - //------ Find the pulse time for this event index --------- - if (pulse_i < numPulses-1) + //Go through all events in the list + for (std::size_t i = 0; i < numEvents; i++) { - bool breakOut = false; - //Go through event_index until you find where the index increases to encompass the current index. Your pulse = the one before. - while ( (i+startAt < event_index->operator[](pulse_i)) - || (i+startAt >= event_index->operator[](pulse_i+1)) ) + //------ Find the pulse time for this event index --------- + if (pulse_i < numPulses-1) { - pulse_i++; - // Check once every new pulse if you need to cancel (checking on every event might slow things down more) - if (alg->getCancel()) breakOut = true; - if (pulse_i >= (numPulses-1)) - break; - } + bool breakOut = false; + //Go through event_index until you find where the index increases to encompass the current index. Your pulse = the one before. + while ( (i+startAt < event_index->operator[](pulse_i)) + || (i+startAt >= event_index->operator[](pulse_i+1)) ) + { + pulse_i++; + // Check once every new pulse if you need to cancel (checking on every event might slow things down more) + if (alg->getCancel()) breakOut = true; + if (pulse_i >= (numPulses-1)) + break; + } - //Save the pulse time at this index for creating those events - pulsetime = thisBankPulseTimes->pulseTimes[pulse_i]; + //Save the pulse time at this index for creating those events + pulsetime = thisBankPulseTimes->pulseTimes[pulse_i]; - // Determine if pulse times continue to increase - if (pulsetime < lastpulsetime) - pulsetimesincreasing = false; - else - lastpulsetime = pulsetime; + // Determine if pulse times continue to increase + if (pulsetime < lastpulsetime) + pulsetimesincreasing = false; + else + lastpulsetime = pulsetime; - // Flag to break out of the event loop without using goto - if (breakOut) - break; - } + // Flag to break out of the event loop without using goto + if (breakOut) + break; + } - // We cached a pointer to the vector -> so retrieve it and add the event - detid_t detId = event_id[i]; - if (detId >= m_min_id && detId <= m_max_id) - { - //Create the tofevent - double tof = static_cast( event_time_of_flight[i] ); - if ((tof >= alg->filter_tof_min) && (tof <= alg->filter_tof_max)) + // We cached a pointer to the vector -> so retrieve it and add the event + detid_t detId = event_id[i]; + if (detId >= m_min_id && detId <= m_max_id) { - // Handle simulated data if present - if (have_weight) + //Create the tofevent + double tof = static_cast( event_time_of_flight[i] ); + if ((tof >= alg->filter_tof_min) && (tof <= alg->filter_tof_max)) { - double weight = static_cast(event_weight[i]); - double errorSq = weight * weight; - std::vector *eventVector = alg->weightedEventVectors[detId]; - // NULL eventVector indicates a bad spectrum lookup - if(eventVector) + // Handle simulated data if present + if (have_weight) { + double weight = static_cast(event_weight[i]); + double errorSq = weight * weight; + std::vector *eventVector = alg->weightedEventVectors[detId]; + // NULL eventVector indicates a bad spectrum lookup + if(eventVector) + { #if !(defined(__INTEL_COMPILER)) && !(defined(__clang__)) - // This avoids a copy constructor call but is only available with GCC (requires variadic templates) - eventVector->emplace_back( tof, pulsetime, weight, errorSq ); + // This avoids a copy constructor call but is only available with GCC (requires variadic templates) + eventVector->emplace_back( tof, pulsetime, weight, errorSq ); #else - eventVector->push_back( WeightedEvent(tof, pulsetime, weight, errorSq) ); + eventVector->push_back( WeightedEvent(tof, pulsetime, weight, errorSq) ); #endif + } + else + { + ++my_discarded_events; + } } else { - ++my_discarded_events; - } - } - else - { - // We have cached the vector of events for this detector ID - std::vector *eventVector = alg->eventVectors[detId]; - // NULL eventVector indicates a bad spectrum lookup - if(eventVector) - { + // We have cached the vector of events for this detector ID + std::vector *eventVector = alg->eventVectors[detId]; + // NULL eventVector indicates a bad spectrum lookup + if(eventVector) + { #if !(defined(__INTEL_COMPILER)) && !(defined(__clang__)) - // This avoids a copy constructor call but is only available with GCC (requires variadic templates) - eventVector->emplace_back( tof, pulsetime ); + // This avoids a copy constructor call but is only available with GCC (requires variadic templates) + eventVector->emplace_back( tof, pulsetime ); #else - eventVector->push_back( TofEvent(tof, pulsetime) ); + eventVector->push_back( TofEvent(tof, pulsetime) ); #endif + } + else + { + ++my_discarded_events; + } } - else + + //Local tof limits + if (tof < my_shortest_tof) { my_shortest_tof = tof;} + // Skip any events that are the cause of bad DAS data (e.g. a negative number in uint32 -> 2.4 billion * 100 nanosec = 2.4e8 microsec) + if (tof < 2e8) { - ++my_discarded_events; + if (tof > my_longest_tof) { my_longest_tof = tof;} } - } - - //Local tof limits - if (tof < my_shortest_tof) { my_shortest_tof = tof;} - // Skip any events that are the cause of bad DAS data (e.g. a negative number in uint32 -> 2.4 billion * 100 nanosec = 2.4e8 microsec) - if (tof < 2e8) - { - if (tof > my_longest_tof) { my_longest_tof = tof;} - } - else - badTofs++; + else + badTofs++; - // Track all the touched wi (only necessary when compressing events, for thread safety) - if (compress) usedDetIds[detId-m_min_id] = true; - } // valid time-of-flight + // Track all the touched wi (only necessary when compressing events, for thread safety) + if (compress) usedDetIds[detId-m_min_id] = true; + } // valid time-of-flight - } // valid detector IDs - } //(for each event) + } // valid detector IDs + } //(for each event) - //------------ Compress Events (or set sort order) ------------------ - // Do it on all the detector IDs we touched - if (compress) - { - for (detid_t pixID = m_min_id; pixID <= m_max_id; pixID++) + //------------ Compress Events (or set sort order) ------------------ + // Do it on all the detector IDs we touched + if (compress) { - if (usedDetIds[pixID-m_min_id]) + for (detid_t pixID = m_min_id; pixID <= m_max_id; pixID++) { - //Find the the workspace index corresponding to that pixel ID - size_t wi = pixelID_to_wi_vector[pixID+pixelID_to_wi_offset]; - EventList * el = outputWS.getEventListPtr(wi); - if (compress) - el->compressEvents(alg->compressTolerance, el); - else + if (usedDetIds[pixID-m_min_id]) { - if (pulsetimesincreasing) - el->setSortOrder(DataObjects::PULSETIME_SORT); + //Find the the workspace index corresponding to that pixel ID + size_t wi = pixelID_to_wi_vector[pixID+pixelID_to_wi_offset]; + EventList * el = outputWS.getEventListPtr(wi); + if (compress) + el->compressEvents(alg->compressTolerance, el); else - el->setSortOrder(DataObjects::UNSORTED); + { + if (pulsetimesincreasing) + el->setSortOrder(DataObjects::PULSETIME_SORT); + else + el->setSortOrder(DataObjects::UNSORTED); + } } } } - } - prog->report(entry_name + ": filled events"); + prog->report(entry_name + ": filled events"); - alg->getLogger().debug() << entry_name << (pulsetimesincreasing ? " had " : " DID NOT have ") << - "monotonically increasing pulse times" << std::endl; + alg->getLogger().debug() << entry_name << (pulsetimesincreasing ? " had " : " DID NOT have ") << + "monotonically increasing pulse times" << std::endl; - //Join back up the tof limits to the global ones - PARALLEL_CRITICAL(tof_limits) - { - //This is not thread safe, so only one thread at a time runs this. - if (my_shortest_tof < alg->shortest_tof) { alg->shortest_tof = my_shortest_tof;} - if (my_longest_tof > alg->longest_tof ) { alg->longest_tof = my_longest_tof;} - alg->bad_tofs += badTofs; - alg->discarded_events += my_discarded_events; - } + //Join back up the tof limits to the global ones + PARALLEL_CRITICAL(tof_limits) + { + //This is not thread safe, so only one thread at a time runs this. + if (my_shortest_tof < alg->shortest_tof) { alg->shortest_tof = my_shortest_tof;} + if (my_longest_tof > alg->longest_tof ) { alg->longest_tof = my_longest_tof;} + alg->bad_tofs += badTofs; + alg->discarded_events += my_discarded_events; + } - // For Linux with tcmalloc, make sure memory goes back; - // but don't call if more than 15% of memory is still available, since that slows down the loading. - MemoryManager::Instance().releaseFreeMemoryIfAbove(0.85); + // For Linux with tcmalloc, make sure memory goes back; + // but don't call if more than 15% of memory is still available, since that slows down the loading. + MemoryManager::Instance().releaseFreeMemoryIfAbove(0.85); #ifndef _WIN32 - alg->getLogger().debug() << "Time to process " << entry_name << " " << m_timer << "\n"; + alg->getLogger().debug() << "Time to process " << entry_name << " " << m_timer << "\n"; #endif - } + } - private: - /// Algorithm being run - LoadEventNexus * alg; - /// NXS path to bank - std::string entry_name; - /// Vector where (index = pixel ID+pixelID_to_wi_offset), value = workspace index) - const std::vector & pixelID_to_wi_vector; - /// Offset in the pixelID_to_wi_vector to use. - detid_t pixelID_to_wi_offset; - /// Progress reporting - Progress * prog; - /// ThreadScheduler running this task - ThreadScheduler * scheduler; - /// event pixel ID array - boost::shared_array event_id; - /// event TOF array - boost::shared_array event_time_of_flight; - /// # of events in arrays - size_t numEvents; - /// index of the first event from event_index - size_t startAt; - /// vector of event index (length of # of pulses) - boost::shared_ptr > event_index; - /// Pulse times for this bank - boost::shared_ptr thisBankPulseTimes; - /// Flag for simulated data - bool have_weight; - /// event weights array - boost::shared_array event_weight; - /// Minimum pixel id - detid_t m_min_id; - /// Maximum pixel id - detid_t m_max_id; - /// timer for performance - Mantid::Kernel::Timer m_timer; - }; // END-DEF-CLASS ProcessBankData - - - //============================================================================================== - // Class LoadBankFromDiskTask - //============================================================================================== - /** This task does the disk IO from loading the NXS file, - * and so will be on a disk IO mutex */ - class LoadBankFromDiskTask : public Task - { + private: + /// Algorithm being run + LoadEventNexus * alg; + /// NXS path to bank + std::string entry_name; + /// Vector where (index = pixel ID+pixelID_to_wi_offset), value = workspace index) + const std::vector & pixelID_to_wi_vector; + /// Offset in the pixelID_to_wi_vector to use. + detid_t pixelID_to_wi_offset; + /// Progress reporting + Progress * prog; + /// ThreadScheduler running this task + ThreadScheduler * scheduler; + /// event pixel ID array + boost::shared_array event_id; + /// event TOF array + boost::shared_array event_time_of_flight; + /// # of events in arrays + size_t numEvents; + /// index of the first event from event_index + size_t startAt; + /// vector of event index (length of # of pulses) + boost::shared_ptr > event_index; + /// Pulse times for this bank + boost::shared_ptr thisBankPulseTimes; + /// Flag for simulated data + bool have_weight; + /// event weights array + boost::shared_array event_weight; + /// Minimum pixel id + detid_t m_min_id; + /// Maximum pixel id + detid_t m_max_id; + /// timer for performance + Mantid::Kernel::Timer m_timer; + }; // END-DEF-CLASS ProcessBankData + + + //============================================================================================== + // Class LoadBankFromDiskTask + //============================================================================================== + /** This task does the disk IO from loading the NXS file, + * and so will be on a disk IO mutex */ + class LoadBankFromDiskTask : public Task + { - public: - //--------------------------------------------------------------------------------------------------- - /** Constructor - * - * @param alg :: Handle to the main algorithm - * @param entry_name :: The pathname of the bank to load - * @param entry_type :: The classtype of the entry to load - * @param numEvents :: The number of events in the bank. - * @param oldNeXusFileNames :: Identify if file is of old variety. - * @param prog :: an optional Progress object - * @param ioMutex :: a mutex shared for all Disk I-O tasks - * @param scheduler :: the ThreadScheduler that runs this task. - */ - LoadBankFromDiskTask(LoadEventNexus * alg, const std::string& entry_name, const std::string & entry_type, - const std::size_t numEvents, const bool oldNeXusFileNames, - Progress * prog, boost::shared_ptr ioMutex, ThreadScheduler * scheduler) - : Task(), + public: + //--------------------------------------------------------------------------------------------------- + /** Constructor + * + * @param alg :: Handle to the main algorithm + * @param entry_name :: The pathname of the bank to load + * @param entry_type :: The classtype of the entry to load + * @param numEvents :: The number of events in the bank. + * @param oldNeXusFileNames :: Identify if file is of old variety. + * @param prog :: an optional Progress object + * @param ioMutex :: a mutex shared for all Disk I-O tasks + * @param scheduler :: the ThreadScheduler that runs this task. + */ + LoadBankFromDiskTask(LoadEventNexus * alg, const std::string& entry_name, const std::string & entry_type, + const std::size_t numEvents, const bool oldNeXusFileNames, + Progress * prog, boost::shared_ptr ioMutex, ThreadScheduler * scheduler) + : Task(), alg(alg), entry_name(entry_name), entry_type(entry_type), pixelID_to_wi_vector(alg->pixelID_to_wi_vector), pixelID_to_wi_offset(alg->pixelID_to_wi_offset), // prog(prog), scheduler(scheduler), thisBankPulseTimes(NULL), m_loadError(false), prog(prog), scheduler(scheduler), m_loadError(false), m_oldNexusFileNames(oldNeXusFileNames), m_loadStart(), m_loadSize(), m_event_id(NULL), m_event_time_of_flight(NULL), m_have_weight(false), m_event_weight(NULL) - { - setMutex(ioMutex); - m_cost = static_cast(numEvents); - m_min_id = std::numeric_limits::max(); - m_max_id = 0; - } - - //--------------------------------------------------------------------------------------------------- - /** Load the pulse times, if needed. This sets - * thisBankPulseTimes to the right pointer. - * */ - void loadPulseTimes(::NeXus::File & file) - { - try - { - // First, get info about the event_time_zero field in this bank - file.openData("event_time_zero"); - } - catch (::NeXus::Exception&) { - // Field not found error is most likely. - // Use the "proton_charge" das logs. - thisBankPulseTimes = alg->m_allBanksPulseTimes; - return; + setMutex(ioMutex); + m_cost = static_cast(numEvents); + m_min_id = std::numeric_limits::max(); + m_max_id = 0; } - std::string thisStartTime = ""; - size_t thisNumPulses = 0; - file.getAttr("offset", thisStartTime); - if (file.getInfo().dims.size() > 0) - thisNumPulses = file.getInfo().dims[0]; - file.closeData(); - // Now, we look through existing ones to see if it is already loaded - // thisBankPulseTimes = NULL; - for (size_t i=0; im_bankPulseTimes.size(); i++) + //--------------------------------------------------------------------------------------------------- + /** Load the pulse times, if needed. This sets + * thisBankPulseTimes to the right pointer. + * */ + void loadPulseTimes(::NeXus::File & file) { - if (alg->m_bankPulseTimes[i]->equals(thisNumPulses, thisStartTime)) + try + { + // First, get info about the event_time_zero field in this bank + file.openData("event_time_zero"); + } + catch (::NeXus::Exception&) { - thisBankPulseTimes = alg->m_bankPulseTimes[i]; + // Field not found error is most likely. + // Use the "proton_charge" das logs. + thisBankPulseTimes = alg->m_allBanksPulseTimes; return; } - } + std::string thisStartTime = ""; + size_t thisNumPulses = 0; + file.getAttr("offset", thisStartTime); + if (file.getInfo().dims.size() > 0) + thisNumPulses = file.getInfo().dims[0]; + file.closeData(); - // Not found? Need to load and add it - thisBankPulseTimes = boost::make_shared(boost::ref(file)); - alg->m_bankPulseTimes.push_back(thisBankPulseTimes); - } + // Now, we look through existing ones to see if it is already loaded + // thisBankPulseTimes = NULL; + for (size_t i=0; im_bankPulseTimes.size(); i++) + { + if (alg->m_bankPulseTimes[i]->equals(thisNumPulses, thisStartTime)) + { + thisBankPulseTimes = alg->m_bankPulseTimes[i]; + return; + } + } + // Not found? Need to load and add it + thisBankPulseTimes = boost::make_shared(boost::ref(file)); + alg->m_bankPulseTimes.push_back(thisBankPulseTimes); + } - //--------------------------------------------------------------------------------------------------- - /** Load the event_index field - (a list of size of # of pulses giving the index in the event list for that pulse) - * @param file :: File handle for the NeXus file - * @param event_index :: ref to the vector - */ - void loadEventIndex(::NeXus::File & file, std::vector & event_index) - { - // Get the event_index (a list of size of # of pulses giving the index in the event list for that pulse) - file.openData("event_index"); - //Must be uint64 - if (file.getInfo().type == ::NeXus::UINT64) - file.getData(event_index); - else - { - alg->getLogger().warning() << "Entry " << entry_name << "'s event_index field is not UINT64! It will be skipped.\n"; - m_loadError = true; - } - file.closeData(); + //--------------------------------------------------------------------------------------------------- + /** Load the event_index field + (a list of size of # of pulses giving the index in the event list for that pulse) - // Look for the sign that the bank is empty - if (event_index.size()==1) + * @param file :: File handle for the NeXus file + * @param event_index :: ref to the vector + */ + void loadEventIndex(::NeXus::File & file, std::vector & event_index) { - if (event_index[0] == 0) + // Get the event_index (a list of size of # of pulses giving the index in the event list for that pulse) + file.openData("event_index"); + //Must be uint64 + if (file.getInfo().type == ::NeXus::UINT64) + file.getData(event_index); + else { - //One entry, only zero. This means NO events in this bank. + alg->getLogger().warning() << "Entry " << entry_name << "'s event_index field is not UINT64! It will be skipped.\n"; m_loadError = true; - alg->getLogger().debug() << "Bank " << entry_name << " is empty.\n"; } - } - - return; - } + file.closeData(); + // Look for the sign that the bank is empty + if (event_index.size()==1) + { + if (event_index[0] == 0) + { + //One entry, only zero. This means NO events in this bank. + m_loadError = true; + alg->getLogger().debug() << "Bank " << entry_name << " is empty.\n"; + } + } - //--------------------------------------------------------------------------------------------------- - /** Open the event_id field and validate the contents - * - * @param file :: File handle for the NeXus file - * @param start_event :: set to the index of the first event - * @param stop_event :: set to the index of the last event + 1 - * @param event_index :: (a list of size of # of pulses giving the index in the event list for that pulse) - */ - void prepareEventId(::NeXus::File & file, size_t & start_event, size_t & stop_event, std::vector & event_index) - { - // Get the list of pixel ID's - if (m_oldNexusFileNames) - file.openData("event_pixel_id"); - else - file.openData("event_id"); + return; + } - // By default, use all available indices - start_event = 0; - ::NeXus::Info id_info = file.getInfo(); - // dims[0] can be negative in ISIS meaning 2^32 + dims[0]. Take that into account - int64_t dim0 = recalculateDataSize(id_info.dims[0]); - stop_event = static_cast(dim0); - //Handle the time filtering by changing the start/end offsets. - for (size_t i=0; i < thisBankPulseTimes->numPulses; i++) + //--------------------------------------------------------------------------------------------------- + /** Open the event_id field and validate the contents + * + * @param file :: File handle for the NeXus file + * @param start_event :: set to the index of the first event + * @param stop_event :: set to the index of the last event + 1 + * @param event_index :: (a list of size of # of pulses giving the index in the event list for that pulse) + */ + void prepareEventId(::NeXus::File & file, size_t & start_event, size_t & stop_event, std::vector & event_index) { - if (thisBankPulseTimes->pulseTimes[i] >= alg->filter_time_start) + // Get the list of pixel ID's + if (m_oldNexusFileNames) + file.openData("event_pixel_id"); + else + file.openData("event_id"); + + // By default, use all available indices + start_event = 0; + ::NeXus::Info id_info = file.getInfo(); + // dims[0] can be negative in ISIS meaning 2^32 + dims[0]. Take that into account + int64_t dim0 = recalculateDataSize(id_info.dims[0]); + stop_event = static_cast(dim0); + + //Handle the time filtering by changing the start/end offsets. + for (size_t i=0; i < thisBankPulseTimes->numPulses; i++) { - start_event = event_index[i]; - break; // stop looking + if (thisBankPulseTimes->pulseTimes[i] >= alg->filter_time_start) + { + start_event = event_index[i]; + break; // stop looking + } } - } - if (start_event > static_cast(dim0)) - { - // If the frame indexes are bad then we can't construct the times of the events properly and filtering by time - // will not work on this data - alg->getLogger().warning() + if (start_event > static_cast(dim0)) + { + // If the frame indexes are bad then we can't construct the times of the events properly and filtering by time + // will not work on this data + alg->getLogger().warning() << this->entry_name << "'s field 'event_index' seems to be invalid (start_index > than the number of events in the bank)." << "All events will appear in the same frame and filtering by time will not be possible on this data.\n"; - start_event = 0; - stop_event = static_cast(dim0); - } - else - { - for (size_t i=0; i < thisBankPulseTimes->numPulses; i++) + start_event = 0; + stop_event = static_cast(dim0); + } + else { - if (thisBankPulseTimes->pulseTimes[i] > alg->filter_time_stop) + for (size_t i=0; i < thisBankPulseTimes->numPulses; i++) { - stop_event = event_index[i]; - break; + if (thisBankPulseTimes->pulseTimes[i] > alg->filter_time_stop) + { + stop_event = event_index[i]; + break; + } } } - } - // We are loading part - work out the event number range - if (alg->chunk != EMPTY_INT()) - { - start_event = (alg->chunk - alg->firstChunkForBank) * alg->eventsPerChunk; - // Don't change stop_event for the final chunk - if ( start_event + alg->eventsPerChunk < stop_event ) stop_event = start_event + alg->eventsPerChunk; - } + // We are loading part - work out the event number range + if (alg->chunk != EMPTY_INT()) + { + start_event = (alg->chunk - alg->firstChunkForBank) * alg->eventsPerChunk; + // Don't change stop_event for the final chunk + if ( start_event + alg->eventsPerChunk < stop_event ) stop_event = start_event + alg->eventsPerChunk; + } - // Make sure it is within range - if (stop_event > static_cast(dim0)) - stop_event = dim0; + // Make sure it is within range + if (stop_event > static_cast(dim0)) + stop_event = dim0; - alg->getLogger().debug() << entry_name << ": start_event " << start_event << " stop_event "<< stop_event << "\n"; + alg->getLogger().debug() << entry_name << ": start_event " << start_event << " stop_event "<< stop_event << "\n"; - return; - } + return; + } - //--------------------------------------------------------------------------------------------------- - /** Load the event_id field, which has been open + //--------------------------------------------------------------------------------------------------- + /** Load the event_id field, which has been open */ - void loadEventId(::NeXus::File & file) - { - // This is the data size - ::NeXus::Info id_info = file.getInfo(); - int64_t dim0 = recalculateDataSize(id_info.dims[0]); - - // Now we allocate the required arrays - m_event_id = new uint32_t[m_loadSize[0]]; - - // Check that the required space is there in the file. - if (dim0 < m_loadSize[0]+m_loadStart[0]) + void loadEventId(::NeXus::File & file) { - alg->getLogger().warning() << "Entry " << entry_name << "'s event_id field is too small (" << dim0 - << ") to load the desired data size (" << m_loadSize[0]+m_loadStart[0] << ").\n"; - m_loadError = true; - } + // This is the data size + ::NeXus::Info id_info = file.getInfo(); + int64_t dim0 = recalculateDataSize(id_info.dims[0]); - if (alg->getCancel()) m_loadError = true; //To allow cancelling the algorithm + // Now we allocate the required arrays + m_event_id = new uint32_t[m_loadSize[0]]; - if (!m_loadError) - { - //Must be uint32 - if (id_info.type == ::NeXus::UINT32) - file.getSlab(m_event_id, m_loadStart, m_loadSize); - else - { - alg->getLogger().warning() << "Entry " << entry_name << "'s event_id field is not UINT32! It will be skipped.\n"; - m_loadError = true; - } - file.closeData(); + // Check that the required space is there in the file. + if (dim0 < m_loadSize[0]+m_loadStart[0]) + { + alg->getLogger().warning() << "Entry " << entry_name << "'s event_id field is too small (" << dim0 + << ") to load the desired data size (" << m_loadSize[0]+m_loadStart[0] << ").\n"; + m_loadError = true; + } - // determine the range of pixel ids - uint32_t temp; - for (auto i = 0; i < m_loadSize[0]; ++i) - { - temp = m_event_id[i]; - if (temp < m_min_id) m_min_id = temp; - if (temp > m_max_id) m_max_id = temp; - } + if (alg->getCancel()) m_loadError = true; //To allow cancelling the algorithm - if ( m_min_id > static_cast(alg->eventid_max) ) - { - // All the detector IDs in the bank are higher than the highest 'known' (from the IDF) - // ID. Setting this will abort the loading of the bank. - m_loadError = true; - } - // fixup the maximum pixel id in the case that it's higher than the highest 'known' id - if (m_max_id > static_cast(alg->eventid_max)) m_max_id = static_cast(alg->eventid_max); - } + if (!m_loadError) + { + //Must be uint32 + if (id_info.type == ::NeXus::UINT32) + file.getSlab(m_event_id, m_loadStart, m_loadSize); + else + { + alg->getLogger().warning() << "Entry " << entry_name << "'s event_id field is not UINT32! It will be skipped.\n"; + m_loadError = true; + } + file.closeData(); - return; - } + // determine the range of pixel ids + uint32_t temp; + for (auto i = 0; i < m_loadSize[0]; ++i) + { + temp = m_event_id[i]; + if (temp < m_min_id) m_min_id = temp; + if (temp > m_max_id) m_max_id = temp; + } - //--------------------------------------------------------------------------------------------------- - /** Open and load the times-of-flight data - */ - void loadTof(::NeXus::File & file) - { - // Allocate the array - float* temp = new float[m_loadSize[0]]; - delete [] m_event_time_of_flight; - m_event_time_of_flight = temp; - - // Get the list of event_time_of_flight's - if (!m_oldNexusFileNames) - file.openData("event_time_offset"); - else - file.openData("event_time_of_flight"); + if ( m_min_id > static_cast(alg->eventid_max) ) + { + // All the detector IDs in the bank are higher than the highest 'known' (from the IDF) + // ID. Setting this will abort the loading of the bank. + m_loadError = true; + } + // fixup the maximum pixel id in the case that it's higher than the highest 'known' id + if (m_max_id > static_cast(alg->eventid_max)) m_max_id = static_cast(alg->eventid_max); + } - // Check that the required space is there in the file. - ::NeXus::Info tof_info = file.getInfo(); - int64_t tof_dim0 = recalculateDataSize(tof_info.dims[0]); - if (tof_dim0 < m_loadSize[0]+m_loadStart[0]) - { - alg->getLogger().warning() << "Entry " << entry_name << "'s event_time_offset field is too small to load the desired data.\n"; - m_loadError = true; + return; } - //Check that the type is what it is supposed to be - if (tof_info.type == ::NeXus::FLOAT32) - file.getSlab(m_event_time_of_flight, m_loadStart, m_loadSize); - else + //--------------------------------------------------------------------------------------------------- + /** Open and load the times-of-flight data + */ + void loadTof(::NeXus::File & file) { - alg->getLogger().warning() << "Entry " << entry_name << "'s event_time_offset field is not FLOAT32! It will be skipped.\n"; - m_loadError = true; - } + // Allocate the array + float* temp = new float[m_loadSize[0]]; + delete [] m_event_time_of_flight; + m_event_time_of_flight = temp; + + // Get the list of event_time_of_flight's + if (!m_oldNexusFileNames) + file.openData("event_time_offset"); + else + file.openData("event_time_of_flight"); + + // Check that the required space is there in the file. + ::NeXus::Info tof_info = file.getInfo(); + int64_t tof_dim0 = recalculateDataSize(tof_info.dims[0]); + if (tof_dim0 < m_loadSize[0]+m_loadStart[0]) + { + alg->getLogger().warning() << "Entry " << entry_name << "'s event_time_offset field is too small to load the desired data.\n"; + m_loadError = true; + } - if (!m_loadError) - { - std::string units; - file.getAttr("units", units); - if (units != "microsecond") + //Check that the type is what it is supposed to be + if (tof_info.type == ::NeXus::FLOAT32) + file.getSlab(m_event_time_of_flight, m_loadStart, m_loadSize); + else { - alg->getLogger().warning() << "Entry " << entry_name << "'s event_time_offset field's units are not microsecond. It will be skipped.\n"; + alg->getLogger().warning() << "Entry " << entry_name << "'s event_time_offset field is not FLOAT32! It will be skipped.\n"; m_loadError = true; } - file.closeData(); - } //no error - return; - } + if (!m_loadError) + { + std::string units; + file.getAttr("units", units); + if (units != "microsecond") + { + alg->getLogger().warning() << "Entry " << entry_name << "'s event_time_offset field's units are not microsecond. It will be skipped.\n"; + m_loadError = true; + } + file.closeData(); + } //no error - //---------------------------------------------------------------------------------------------- - /** Load weight of weigthed events - */ - void loadEventWeights(::NeXus::File &file) - { - try - { - // First, get info about the event_weight field in this bank - file.openData("event_weight"); - } - catch (::NeXus::Exception&) - { - // Field not found error is most likely. - m_have_weight = false; return; } - // OK, we've got them - m_have_weight = true; - // Allocate the array - float* temp = new float[m_loadSize[0]]; - delete [] m_event_weight; - m_event_weight = temp; - - ::NeXus::Info weight_info = file.getInfo(); - int64_t weight_dim0 = recalculateDataSize(weight_info.dims[0]); - if (weight_dim0 < m_loadSize[0]+m_loadStart[0]) + //---------------------------------------------------------------------------------------------- + /** Load weight of weigthed events + */ + void loadEventWeights(::NeXus::File &file) { - alg->getLogger().warning() << "Entry " << entry_name << "'s event_weight field is too small to load the desired data.\n"; - m_loadError = true; - } + try + { + // First, get info about the event_weight field in this bank + file.openData("event_weight"); + } + catch (::NeXus::Exception&) + { + // Field not found error is most likely. + m_have_weight = false; + return; + } + // OK, we've got them + m_have_weight = true; - // Check that the type is what it is supposed to be - if (weight_info.type == ::NeXus::FLOAT32) - file.getSlab(m_event_weight, m_loadStart, m_loadSize); - else - { - alg->getLogger().warning() << "Entry " << entry_name << "'s event_weight field is not FLOAT32! It will be skipped.\n"; - m_loadError = true; + // Allocate the array + float* temp = new float[m_loadSize[0]]; + delete [] m_event_weight; + m_event_weight = temp; + + ::NeXus::Info weight_info = file.getInfo(); + int64_t weight_dim0 = recalculateDataSize(weight_info.dims[0]); + if (weight_dim0 < m_loadSize[0]+m_loadStart[0]) + { + alg->getLogger().warning() << "Entry " << entry_name << "'s event_weight field is too small to load the desired data.\n"; + m_loadError = true; + } + + // Check that the type is what it is supposed to be + if (weight_info.type == ::NeXus::FLOAT32) + file.getSlab(m_event_weight, m_loadStart, m_loadSize); + else + { + alg->getLogger().warning() << "Entry " << entry_name << "'s event_weight field is not FLOAT32! It will be skipped.\n"; + m_loadError = true; + } + + if (!m_loadError) + { + file.closeData(); + } + + return; } - if (!m_loadError) + //--------------------------------------------------------------------------------------------------- + void run() { - file.closeData(); - } + //The vectors we will be filling + std::vector * event_index_ptr = new std::vector(); + std::vector & event_index = *event_index_ptr; - return; - } + // These give the limits in each file as to which events we actually load (when filtering by time). + m_loadStart.resize(1, 0); + m_loadSize.resize(1, 0); - //--------------------------------------------------------------------------------------------------- - void run() - { - //The vectors we will be filling - std::vector * event_index_ptr = new std::vector(); - std::vector & event_index = *event_index_ptr; + // Data arrays + m_event_id = NULL; + m_event_time_of_flight = NULL; + m_event_weight = NULL; - // These give the limits in each file as to which events we actually load (when filtering by time). - m_loadStart.resize(1, 0); - m_loadSize.resize(1, 0); + m_loadError = false; + m_have_weight = alg->m_haveWeights; - // Data arrays - m_event_id = NULL; - m_event_time_of_flight = NULL; - m_event_weight = NULL; + prog->report(entry_name + ": load from disk"); - m_loadError = false; - m_have_weight = alg->m_haveWeights; + // Open the file + ::NeXus::File file(alg->m_filename); + try + { + // Navigate into the file + file.openGroup(alg->m_top_entry_name, "NXentry"); + //Open the bankN_event group + file.openGroup(entry_name, entry_type); - prog->report(entry_name + ": load from disk"); + // Load the event_index field. + this->loadEventIndex(file, event_index); - // Open the file - ::NeXus::File file(alg->m_filename); - try - { - // Navigate into the file - file.openGroup(alg->m_top_entry_name, "NXentry"); - //Open the bankN_event group - file.openGroup(entry_name, entry_type); - - // Load the event_index field. - this->loadEventIndex(file, event_index); - - if (!m_loadError) - { - // Load and validate the pulse times - this->loadPulseTimes(file); - - // The event_index should be the same length as the pulse times from DAS logs. - if (event_index.size() != thisBankPulseTimes->numPulses) - alg->getLogger().warning() << "Bank " << entry_name << " has a mismatch between the number of event_index entries and the number of pulse times in event_time_zero.\n"; + if (!m_loadError) + { + // Load and validate the pulse times + this->loadPulseTimes(file); - // Open and validate event_id field. - size_t start_event = 0; - size_t stop_event = 0; - this->prepareEventId(file, start_event, stop_event, event_index); + // The event_index should be the same length as the pulse times from DAS logs. + if (event_index.size() != thisBankPulseTimes->numPulses) + alg->getLogger().warning() << "Bank " << entry_name << " has a mismatch between the number of event_index entries and the number of pulse times in event_time_zero.\n"; - // These are the arguments to getSlab() - m_loadStart[0] = static_cast(start_event); - m_loadSize[0] = static_cast(stop_event - start_event); + // Open and validate event_id field. + size_t start_event = 0; + size_t stop_event = 0; + this->prepareEventId(file, start_event, stop_event, event_index); - if ((m_loadSize[0] > 0) && (m_loadStart[0]>=0) ) - { - // Load pixel IDs - this->loadEventId(file); - if (alg->getCancel()) m_loadError = true; //To allow cancelling the algorithm + // These are the arguments to getSlab() + m_loadStart[0] = static_cast(start_event); + m_loadSize[0] = static_cast(stop_event - start_event); - // And TOF. - if (!m_loadError) + if ((m_loadSize[0] > 0) && (m_loadStart[0]>=0) ) { - this->loadTof(file); - if (m_have_weight) + // Load pixel IDs + this->loadEventId(file); + if (alg->getCancel()) m_loadError = true; //To allow cancelling the algorithm + + // And TOF. + if (!m_loadError) { - this->loadEventWeights(file); + this->loadTof(file); + if (m_have_weight) + { + this->loadEventWeights(file); + } } + } // Size is at least 1 + else + { + // Found a size that was 0 or less; stop processing + m_loadError=true; } - } // Size is at least 1 - else - { - // Found a size that was 0 or less; stop processing - m_loadError=true; - } - } //no error + } //no error - } // try block - catch (std::exception & e) - { - alg->getLogger().error() << "Error while loading bank " << entry_name << ":" << std::endl; - alg->getLogger().error() << e.what() << std::endl; - m_loadError = true; - } - catch (...) - { - alg->getLogger().error() << "Unspecified error while loading bank " << entry_name << std::endl; - m_loadError = true; - } + } // try block + catch (std::exception & e) + { + alg->getLogger().error() << "Error while loading bank " << entry_name << ":" << std::endl; + alg->getLogger().error() << e.what() << std::endl; + m_loadError = true; + } + catch (...) + { + alg->getLogger().error() << "Unspecified error while loading bank " << entry_name << std::endl; + m_loadError = true; + } - //Close up the file even if errors occured. - file.closeGroup(); - file.close(); + //Close up the file even if errors occured. + file.closeGroup(); + file.close(); - //Abort if anything failed - if (m_loadError) - { - prog->reportIncrement(4, entry_name + ": skipping"); - delete [] m_event_id; - delete [] m_event_time_of_flight; - if (m_have_weight) + //Abort if anything failed + if (m_loadError) { - delete [] m_event_weight; + prog->reportIncrement(4, entry_name + ": skipping"); + delete [] m_event_id; + delete [] m_event_time_of_flight; + if (m_have_weight) + { + delete [] m_event_weight; + } + delete event_index_ptr; + return; } - delete event_index_ptr; - return; - } - // No error? Launch a new task to process that data. - size_t numEvents = m_loadSize[0]; - size_t startAt = m_loadStart[0]; + // No error? Launch a new task to process that data. + size_t numEvents = m_loadSize[0]; + size_t startAt = m_loadStart[0]; - // convert things to shared_arrays - boost::shared_array event_id_shrd(m_event_id); - boost::shared_array event_time_of_flight_shrd(m_event_time_of_flight); - boost::shared_array event_weight_shrd(m_event_weight); - boost::shared_ptr > event_index_shrd(event_index_ptr); + // convert things to shared_arrays + boost::shared_array event_id_shrd(m_event_id); + boost::shared_array event_time_of_flight_shrd(m_event_time_of_flight); + boost::shared_array event_weight_shrd(m_event_weight); + boost::shared_ptr > event_index_shrd(event_index_ptr); - // schedule the job to generate the event lists - auto mid_id = m_max_id; - if (alg->splitProcessing) - mid_id = (m_max_id + m_min_id) / 2; + // schedule the job to generate the event lists + auto mid_id = m_max_id; + if (alg->splitProcessing) + mid_id = (m_max_id + m_min_id) / 2; - ProcessBankData * newTask1 = new ProcessBankData(alg, entry_name, prog,scheduler, + ProcessBankData * newTask1 = new ProcessBankData(alg, entry_name, prog,scheduler, event_id_shrd, event_time_of_flight_shrd, numEvents, startAt, event_index_shrd, thisBankPulseTimes, m_have_weight, event_weight_shrd, m_min_id, mid_id); - scheduler->push(newTask1); - if (alg->splitProcessing) + scheduler->push(newTask1); + if (alg->splitProcessing) + { + ProcessBankData * newTask2 = new ProcessBankData(alg, entry_name, prog,scheduler, + event_id_shrd, event_time_of_flight_shrd, numEvents, startAt, event_index_shrd, + thisBankPulseTimes, m_have_weight, event_weight_shrd, + (mid_id+1), m_max_id); + scheduler->push(newTask2); + } + } + + //--------------------------------------------------------------------------------------------------- + /** + * Interpret the value describing the number of events. If the number is positive return it unchanged. + * If the value is negative (can happen at ISIS) add 2^32 to it. + * @param size :: The size of events value. + */ + int64_t recalculateDataSize(const int64_t& size) { - ProcessBankData * newTask2 = new ProcessBankData(alg, entry_name, prog,scheduler, - event_id_shrd, event_time_of_flight_shrd, numEvents, startAt, event_index_shrd, - thisBankPulseTimes, m_have_weight, event_weight_shrd, - (mid_id+1), m_max_id); - scheduler->push(newTask2); + if (size < 0) + { + const int64_t shift = int64_t(1) << 32; + return shift + size; + } + return size; } - } - //--------------------------------------------------------------------------------------------------- - /** - * Interpret the value describing the number of events. If the number is positive return it unchanged. - * If the value is negative (can happen at ISIS) add 2^32 to it. - * @param size :: The size of events value. + private: + /// Algorithm being run + LoadEventNexus * alg; + /// NXS path to bank + std::string entry_name; + /// NXS type + std::string entry_type; + /// Vector where (index = pixel ID+pixelID_to_wi_offset), value = workspace index) + const std::vector & pixelID_to_wi_vector; + /// Offset in the pixelID_to_wi_vector to use. + detid_t pixelID_to_wi_offset; + /// Progress reporting + Progress * prog; + /// ThreadScheduler running this task + ThreadScheduler * scheduler; + /// Object with the pulse times for this bank + boost::shared_ptr thisBankPulseTimes; + /// Did we get an error in loading + bool m_loadError; + /// Old names in the file? + bool m_oldNexusFileNames; + /// Index to load start at in the file + std::vector m_loadStart; + /// How much to load in the file + std::vector m_loadSize; + /// Event pixel ID data + uint32_t * m_event_id; + /// Minimum pixel ID in this data + uint32_t m_min_id; + /// Maximum pixel ID in this data + uint32_t m_max_id; + /// TOF data + float * m_event_time_of_flight; + /// Flag for simulated data + bool m_have_weight; + /// Event weights + float * m_event_weight; + }; // END-DEF-CLASS LoadBankFromDiskTask + + + //=============================================================================================== + // LoadEventNexus + //=============================================================================================== + + //---------------------------------------------------------------------------------------------- + /** Empty default constructor */ - int64_t recalculateDataSize(const int64_t& size) + LoadEventNexus::LoadEventNexus() : IFileLoader(), + discarded_events(0), event_id_is_spec(false) { - if (size < 0) - { - const int64_t shift = int64_t(1) << 32; - return shift + size; - } - return size; } - private: - /// Algorithm being run - LoadEventNexus * alg; - /// NXS path to bank - std::string entry_name; - /// NXS type - std::string entry_type; - /// Vector where (index = pixel ID+pixelID_to_wi_offset), value = workspace index) - const std::vector & pixelID_to_wi_vector; - /// Offset in the pixelID_to_wi_vector to use. - detid_t pixelID_to_wi_offset; - /// Progress reporting - Progress * prog; - /// ThreadScheduler running this task - ThreadScheduler * scheduler; - /// Object with the pulse times for this bank - boost::shared_ptr thisBankPulseTimes; - /// Did we get an error in loading - bool m_loadError; - /// Old names in the file? - bool m_oldNexusFileNames; - /// Index to load start at in the file - std::vector m_loadStart; - /// How much to load in the file - std::vector m_loadSize; - /// Event pixel ID data - uint32_t * m_event_id; - /// Minimum pixel ID in this data - uint32_t m_min_id; - /// Maximum pixel ID in this data - uint32_t m_max_id; - /// TOF data - float * m_event_time_of_flight; - /// Flag for simulated data - bool m_have_weight; - /// Event weights - float * m_event_weight; - }; // END-DEF-CLASS LoadBankFromDiskTask - - - //=============================================================================================== - // LoadEventNexus - //=============================================================================================== - - //---------------------------------------------------------------------------------------------- - /** Empty default constructor - */ - LoadEventNexus::LoadEventNexus() : IFileLoader(), - discarded_events(0), event_id_is_spec(false) - { - } + //---------------------------------------------------------------------------------------------- + /** Destructor */ + LoadEventNexus::~LoadEventNexus() + { + } - //---------------------------------------------------------------------------------------------- - /** Destructor */ - LoadEventNexus::~LoadEventNexus() - { - } - - //---------------------------------------------------------------------------------------------- - /** - * Return the confidence with with this algorithm can load the file - * @param descriptor A descriptor for the file - * @returns An integer specifying the confidence level. 0 indicates it will not be used - */ - int LoadEventNexus::confidence(Kernel::NexusDescriptor & descriptor) const - { - int confidence(0); - if(descriptor.classTypeExists("NXevent_data")) + //---------------------------------------------------------------------------------------------- + /** + * Return the confidence with with this algorithm can load the file + * @param descriptor A descriptor for the file + * @returns An integer specifying the confidence level. 0 indicates it will not be used + */ + int LoadEventNexus::confidence(Kernel::NexusDescriptor & descriptor) const { - if(descriptor.pathOfTypeExists("/entry", "NXentry") || descriptor.pathOfTypeExists("/raw_data_1", "NXentry")) + int confidence(0); + if(descriptor.classTypeExists("NXevent_data")) { - confidence = 80; + if(descriptor.pathOfTypeExists("/entry", "NXentry") || descriptor.pathOfTypeExists("/raw_data_1", "NXentry")) + { + confidence = 80; + } } + return confidence; } - return confidence; - } - //---------------------------------------------------------------------------------------------- - /** Initialisation method. - */ - void LoadEventNexus::init() - { - std::vector exts; - exts.push_back("_event.nxs"); - exts.push_back(".nxs.h5"); - exts.push_back(".nxs"); - this->declareProperty(new FileProperty("Filename", "", FileProperty::Load, exts), - "The name of the Event NeXus file to read, including its full or relative path. " - "The file name is typically of the form INST_####_event.nxs (N.B. case sensitive if running on Linux)." ); - - this->declareProperty( - new WorkspaceProperty("OutputWorkspace", "", Direction::Output), - "The name of the output EventWorkspace in which to load the EventNexus file." ); - - declareProperty( - new PropertyWithValue("FilterByTofMin", EMPTY_DBL(), Direction::Input), - "Optional: To exclude events that do not fall within a range of times-of-flight. "\ - "This is the minimum accepted value in microseconds. Keep blank to load all events." ); - - declareProperty( - new PropertyWithValue("FilterByTofMax", EMPTY_DBL(), Direction::Input), - "Optional: To exclude events that do not fall within a range of times-of-flight. "\ - "This is the maximum accepted value in microseconds. Keep blank to load all events." ); - - declareProperty( - new PropertyWithValue("FilterByTimeStart", EMPTY_DBL(), Direction::Input), - "Optional: To only include events after the provided start time, in seconds (relative to the start of the run)."); - - declareProperty( - new PropertyWithValue("FilterByTimeStop", EMPTY_DBL(), Direction::Input), - "Optional: To only include events before the provided stop time, in seconds (relative to the start of the run)."); - - std::string grp1 = "Filter Events"; - setPropertyGroup("FilterByTofMin", grp1); - setPropertyGroup("FilterByTofMax", grp1); - setPropertyGroup("FilterByTimeStart", grp1); - setPropertyGroup("FilterByTimeStop", grp1); - - declareProperty( - new PropertyWithValue("NXentryName", "", Direction::Input), - "Optional: Name of the NXentry to load if it's not the default."); - - declareProperty( - new ArrayProperty("BankName", Direction::Input), - "Optional: To only include events from one bank. Any bank whose name does not match the given string will have no events."); - - declareProperty( - new PropertyWithValue("SingleBankPixelsOnly", true, Direction::Input), - "Optional: Only applies if you specified a single bank to load with BankName. " - "Only pixels in the specified bank will be created if true; all of the instrument's pixels will be created otherwise."); - setPropertySettings("SingleBankPixelsOnly", new VisibleWhenProperty("BankName", IS_NOT_DEFAULT) ); - - std::string grp2 = "Loading a Single Bank"; - setPropertyGroup("BankName", grp2); - setPropertyGroup("SingleBankPixelsOnly", grp2); - - declareProperty( - new PropertyWithValue("Precount", true, Direction::Input), - "Pre-count the number of events in each pixel before allocating memory (optional, default False). " - "This can significantly reduce memory use and memory fragmentation; it may also speed up loading."); - - declareProperty( - new PropertyWithValue("CompressTolerance", -1.0, Direction::Input), - "Run CompressEvents while loading (optional, leave blank or negative to not do). " - "This specified the tolerance to use (in microseconds) when compressing."); - - auto mustBePositive = boost::make_shared >(); - mustBePositive->setLower(1); - declareProperty("ChunkNumber", EMPTY_INT(), mustBePositive, - "If loading the file by sections ('chunks'), this is the section number of this execution of the algorithm."); - declareProperty("TotalChunks", EMPTY_INT(), mustBePositive, - "If loading the file by sections ('chunks'), this is the total number of sections."); - // TotalChunks is only meaningful if ChunkNumber is set - // Would be nice to be able to restrict ChunkNumber to be <= TotalChunks at validation - setPropertySettings("TotalChunks", new VisibleWhenProperty("ChunkNumber", IS_NOT_DEFAULT)); - - std::string grp3 = "Reduce Memory Use"; - setPropertyGroup("Precount", grp3); - setPropertyGroup("CompressTolerance", grp3); - setPropertyGroup("ChunkNumber", grp3); - setPropertyGroup("TotalChunks", grp3); - - declareProperty( - new PropertyWithValue("LoadMonitors", false, Direction::Input), - "Load the monitors from the file (optional, default False)."); - - declareProperty(new PropertyWithValue("MonitorsAsEvents", false, Direction::Input), - "If present, load the monitors as events. '''WARNING:''' WILL SIGNIFICANTLY INCREASE MEMORY USAGE (optional, default False). "); - - declareProperty( - new PropertyWithValue("FilterMonByTofMin", EMPTY_DBL(), Direction::Input), - "Optional: To exclude events from monitors that do not fall within a range of times-of-flight. "\ - "This is the minimum accepted value in microseconds." ); - - declareProperty( - new PropertyWithValue("FilterMonByTofMax", EMPTY_DBL(), Direction::Input), - "Optional: To exclude events from monitors that do not fall within a range of times-of-flight. "\ - "This is the maximum accepted value in microseconds." ); - - declareProperty( - new PropertyWithValue("FilterMonByTimeStart", EMPTY_DBL(), Direction::Input), - "Optional: To only include events from monitors after the provided start time, in seconds (relative to the start of the run)."); - - declareProperty( - new PropertyWithValue("FilterMonByTimeStop", EMPTY_DBL(), Direction::Input), - "Optional: To only include events from monitors before the provided stop time, in seconds (relative to the start of the run)."); - - setPropertySettings("MonitorsAsEvents", new VisibleWhenProperty("LoadMonitors", IS_EQUAL_TO, "1") ); - IPropertySettings *asEventsIsOn = new VisibleWhenProperty("MonitorsAsEvents", IS_EQUAL_TO, "1"); - setPropertySettings("FilterMonByTofMin", asEventsIsOn); - setPropertySettings("FilterMonByTofMax", asEventsIsOn->clone()); - setPropertySettings("FilterMonByTimeStart", asEventsIsOn->clone()); - setPropertySettings("FilterMonByTimeStop", asEventsIsOn->clone()); - - std::string grp4 = "Monitors"; - setPropertyGroup("LoadMonitors", grp4); - setPropertyGroup("MonitorsAsEvents", grp4); - setPropertyGroup("FilterMonByTofMin", grp4); - setPropertyGroup("FilterMonByTofMax", grp4); - setPropertyGroup("FilterMonByTimeStart", grp4); - setPropertyGroup("FilterMonByTimeStop", grp4); - - declareProperty( - new PropertyWithValue("MetaDataOnly", false, Direction::Input), - "If true, only the meta data and sample logs will be loaded."); - - declareProperty( - new PropertyWithValue("LoadLogs", true, Direction::Input), - "Load the Sample/DAS logs from the file (default True)."); - } - - //---------------------------------------------------------------------------------------------- - /** set the name of the top level NXentry m_top_entry_name + //---------------------------------------------------------------------------------------------- + /** Initialisation method. */ - void LoadEventNexus::setTopEntryName() - { - std::string nxentryProperty = getProperty("NXentryName"); - if (nxentryProperty.size()>0) + void LoadEventNexus::init() { - m_top_entry_name = nxentryProperty; - return; + std::vector exts; + exts.push_back("_event.nxs"); + exts.push_back(".nxs.h5"); + exts.push_back(".nxs"); + this->declareProperty(new FileProperty("Filename", "", FileProperty::Load, exts), + "The name of the Event NeXus file to read, including its full or relative path. " + "The file name is typically of the form INST_####_event.nxs (N.B. case sensitive if running on Linux)." ); + + this->declareProperty( + new WorkspaceProperty("OutputWorkspace", "", Direction::Output), + "The name of the output EventWorkspace in which to load the EventNexus file." ); + + declareProperty( + new PropertyWithValue("FilterByTofMin", EMPTY_DBL(), Direction::Input), + "Optional: To exclude events that do not fall within a range of times-of-flight. "\ + "This is the minimum accepted value in microseconds. Keep blank to load all events." ); + + declareProperty( + new PropertyWithValue("FilterByTofMax", EMPTY_DBL(), Direction::Input), + "Optional: To exclude events that do not fall within a range of times-of-flight. "\ + "This is the maximum accepted value in microseconds. Keep blank to load all events." ); + + declareProperty( + new PropertyWithValue("FilterByTimeStart", EMPTY_DBL(), Direction::Input), + "Optional: To only include events after the provided start time, in seconds (relative to the start of the run)."); + + declareProperty( + new PropertyWithValue("FilterByTimeStop", EMPTY_DBL(), Direction::Input), + "Optional: To only include events before the provided stop time, in seconds (relative to the start of the run)."); + + std::string grp1 = "Filter Events"; + setPropertyGroup("FilterByTofMin", grp1); + setPropertyGroup("FilterByTofMax", grp1); + setPropertyGroup("FilterByTimeStart", grp1); + setPropertyGroup("FilterByTimeStop", grp1); + + declareProperty( + new PropertyWithValue("NXentryName", "", Direction::Input), + "Optional: Name of the NXentry to load if it's not the default."); + + declareProperty( + new ArrayProperty("BankName", Direction::Input), + "Optional: To only include events from one bank. Any bank whose name does not match the given string will have no events."); + + declareProperty( + new PropertyWithValue("SingleBankPixelsOnly", true, Direction::Input), + "Optional: Only applies if you specified a single bank to load with BankName. " + "Only pixels in the specified bank will be created if true; all of the instrument's pixels will be created otherwise."); + setPropertySettings("SingleBankPixelsOnly", new VisibleWhenProperty("BankName", IS_NOT_DEFAULT) ); + + std::string grp2 = "Loading a Single Bank"; + setPropertyGroup("BankName", grp2); + setPropertyGroup("SingleBankPixelsOnly", grp2); + + declareProperty( + new PropertyWithValue("Precount", true, Direction::Input), + "Pre-count the number of events in each pixel before allocating memory (optional, default False). " + "This can significantly reduce memory use and memory fragmentation; it may also speed up loading."); + + declareProperty( + new PropertyWithValue("CompressTolerance", -1.0, Direction::Input), + "Run CompressEvents while loading (optional, leave blank or negative to not do). " + "This specified the tolerance to use (in microseconds) when compressing."); + + auto mustBePositive = boost::make_shared >(); + mustBePositive->setLower(1); + declareProperty("ChunkNumber", EMPTY_INT(), mustBePositive, + "If loading the file by sections ('chunks'), this is the section number of this execution of the algorithm."); + declareProperty("TotalChunks", EMPTY_INT(), mustBePositive, + "If loading the file by sections ('chunks'), this is the total number of sections."); + // TotalChunks is only meaningful if ChunkNumber is set + // Would be nice to be able to restrict ChunkNumber to be <= TotalChunks at validation + setPropertySettings("TotalChunks", new VisibleWhenProperty("ChunkNumber", IS_NOT_DEFAULT)); + + std::string grp3 = "Reduce Memory Use"; + setPropertyGroup("Precount", grp3); + setPropertyGroup("CompressTolerance", grp3); + setPropertyGroup("ChunkNumber", grp3); + setPropertyGroup("TotalChunks", grp3); + + declareProperty( + new PropertyWithValue("LoadMonitors", false, Direction::Input), + "Load the monitors from the file (optional, default False)."); + + declareProperty(new PropertyWithValue("MonitorsAsEvents", false, Direction::Input), + "If present, load the monitors as events. '''WARNING:''' WILL SIGNIFICANTLY INCREASE MEMORY USAGE (optional, default False). "); + + declareProperty( + new PropertyWithValue("FilterMonByTofMin", EMPTY_DBL(), Direction::Input), + "Optional: To exclude events from monitors that do not fall within a range of times-of-flight. "\ + "This is the minimum accepted value in microseconds." ); + + declareProperty( + new PropertyWithValue("FilterMonByTofMax", EMPTY_DBL(), Direction::Input), + "Optional: To exclude events from monitors that do not fall within a range of times-of-flight. "\ + "This is the maximum accepted value in microseconds." ); + + declareProperty( + new PropertyWithValue("FilterMonByTimeStart", EMPTY_DBL(), Direction::Input), + "Optional: To only include events from monitors after the provided start time, in seconds (relative to the start of the run)."); + + declareProperty( + new PropertyWithValue("FilterMonByTimeStop", EMPTY_DBL(), Direction::Input), + "Optional: To only include events from monitors before the provided stop time, in seconds (relative to the start of the run)."); + + setPropertySettings("MonitorsAsEvents", new VisibleWhenProperty("LoadMonitors", IS_EQUAL_TO, "1") ); + IPropertySettings *asEventsIsOn = new VisibleWhenProperty("MonitorsAsEvents", IS_EQUAL_TO, "1"); + setPropertySettings("FilterMonByTofMin", asEventsIsOn); + setPropertySettings("FilterMonByTofMax", asEventsIsOn->clone()); + setPropertySettings("FilterMonByTimeStart", asEventsIsOn->clone()); + setPropertySettings("FilterMonByTimeStop", asEventsIsOn->clone()); + + std::string grp4 = "Monitors"; + setPropertyGroup("LoadMonitors", grp4); + setPropertyGroup("MonitorsAsEvents", grp4); + setPropertyGroup("FilterMonByTofMin", grp4); + setPropertyGroup("FilterMonByTofMax", grp4); + setPropertyGroup("FilterMonByTimeStart", grp4); + setPropertyGroup("FilterMonByTimeStop", grp4); + + declareProperty( + new PropertyWithValue("MetaDataOnly", false, Direction::Input), + "If true, only the meta data and sample logs will be loaded."); + + declareProperty( + new PropertyWithValue("LoadLogs", true, Direction::Input), + "Load the Sample/DAS logs from the file (default True)."); } - typedef std::map string_map_t; - try + + //---------------------------------------------------------------------------------------------- + /** set the name of the top level NXentry m_top_entry_name + */ + void LoadEventNexus::setTopEntryName() { - string_map_t::const_iterator it; - ::NeXus::File file = ::NeXus::File(m_filename); - string_map_t entries = file.getEntries(); + std::string nxentryProperty = getProperty("NXentryName"); + if (nxentryProperty.size()>0) + { + m_top_entry_name = nxentryProperty; + return; + } + typedef std::map string_map_t; + try + { + string_map_t::const_iterator it; + ::NeXus::File file = ::NeXus::File(m_filename); + string_map_t entries = file.getEntries(); - // Choose the first entry as the default - m_top_entry_name = entries.begin()->first; + // Choose the first entry as the default + m_top_entry_name = entries.begin()->first; - for (it = entries.begin(); it != entries.end(); ++it) - { - if ( ((it->first == "entry") || (it->first == "raw_data_1")) && (it->second == "NXentry") ) + for (it = entries.begin(); it != entries.end(); ++it) { - m_top_entry_name = it->first; - break; + if ( ((it->first == "entry") || (it->first == "raw_data_1")) && (it->second == "NXentry") ) + { + m_top_entry_name = it->first; + break; + } } } + catch(const std::exception&) + { + g_log.error() << "Unable to determine name of top level NXentry - assuming \"entry\"." << std::endl; + m_top_entry_name = "entry"; + } } - catch(const std::exception&) - { - g_log.error() << "Unable to determine name of top level NXentry - assuming \"entry\"." << std::endl; - m_top_entry_name = "entry"; - } - } - //------------------------------------------------------------------------------------------------ - /** Executes the algorithm. Reading in the file and creating and populating - * the output workspace - */ - void LoadEventNexus::exec() - { - // Retrieve the filename from the properties - m_filename = getPropertyValue("Filename"); + //------------------------------------------------------------------------------------------------ + /** Executes the algorithm. Reading in the file and creating and populating + * the output workspace + */ + void LoadEventNexus::exec() + { + // Retrieve the filename from the properties + m_filename = getPropertyValue("Filename"); - precount = getProperty("Precount"); - compressTolerance = getProperty("CompressTolerance"); + precount = getProperty("Precount"); + compressTolerance = getProperty("CompressTolerance"); - loadlogs = getProperty("LoadLogs"); + loadlogs = getProperty("LoadLogs"); - // Check to see if the monitors need to be loaded later - bool load_monitors = this->getProperty("LoadMonitors"); - setTopEntryName(); + // Check to see if the monitors need to be loaded later + bool load_monitors = this->getProperty("LoadMonitors"); + setTopEntryName(); - //Initialize progress reporting. - int reports = 3; - if (load_monitors) - reports++; - Progress prog(this,0.0,0.3, reports); - - // Load the detector events - WS = createEmptyEventWorkspace(); // Algorithm currently relies on an object-level workspace ptr - loadEvents(&prog, false); // Do not load monitor blocks + //Initialize progress reporting. + int reports = 3; + if (load_monitors) + reports++; + Progress prog(this,0.0,0.3, reports); - if ( discarded_events > 0 ) - { - g_log.information() << discarded_events - << " events were encountered coming from pixels which are not in the Instrument Definition File." - "These events were discarded.\n"; - } - - // If the run was paused at any point, filter out those events (SNS only, I think) - filterDuringPause(WS); - - //add filename - WS->mutableRun().addProperty("Filename",m_filename); - //Save output - this->setProperty("OutputWorkspace", WS); - // Load the monitors - if (load_monitors) - { - prog.report("Loading monitors"); - const bool eventMonitors = getProperty("MonitorsAsEvents"); - if( eventMonitors && this->hasEventMonitors() ) - { - // Note the reuse of the WS member variable below. Means I need to grab a copy of its current value. - auto dataWS = WS; + // Load the detector events WS = createEmptyEventWorkspace(); // Algorithm currently relies on an object-level workspace ptr - //add filename - WS->mutableRun().addProperty("Filename",m_filename); - // Perform the load - loadEvents(&prog, true); - std::string mon_wsname = this->getProperty("OutputWorkspace"); - mon_wsname.append("_monitors"); - this->declareProperty(new WorkspaceProperty - ("MonitorWorkspace", mon_wsname, Direction::Output), "Monitors from the Event NeXus file"); - this->setProperty("MonitorWorkspace", WS); - // Set the internal monitor workspace pointer as well - dataWS->setMonitorWorkspace(WS); + loadEvents(&prog, false); // Do not load monitor blocks + + if ( discarded_events > 0 ) + { + g_log.information() << discarded_events + << " events were encountered coming from pixels which are not in the Instrument Definition File." + "These events were discarded.\n"; + } + // If the run was paused at any point, filter out those events (SNS only, I think) filterDuringPause(WS); - } - else - { - this->runLoadMonitors(); - } - } - // Some memory feels like it sticks around (on Linux). Free it. - MemoryManager::Instance().releaseFreeMemory(); + //add filename + WS->mutableRun().addProperty("Filename",m_filename); + //Save output + this->setProperty("OutputWorkspace", WS); + // Load the monitors + if (load_monitors) + { + prog.report("Loading monitors"); + const bool eventMonitors = getProperty("MonitorsAsEvents"); + if( eventMonitors && this->hasEventMonitors() ) + { + // Note the reuse of the WS member variable below. Means I need to grab a copy of its current value. + auto dataWS = WS; + WS = createEmptyEventWorkspace(); // Algorithm currently relies on an object-level workspace ptr + //add filename + WS->mutableRun().addProperty("Filename",m_filename); + // Perform the load + loadEvents(&prog, true); + std::string mon_wsname = this->getProperty("OutputWorkspace"); + mon_wsname.append("_monitors"); + this->declareProperty(new WorkspaceProperty + ("MonitorWorkspace", mon_wsname, Direction::Output), "Monitors from the Event NeXus file"); + this->setProperty("MonitorWorkspace", WS); + // Set the internal monitor workspace pointer as well + dataWS->setMonitorWorkspace(WS); + // If the run was paused at any point, filter out those events (SNS only, I think) + filterDuringPause(WS); + } + else + { + this->runLoadMonitors(); + } + } - return; -} + // Some memory feels like it sticks around (on Linux). Free it. + MemoryManager::Instance().releaseFreeMemory(); + return; + } -//----------------------------------------------------------------------------- -/** Generate a look-up table where the index = the pixel ID of an event - * and the value = a pointer to the EventList in the workspace - * @param vectors :: the array to create the map on - */ -template -void LoadEventNexus::makeMapToEventLists(std::vector & vectors) -{ - if( this->event_id_is_spec ) - { - // Find max spectrum no - Axis *ax1 = WS->getAxis(1); - specid_t maxSpecNo = -std::numeric_limits::max(); // So that any number will be greater than this - for (size_t i=0; i < ax1->length(); i++) - { - specid_t spec = ax1->spectraNo(i); - if (spec > maxSpecNo) maxSpecNo = spec; - } - // These are used by the bank loader to figure out where to put the events - // The index of eventVectors is a spectrum number so it is simply resized to the maximum - // possible spectrum number - eventid_max = maxSpecNo; - vectors.resize(maxSpecNo+1, NULL); - for(size_t i = 0; i < WS->getNumberHistograms(); ++i) + //----------------------------------------------------------------------------- + /** Generate a look-up table where the index = the pixel ID of an event + * and the value = a pointer to the EventList in the workspace + * @param vectors :: the array to create the map on + */ + template + void LoadEventNexus::makeMapToEventLists(std::vector & vectors) { - const ISpectrum * spec = WS->getSpectrum(i); - if(spec) + if( this->event_id_is_spec ) { - getEventsFrom(WS->getEventList(i), vectors[spec->getSpectrumNo()]); + // Find max spectrum no + Axis *ax1 = WS->getAxis(1); + specid_t maxSpecNo = -std::numeric_limits::max(); // So that any number will be greater than this + for (size_t i=0; i < ax1->length(); i++) + { + specid_t spec = ax1->spectraNo(i); + if (spec > maxSpecNo) maxSpecNo = spec; + } + + // These are used by the bank loader to figure out where to put the events + // The index of eventVectors is a spectrum number so it is simply resized to the maximum + // possible spectrum number + eventid_max = maxSpecNo; + vectors.resize(maxSpecNo+1, NULL); + for(size_t i = 0; i < WS->getNumberHistograms(); ++i) + { + const ISpectrum * spec = WS->getSpectrum(i); + if(spec) + { + getEventsFrom(WS->getEventList(i), vectors[spec->getSpectrumNo()]); + } + } } - } - } - else - { - // To avoid going out of range in the vector, this is the MAX index that can go into it - eventid_max = static_cast(pixelID_to_wi_vector.size()) + pixelID_to_wi_offset; - - // Make an array where index = pixel ID - // Set the value to NULL by default - vectors.resize(eventid_max+1, NULL); - - for (size_t j=size_t(pixelID_to_wi_offset); jgetNumberHistograms() ) + else { - getEventsFrom(WS->getEventList(wi), vectors[j-pixelID_to_wi_offset]); - } - } - } -} - -/** - * Get the number of events in the currently opened group. - * - * @param file The handle to the nexus file opened to the group to look at. - * @param hasTotalCounts Whether to try looking at the total_counts field. This - * variable will be changed if the field is not there. - * @param oldNeXusFileNames Whether to try using old names. This variable will - * be changed if it is determined that old names are being used. - * - * @return The number of events. - */ -std::size_t numEvents(::NeXus::File &file, bool &hasTotalCounts, bool &oldNeXusFileNames) -{ - // try getting the value of total_counts - if (hasTotalCounts) - { - try - { - uint64_t numEvents; - file.readData("total_counts", numEvents); - return numEvents; - } - catch (::NeXus::Exception& ) - { - hasTotalCounts=false; // carry on with the field not existing - } - } - - // just get the length of the event pixel ids - try - { - if (oldNeXusFileNames) - file.openData("event_pixel_id"); - else - file.openData("event_id"); - } - catch (::NeXus::Exception& ) - { - // Older files (before Nov 5, 2010) used this field. - try - { - file.openData("event_pixel_id"); - oldNeXusFileNames = true; - } - catch(::NeXus::Exception&) - { - // Some groups have neither indicating there are not events here - return 0; - } - } - - size_t numEvents = static_cast(file.getInfo().dims[0]); - file.closeData(); - return numEvents; -} - -//----------------------------------------------------------------------------- -/** - * Load events from the file - * @param prog :: A pointer to the progress reporting object - * @param monitors :: If true the events from the monitors are loaded and not the main banks - */ -void LoadEventNexus::loadEvents(API::Progress * const prog, const bool monitors) -{ - bool metaDataOnly = getProperty("MetaDataOnly"); + // To avoid going out of range in the vector, this is the MAX index that can go into it + eventid_max = static_cast(pixelID_to_wi_vector.size()) + pixelID_to_wi_offset; - // Get the time filters - setTimeFilters(monitors); + // Make an array where index = pixel ID + // Set the value to NULL by default + vectors.resize(eventid_max+1, NULL); - // The run_start will be loaded from the pulse times. - DateAndTime run_start(0,0); - // Initialize the counter of bad TOFs - bad_tofs = 0; + for (size_t j=size_t(pixelID_to_wi_offset); jgetNumberHistograms() ) + { + getEventsFrom(WS->getEventList(wi), vectors[j-pixelID_to_wi_offset]); + } + } + } + } - if (loadlogs) - { - prog->doReport("Loading DAS logs"); - m_allBanksPulseTimes = runLoadNexusLogs(m_filename, WS, *this, true); - run_start = WS->getFirstPulseTime(); - } - else - { - g_log.information() << "Skipping the loading of sample logs!\n" - << "Reading the start time directly from /" << m_top_entry_name - << "/start_time\n"; - // start_time is read and set - ::NeXus::File nxfile(m_filename); - nxfile.openGroup(m_top_entry_name, "NXentry"); - std::string tmp; - nxfile.readData("start_time", tmp); - run_start = DateAndTime(tmp); - WS->mutableRun().addProperty("run_start", run_start.toISO8601String(), true ); - } - - // Make sure you have a non-NULL m_allBanksPulseTimes - if (m_allBanksPulseTimes == NULL) - { - std::vector temp; - // m_allBanksPulseTimes = new BankPulseTimes(temp); - m_allBanksPulseTimes = boost::make_shared(temp); - } - - - //Load the instrument - prog->report("Loading instrument"); - instrument_loaded_correctly = loadInstrument(m_filename, WS, m_top_entry_name, this); - - if (!this->instrument_loaded_correctly) - throw std::runtime_error("Instrument was not initialized correctly! Loading cannot continue."); - - - // top level file information - ::NeXus::File file(m_filename); - - //Start with the base entry - file.openGroup(m_top_entry_name, "NXentry"); - - //Now we want to go through all the bankN_event entries - vector bankNames; - vector bankNumEvents; - size_t total_events = 0; - map entries = file.getEntries(); - map::const_iterator it = entries.begin(); - std::string classType = monitors ? "NXmonitor" : "NXevent_data"; - ::NeXus::Info info; - bool oldNeXusFileNames(false); - bool hasTotalCounts(true); - m_haveWeights = false; - for (; it != entries.end(); ++it) - { - std::string entry_name(it->first); - std::string entry_class(it->second); - if ( entry_class == classType ) + /** + * Get the number of events in the currently opened group. + * + * @param file The handle to the nexus file opened to the group to look at. + * @param hasTotalCounts Whether to try looking at the total_counts field. This + * variable will be changed if the field is not there. + * @param oldNeXusFileNames Whether to try using old names. This variable will + * be changed if it is determined that old names are being used. + * + * @return The number of events. + */ + std::size_t numEvents(::NeXus::File &file, bool &hasTotalCounts, bool &oldNeXusFileNames) { - // open the group - file.openGroup(entry_name, classType); - - // get the number of events - std::size_t num = numEvents(file, hasTotalCounts, oldNeXusFileNames); - bankNames.push_back( entry_name ); - bankNumEvents.push_back(num); - total_events += num; + // try getting the value of total_counts + if (hasTotalCounts) + { + try + { + uint64_t numEvents; + file.readData("total_counts", numEvents); + return numEvents; + } + catch (::NeXus::Exception& ) + { + hasTotalCounts=false; // carry on with the field not existing + } + } - // Look for weights in simulated file + // just get the length of the event pixel ids try { - file.openData("event_weight"); - m_haveWeights = true; - file.closeData(); + if (oldNeXusFileNames) + file.openData("event_pixel_id"); + else + file.openData("event_id"); } - catch (::NeXus::Exception &) + catch (::NeXus::Exception& ) { - // Swallow exception since flag is already false; + // Older files (before Nov 5, 2010) used this field. + try + { + file.openData("event_pixel_id"); + oldNeXusFileNames = true; + } + catch(::NeXus::Exception&) + { + // Some groups have neither indicating there are not events here + return 0; + } } - file.closeGroup(); + size_t numEvents = static_cast(file.getInfo().dims[0]); + file.closeData(); + return numEvents; } - } - - loadSampleDataISIScompatibility(file, WS); - - //Close up the file - file.closeGroup(); - file.close(); - - // Delete the output workspace name if it existed - std::string outName = getPropertyValue("OutputWorkspace"); - if (AnalysisDataService::Instance().doesExist(outName)) - AnalysisDataService::Instance().remove( outName ); - // set more properties on the workspace - try - { - // this is a static method that is why it is passing the file path - loadEntryMetadata(m_filename, WS, m_top_entry_name); - } - catch (std::runtime_error & e) - { - // Missing metadata is not a fatal error. Log and go on with your life - g_log.error() << "Error loading metadata: " << e.what() << std::endl; - } - - // --------------------------- Time filtering ------------------------------------ - double filter_time_start_sec, filter_time_stop_sec; - filter_time_start_sec = getProperty("FilterByTimeStart"); - filter_time_stop_sec = getProperty("FilterByTimeStop"); - chunk = getProperty("ChunkNumber"); - totalChunks = getProperty("TotalChunks"); - - //Default to ALL pulse times - bool is_time_filtered = false; - filter_time_start = Kernel::DateAndTime::minimum(); - filter_time_stop = Kernel::DateAndTime::maximum(); - - if (m_allBanksPulseTimes->numPulses > 0) - { - //If not specified, use the limits of doubles. Otherwise, convert from seconds to absolute PulseTime - if (filter_time_start_sec != EMPTY_DBL()) + //----------------------------------------------------------------------------- + /** + * Load events from the file + * @param prog :: A pointer to the progress reporting object + * @param monitors :: If true the events from the monitors are loaded and not the main banks + */ + void LoadEventNexus::loadEvents(API::Progress * const prog, const bool monitors) { - filter_time_start = run_start + filter_time_start_sec; - is_time_filtered = true; - } + bool metaDataOnly = getProperty("MetaDataOnly"); - if (filter_time_stop_sec != EMPTY_DBL()) - { - filter_time_stop = run_start + filter_time_stop_sec; - is_time_filtered = true; - } + // Get the time filters + setTimeFilters(monitors); - //Silly values? - if (filter_time_stop < filter_time_start) - { - std::string msg = "Your "; - if(monitors) msg += "monitor "; - msg += "filter for time's Stop value is smaller than the Start value."; - throw std::invalid_argument(msg); - } - } + // The run_start will be loaded from the pulse times. + DateAndTime run_start(0,0); + // Initialize the counter of bad TOFs + bad_tofs = 0; - if (is_time_filtered) - { - //Now filter out the run, using the DateAndTime type. - WS->mutableRun().filterByTime(filter_time_start, filter_time_stop); - } - - if(metaDataOnly) { - //Now, create a default X-vector for histogramming, with just 2 bins. - Kernel::cow_ptr axis; - MantidVec& xRef = axis.access(); - xRef.resize(2); - xRef[0] = static_cast(std::numeric_limits::max()) * 0.1 - 1; //Just to make sure the bins hold it all - xRef[1] = 1; - //Set the binning axis using this. - WS->setAllX(axis); - return; - } - - // --------- Loading only one bank ? ---------------------------------- - std::vector someBanks = getProperty("BankName"); - bool SingleBankPixelsOnly = getProperty("SingleBankPixelsOnly"); - if ((!someBanks.empty()) && (!monitors)) - { - // check that all of the requested banks are in the file - for (auto someBank = someBanks.begin(); someBank != someBanks.end(); ++someBank) - { - bool foundIt = false; - for (auto bankName = bankNames.begin(); bankName != bankNames.end(); ++bankName) + if (loadlogs) { - if ((*bankName) == (*someBank)+"_events") - { - foundIt = true; - break; - } + prog->doReport("Loading DAS logs"); + m_allBanksPulseTimes = runLoadNexusLogs(m_filename, WS, *this, true); + run_start = WS->getFirstPulseTime(); } - if (!foundIt) + else { - throw std::invalid_argument("No entry named '" + (*someBank) + "' was found in the .NXS file.\n"); + g_log.information() << "Skipping the loading of sample logs!\n" + << "Reading the start time directly from /" << m_top_entry_name + << "/start_time\n"; + // start_time is read and set + ::NeXus::File nxfile(m_filename); + nxfile.openGroup(m_top_entry_name, "NXentry"); + std::string tmp; + nxfile.readData("start_time", tmp); + run_start = DateAndTime(tmp); + WS->mutableRun().addProperty("run_start", run_start.toISO8601String(), true ); } - } - // change the number of banks to load - bankNames.clear(); - for (auto someBank = someBanks.begin(); someBank != someBanks.end(); ++someBank) - bankNames.push_back((*someBank) + "_events"); + // Make sure you have a non-NULL m_allBanksPulseTimes + if (m_allBanksPulseTimes == NULL) + { + std::vector temp; + // m_allBanksPulseTimes = new BankPulseTimes(temp); + m_allBanksPulseTimes = boost::make_shared(temp); + } - // how many events are in a bank - bankNumEvents.clear(); - bankNumEvents.assign(someBanks.size(), 1); // TODO this equally weights the banks - if( !SingleBankPixelsOnly ) someBanks.clear(); // Marker to load all pixels - } - else - { - someBanks.clear(); - } + //Load the instrument + prog->report("Loading instrument"); + instrument_loaded_correctly = loadInstrument(m_filename, WS, m_top_entry_name, this); - prog->report("Initializing all pixels"); - // Remove used banks if parameter is set - if (WS->getInstrument()->hasParameter("remove-unused-banks")) - { - std::vector instrumentUnused = WS->getInstrument()->getNumberParameter("remove-unused-banks", true); - if (!instrumentUnused.empty()) - { - const int unused = static_cast(instrumentUnused.front()); - if(unused == 1) deleteBanks(WS, bankNames); - } - } - //----------------- Pad Empty Pixels ------------------------------- - // Create the required spectra mapping so that the workspace knows what to pad to - createSpectraMapping(m_filename, monitors, someBanks); - - //This map will be used to find the workspace index - if( this->event_id_is_spec ) - WS->getSpectrumToWorkspaceIndexVector(pixelID_to_wi_vector, pixelID_to_wi_offset); - else - WS->getDetectorIDToWorkspaceIndexVector(pixelID_to_wi_vector, pixelID_to_wi_offset, true); - - // Cache a map for speed. - if (!m_haveWeights) - { - this->makeMapToEventLists(eventVectors); - } - else - { - // Convert to weighted events - for (size_t i=0; i < WS->getNumberHistograms(); i++) - { - WS->getEventList(i).switchTo(API::WEIGHTED); - } - this->makeMapToEventLists(weightedEventVectors); - } + if (!this->instrument_loaded_correctly) + throw std::runtime_error("Instrument was not initialized correctly! Loading cannot continue."); - // Set all (empty) event lists as sorted by pulse time. That way, calling SortEvents will not try to sort these empty lists. - for (size_t i=0; i < WS->getNumberHistograms(); i++) - WS->getEventList(i).setSortOrder(DataObjects::PULSETIME_SORT); - //Count the limits to time of flight - shortest_tof = static_cast(std::numeric_limits::max()) * 0.1; - longest_tof = 0.; + // top level file information + ::NeXus::File file(m_filename); - // Make the thread pool - ThreadScheduler * scheduler = new ThreadSchedulerMutexes(); - ThreadPool pool(scheduler); - auto diskIOMutex = boost::make_shared(); - size_t bank0 = 0; - size_t bankn = bankNames.size(); + //Start with the base entry + file.openGroup(m_top_entry_name, "NXentry"); - if (chunk != EMPTY_INT()) // We are loading part - work out the bank number range - { - eventsPerChunk = total_events / totalChunks; - // Sort banks by size - size_t tmp; - string stmp; - for (size_t i = 0; i < bankn; i++) - for (size_t j = 0; j < bankn - 1; j++) - if (bankNumEvents[j] < bankNumEvents[j + 1]) - { - tmp = bankNumEvents[j]; - bankNumEvents[j] = bankNumEvents[j + 1]; - bankNumEvents[j + 1] = tmp; - stmp = bankNames[j]; - bankNames[j] = bankNames[j + 1]; - bankNames[j + 1] = stmp; - } - int bigBanks = 0; - for (size_t i = 0; i < bankn; i++) if (bankNumEvents[i] > eventsPerChunk)bigBanks++; - // Each chunk is part of bank or multiple whole banks - // 0.5 for last chunk of a bank with multiple chunks - // 0.1 for multiple whole banks not completely filled - eventsPerChunk += static_cast((static_cast(bigBanks) / static_cast(totalChunks) * - 0.5 + 0.05) * static_cast(eventsPerChunk)); - double partialChunk = 0.; - firstChunkForBank = 1; - for (int chunki = 1; chunki <=chunk; chunki++) - { - if (partialChunk > 1.) - { - partialChunk = 0.; - firstChunkForBank = chunki; - bank0 = bankn; - } - if (bankNumEvents[bank0] > 1) + //Now we want to go through all the bankN_event entries + vector bankNames; + vector bankNumEvents; + size_t total_events = 0; + map entries = file.getEntries(); + map::const_iterator it = entries.begin(); + std::string classType = monitors ? "NXmonitor" : "NXevent_data"; + ::NeXus::Info info; + bool oldNeXusFileNames(false); + bool hasTotalCounts(true); + m_haveWeights = false; + for (; it != entries.end(); ++it) { - partialChunk += static_cast(eventsPerChunk)/static_cast(bankNumEvents[bank0]); - } - if (chunki < totalChunks) bankn = bank0 + 1; - else bankn = bankNames.size(); - if (chunki == firstChunkForBank && partialChunk > 1.0) bankn += static_cast(partialChunk) - 1; - if (bankn > bankNames.size()) bankn = bankNames.size(); - } - for (size_t i=bank0; i < bankn; i++) - { - size_t start_event = (chunk - firstChunkForBank) * eventsPerChunk; - size_t stop_event = bankNumEvents[i]; - // Don't change stop_event for the final chunk - if ( start_event + eventsPerChunk < stop_event ) stop_event = start_event + eventsPerChunk; - bankNumEvents[i] = stop_event - start_event; - } - } + std::string entry_name(it->first); + std::string entry_class(it->second); + if ( entry_class == classType ) + { + // open the group + file.openGroup(entry_name, classType); - // split banks up if the number of cores is more than twice the number of banks - splitProcessing = bool(bankNames.size() * 2 < ThreadPool::getNumPhysicalCores()); + // get the number of events + std::size_t num = numEvents(file, hasTotalCounts, oldNeXusFileNames); + bankNames.push_back( entry_name ); + bankNumEvents.push_back(num); + total_events += num; - // set up progress bar for the rest of the (multi-threaded) process - size_t numProg = bankNames.size() * (1 + 3); // 1 = disktask, 3 = proc task - if (splitProcessing) numProg += bankNames.size() * 3; // 3 = second proc task - Progress * prog2 = new Progress(this,0.3,1.0, numProg); + // Look for weights in simulated file + try + { + file.openData("event_weight"); + m_haveWeights = true; + file.closeData(); + } + catch (::NeXus::Exception &) + { + // Swallow exception since flag is already false; + } - for (size_t i=bank0; i < bankn; i++) - { - // We make tasks for loading - if (bankNumEvents[i] > 0) - pool.schedule( new LoadBankFromDiskTask(this, bankNames[i], classType, bankNumEvents[i], oldNeXusFileNames, - prog2, diskIOMutex, scheduler) ); - } - // Start and end all threads - pool.joinAll(); - diskIOMutex.reset(); - delete prog2; - - - //Info reporting - const std::size_t eventsLoaded = WS->getNumberEvents(); - g_log.information() << "Read " << eventsLoaded << " events" - << ". Shortest TOF: " << shortest_tof << " microsec; longest TOF: " - << longest_tof << " microsec." << std::endl; - - if (shortest_tof < 0) - g_log.warning() << "The shortest TOF was negative! At least 1 event has an invalid time-of-flight." << std::endl; - if (bad_tofs > 0) - g_log.warning() << "Found " << bad_tofs << " events with TOF > 2e8. This may indicate errors in the raw TOF data." << std::endl; - - //Now, create a default X-vector for histogramming, with just 2 bins. - Kernel::cow_ptr axis; - MantidVec& xRef = axis.access(); - xRef.resize(2,0.0); - if ( eventsLoaded > 0) - { - xRef[0] = shortest_tof - 1; //Just to make sure the bins hold it all - xRef[1] = longest_tof + 1; - } - //Set the binning axis using this. - WS->setAllX(axis); - - // if there is time_of_flight load it - loadTimeOfFlight(m_filename, WS, m_top_entry_name,classType); -} - -//----------------------------------------------------------------------------- -/** - * Create a blank event workspace - * @returns A shared pointer to a new empty EventWorkspace object - */ -EventWorkspace_sptr LoadEventNexus::createEmptyEventWorkspace() -{ - // Create the output workspace - EventWorkspace_sptr eventWS(new EventWorkspace()); - //Make sure to initialize. - // We can use dummy numbers for arguments, for event workspace it doesn't matter - eventWS->initialize(1,1,1); + file.closeGroup(); + } + } - // Set the units - eventWS->getAxis(0)->unit() = UnitFactory::Instance().create("TOF"); - eventWS->setYUnit("Counts"); + loadSampleDataISIScompatibility(file, WS); - return eventWS; -} + //Close up the file + file.closeGroup(); + file.close(); + // Delete the output workspace name if it existed + std::string outName = getPropertyValue("OutputWorkspace"); + if (AnalysisDataService::Instance().doesExist(outName)) + AnalysisDataService::Instance().remove( outName ); -//----------------------------------------------------------------------------- -/** Load the run number and other meta data from the given bank */ -void LoadEventNexus::loadEntryMetadata(const std::string &nexusfilename, Mantid::API::MatrixWorkspace_sptr WS, - const std::string &entry_name) -{ - // Open the file - ::NeXus::File file(nexusfilename); - file.openGroup(entry_name, "NXentry"); - - // get the title - file.openData("title"); - if (file.getInfo().type == ::NeXus::CHAR) { - string title = file.getStrData(); - if (!title.empty()) - WS->setTitle(title); - } - file.closeData(); - - // get the notes - try { - file.openData("notes"); - if (file.getInfo().type == ::NeXus::CHAR) { - string notes = file.getStrData(); - if (!notes.empty()) - WS->mutableRun().addProperty("file_notes", notes); - } - file.closeData(); - } catch (::NeXus::Exception &) { - // let it drop on floor - } - - // Get the run number - file.openData("run_number"); - string run(""); - if (file.getInfo().type == ::NeXus::CHAR) { - run = file.getStrData(); - }else if (file.isDataInt()){ - // inside ISIS the run_number type is int32 - vector value; - file.getData(value); - if (value.size() > 0) - run = boost::lexical_cast(value[0]); - } - if (!run.empty()) { - WS->mutableRun().addProperty("run_number", run); - } - file.closeData(); - - // get the duration - file.openData("duration"); - std::vector duration; - file.getDataCoerce(duration); - if (duration.size() == 1) - { - // get the units - std::vector infos = file.getAttrInfos(); - std::string units(""); - for (std::vector::const_iterator it = infos.begin(); it != infos.end(); ++it) - { - if (it->name.compare("units") == 0) + // set more properties on the workspace + try { - units = file.getStrAttr(*it); - break; + // this is a static method that is why it is passing the file path + loadEntryMetadata(m_filename, WS, m_top_entry_name); } - } - - // set the property - WS->mutableRun().addProperty("duration", duration[0], units); - } - file.closeData(); - - // close the file - file.close(); -} - - -//----------------------------------------------------------------------------- -/** Load the instrument from the nexus file or if not found from the IDF file - * specified by the info in the Nexus file - * - * @param nexusfilename :: The Nexus file name - * @param localWorkspace :: MatrixWorkspace in which to put the instrument geometry - * @param top_entry_name :: entry name at the top of the Nexus file - * @param alg :: Handle of the algorithm - * @return true if successful - */ -bool LoadEventNexus::loadInstrument(const std::string & nexusfilename, MatrixWorkspace_sptr localWorkspace, - const std::string & top_entry_name, Algorithm * alg) -{ - bool foundInstrument = runLoadIDFFromNexus( nexusfilename, localWorkspace, top_entry_name, alg); - if (!foundInstrument) foundInstrument = runLoadInstrument( nexusfilename, localWorkspace, top_entry_name, alg ); - return foundInstrument; -} - -//----------------------------------------------------------------------------- -/** Load the instrument from the nexus file - * - * @param nexusfilename :: The name of the nexus file being loaded - * @param localWorkspace :: MatrixWorkspace in which to put the instrument geometry - * @param top_entry_name :: entry name at the top of the Nexus file - * @param alg :: Handle of the algorithm - * @return true if successful - */ -bool LoadEventNexus::runLoadIDFFromNexus(const std::string & nexusfilename, API::MatrixWorkspace_sptr localWorkspace, - const std::string & top_entry_name, Algorithm * alg) -{ - // Test if IDF exists in file, move on quickly if not - try { - ::NeXus::File nxsfile(nexusfilename); - nxsfile.openPath(top_entry_name+"/instrument/instrument_xml"); - } catch (::NeXus::Exception&) { - alg->getLogger().information("No instrument definition found in "+nexusfilename+" at "+top_entry_name+"/instrument"); - return false; - } - - IAlgorithm_sptr loadInst= alg->createChildAlgorithm("LoadIDFFromNexus"); - - // Now execute the Child Algorithm. Catch and log any error, but don't stop. - try - { - loadInst->setPropertyValue("Filename", nexusfilename); - loadInst->setProperty ("Workspace", localWorkspace); - loadInst->setPropertyValue("InstrumentParentPath",top_entry_name); - loadInst->execute(); - } - catch( std::invalid_argument&) - { - alg->getLogger().error("Invalid argument to LoadIDFFromNexus Child Algorithm "); - } - catch (std::runtime_error&) - { - alg->getLogger().debug("No instrument definition found in "+nexusfilename+" at "+top_entry_name+"/instrument"); - } - - if ( !loadInst->isExecuted() ) alg->getLogger().information("No IDF loaded from Nexus file."); - return loadInst->isExecuted(); -} - -//----------------------------------------------------------------------------- -/** Load the instrument defination file specified by info in the NXS file. - * - * @param nexusfilename :: Used to pick the instrument. - * @param localWorkspace :: MatrixWorkspace in which to put the instrument geometry - * @param top_entry_name :: entry name at the top of the NXS file - * @param alg :: Handle of the algorithm - * @return true if successful - */ -bool LoadEventNexus::runLoadInstrument(const std::string &nexusfilename, MatrixWorkspace_sptr localWorkspace, - const std::string & top_entry_name, Algorithm * alg) -{ - string instrument = ""; - - // Get the instrument name - ::NeXus::File nxfile(nexusfilename); - //Start with the base entry - nxfile.openGroup(top_entry_name, "NXentry"); - // Open the instrument - nxfile.openGroup("instrument", "NXinstrument"); - try - { - nxfile.openData("name"); - instrument = nxfile.getStrData(); - alg->getLogger().debug() << "Instrument name read from NeXus file is " << instrument << std::endl; - } - catch ( ::NeXus::Exception &) - { - // Get the instrument name from the file instead - size_t n = nexusfilename.rfind('/'); - if (n != std::string::npos) - { - std::string temp = nexusfilename.substr(n+1, nexusfilename.size()-n-1); - n = temp.find('_'); - if (n != std::string::npos && n > 0) + catch (std::runtime_error & e) { - instrument = temp.substr(0, n); + // Missing metadata is not a fatal error. Log and go on with your life + g_log.error() << "Error loading metadata: " << e.what() << std::endl; } - } - } - if (instrument.compare("POWGEN3") == 0) // hack for powgen b/c of bad long name - instrument = "POWGEN"; - if (instrument.compare("NOM") == 0) // hack for nomad - instrument = "NOMAD"; - if (instrument.empty()) - throw std::runtime_error("Could not find the instrument name in the NXS file or using the filename. Cannot load instrument!"); + // --------------------------- Time filtering ------------------------------------ + double filter_time_start_sec, filter_time_stop_sec; + filter_time_start_sec = getProperty("FilterByTimeStart"); + filter_time_stop_sec = getProperty("FilterByTimeStop"); + chunk = getProperty("ChunkNumber"); + totalChunks = getProperty("TotalChunks"); - // Now let's close the file as we don't need it anymore to load the instrument. - nxfile.close(); + //Default to ALL pulse times + bool is_time_filtered = false; + filter_time_start = Kernel::DateAndTime::minimum(); + filter_time_stop = Kernel::DateAndTime::maximum(); - // do the actual work - IAlgorithm_sptr loadInst= alg->createChildAlgorithm("LoadInstrument"); + if (m_allBanksPulseTimes->numPulses > 0) + { + //If not specified, use the limits of doubles. Otherwise, convert from seconds to absolute PulseTime + if (filter_time_start_sec != EMPTY_DBL()) + { + filter_time_start = run_start + filter_time_start_sec; + is_time_filtered = true; + } - // Now execute the Child Algorithm. Catch and log any error, but don't stop. - bool executionSuccessful(true); - try - { - loadInst->setPropertyValue("InstrumentName", instrument); - loadInst->setProperty ("Workspace", localWorkspace); - loadInst->setProperty("RewriteSpectraMap", false); - loadInst->execute(); - - // Populate the instrument parameters in this workspace - this works around a bug - localWorkspace->populateInstrumentParameters(); - } catch (std::invalid_argument& e) - { - alg->getLogger().information() << "Invalid argument to LoadInstrument Child Algorithm : " << e.what() << std::endl; - executionSuccessful = false; - } catch (std::runtime_error& e) - { - alg->getLogger().information("Unable to successfully run LoadInstrument Child Algorithm"); - alg->getLogger().information(e.what()); - executionSuccessful = false; - } + if (filter_time_stop_sec != EMPTY_DBL()) + { + filter_time_stop = run_start + filter_time_stop_sec; + is_time_filtered = true; + } - // If loading instrument definition file fails - if (!executionSuccessful) - { - alg->getLogger().error() << "Error loading Instrument definition file\n"; - return false; - } - - // Ticket #2049: Cleanup all loadinstrument members to a single instance - // If requested update the instrument to positions in the data file - const Geometry::ParameterMap & pmap = localWorkspace->instrumentParameters(); - if( !pmap.contains(localWorkspace->getInstrument()->getComponentID(),"det-pos-source") ) - return executionSuccessful; - - boost::shared_ptr updateDets = pmap.get(localWorkspace->getInstrument()->getComponentID(),"det-pos-source"); - std::string value = updateDets->value(); - if(value.substr(0,8) == "datafile" ) - { - IAlgorithm_sptr updateInst = alg->createChildAlgorithm("UpdateInstrumentFromFile"); - updateInst->setProperty("Workspace", localWorkspace); - updateInst->setPropertyValue("Filename", nexusfilename); - if(value == "datafile-ignore-phi" ) - { - updateInst->setProperty("IgnorePhi", true); - alg->getLogger().information("Detector positions in IDF updated with positions in the data file except for the phi values"); - } - else - { - alg->getLogger().information("Detector positions in IDF updated with positions in the data file"); - } - // We want this to throw if it fails to warn the user that the information is not correct. - updateInst->execute(); - } - - return executionSuccessful; -} - - -//----------------------------------------------------------------------------- -/** - * Create the required spectra mapping. If the file contains an isis_vms_compat block then - * the mapping is read from there, otherwise a 1:1 map with the instrument is created (along - * with the associated spectra axis) - * @param workspace :: The workspace to contain the spectra mapping - * @param bankNames :: Bank names that are in Nexus file - */ -void LoadEventNexus::deleteBanks(API::MatrixWorkspace_sptr workspace, std::vector bankNames) -{ - Instrument_sptr inst = boost::const_pointer_cast(workspace->getInstrument()->baseInstrument()); - //Build a list of Rectangular Detectors - std::vector > detList; - for (int i=0; i < inst->nelements(); i++) - { - boost::shared_ptr det; - boost::shared_ptr assem; - boost::shared_ptr assem2; + //Silly values? + if (filter_time_stop < filter_time_start) + { + std::string msg = "Your "; + if(monitors) msg += "monitor "; + msg += "filter for time's Stop value is smaller than the Start value."; + throw std::invalid_argument(msg); + } + } - det = boost::dynamic_pointer_cast( (*inst)[i] ); - if (det) + if (is_time_filtered) { - detList.push_back(det); + //Now filter out the run, using the DateAndTime type. + WS->mutableRun().filterByTime(filter_time_start, filter_time_stop); } - else + + if(metaDataOnly) { + //Now, create a default X-vector for histogramming, with just 2 bins. + Kernel::cow_ptr axis; + MantidVec& xRef = axis.access(); + xRef.resize(2); + xRef[0] = static_cast(std::numeric_limits::max()) * 0.1 - 1; //Just to make sure the bins hold it all + xRef[1] = 1; + //Set the binning axis using this. + WS->setAllX(axis); + return; + } + + // --------- Loading only one bank ? ---------------------------------- + std::vector someBanks = getProperty("BankName"); + bool SingleBankPixelsOnly = getProperty("SingleBankPixelsOnly"); + if ((!someBanks.empty()) && (!monitors)) { - //Also, look in the first sub-level for RectangularDetectors (e.g. PG3). - // We are not doing a full recursive search since that will be very long for lots of pixels. - assem = boost::dynamic_pointer_cast( (*inst)[i] ); - if (assem) + // check that all of the requested banks are in the file + for (auto someBank = someBanks.begin(); someBank != someBanks.end(); ++someBank) { - for (int j=0; j < assem->nelements(); j++) + bool foundIt = false; + for (auto bankName = bankNames.begin(); bankName != bankNames.end(); ++bankName) { - det = boost::dynamic_pointer_cast( (*assem)[j] ); - if (det) + if ((*bankName) == (*someBank)+"_events") { - detList.push_back(det); + foundIt = true; + break; + } + } + if (!foundIt) + { + throw std::invalid_argument("No entry named '" + (*someBank) + "' was found in the .NXS file.\n"); + } + } + + // change the number of banks to load + bankNames.clear(); + for (auto someBank = someBanks.begin(); someBank != someBanks.end(); ++someBank) + bankNames.push_back((*someBank) + "_events"); + // how many events are in a bank + bankNumEvents.clear(); + bankNumEvents.assign(someBanks.size(), 1); // TODO this equally weights the banks + + if( !SingleBankPixelsOnly ) someBanks.clear(); // Marker to load all pixels + } + else + { + someBanks.clear(); + } + + prog->report("Initializing all pixels"); + // Remove used banks if parameter is set + if (WS->getInstrument()->hasParameter("remove-unused-banks")) + { + std::vector instrumentUnused = WS->getInstrument()->getNumberParameter("remove-unused-banks", true); + if (!instrumentUnused.empty()) + { + const int unused = static_cast(instrumentUnused.front()); + if(unused == 1) deleteBanks(WS, bankNames); + } + } + //----------------- Pad Empty Pixels ------------------------------- + // Create the required spectra mapping so that the workspace knows what to pad to + createSpectraMapping(m_filename, monitors, someBanks); + + //This map will be used to find the workspace index + if( this->event_id_is_spec ) + WS->getSpectrumToWorkspaceIndexVector(pixelID_to_wi_vector, pixelID_to_wi_offset); + else + WS->getDetectorIDToWorkspaceIndexVector(pixelID_to_wi_vector, pixelID_to_wi_offset, true); + + // Cache a map for speed. + if (!m_haveWeights) + { + this->makeMapToEventLists(eventVectors); + } + else + { + // Convert to weighted events + for (size_t i=0; i < WS->getNumberHistograms(); i++) + { + WS->getEventList(i).switchTo(API::WEIGHTED); + } + this->makeMapToEventLists(weightedEventVectors); + } + + // Set all (empty) event lists as sorted by pulse time. That way, calling SortEvents will not try to sort these empty lists. + for (size_t i=0; i < WS->getNumberHistograms(); i++) + WS->getEventList(i).setSortOrder(DataObjects::PULSETIME_SORT); + + //Count the limits to time of flight + shortest_tof = static_cast(std::numeric_limits::max()) * 0.1; + longest_tof = 0.; + + // Make the thread pool + ThreadScheduler * scheduler = new ThreadSchedulerMutexes(); + ThreadPool pool(scheduler); + auto diskIOMutex = boost::make_shared(); + size_t bank0 = 0; + size_t bankn = bankNames.size(); + + if (chunk != EMPTY_INT()) // We are loading part - work out the bank number range + { + eventsPerChunk = total_events / totalChunks; + // Sort banks by size + size_t tmp; + string stmp; + for (size_t i = 0; i < bankn; i++) + for (size_t j = 0; j < bankn - 1; j++) + if (bankNumEvents[j] < bankNumEvents[j + 1]) + { + tmp = bankNumEvents[j]; + bankNumEvents[j] = bankNumEvents[j + 1]; + bankNumEvents[j + 1] = tmp; + stmp = bankNames[j]; + bankNames[j] = bankNames[j + 1]; + bankNames[j + 1] = stmp; } - else + int bigBanks = 0; + for (size_t i = 0; i < bankn; i++) if (bankNumEvents[i] > eventsPerChunk)bigBanks++; + // Each chunk is part of bank or multiple whole banks + // 0.5 for last chunk of a bank with multiple chunks + // 0.1 for multiple whole banks not completely filled + eventsPerChunk += static_cast((static_cast(bigBanks) / static_cast(totalChunks) * + 0.5 + 0.05) * static_cast(eventsPerChunk)); + double partialChunk = 0.; + firstChunkForBank = 1; + for (int chunki = 1; chunki <=chunk; chunki++) + { + if (partialChunk > 1.) + { + partialChunk = 0.; + firstChunkForBank = chunki; + bank0 = bankn; + } + if (bankNumEvents[bank0] > 1) + { + partialChunk += static_cast(eventsPerChunk)/static_cast(bankNumEvents[bank0]); + } + if (chunki < totalChunks) bankn = bank0 + 1; + else bankn = bankNames.size(); + if (chunki == firstChunkForBank && partialChunk > 1.0) bankn += static_cast(partialChunk) - 1; + if (bankn > bankNames.size()) bankn = bankNames.size(); + } + for (size_t i=bank0; i < bankn; i++) + { + size_t start_event = (chunk - firstChunkForBank) * eventsPerChunk; + size_t stop_event = bankNumEvents[i]; + // Don't change stop_event for the final chunk + if ( start_event + eventsPerChunk < stop_event ) stop_event = start_event + eventsPerChunk; + bankNumEvents[i] = stop_event - start_event; + } + } + + // split banks up if the number of cores is more than twice the number of banks + splitProcessing = bool(bankNames.size() * 2 < ThreadPool::getNumPhysicalCores()); + + // set up progress bar for the rest of the (multi-threaded) process + size_t numProg = bankNames.size() * (1 + 3); // 1 = disktask, 3 = proc task + if (splitProcessing) numProg += bankNames.size() * 3; // 3 = second proc task + Progress * prog2 = new Progress(this,0.3,1.0, numProg); + + for (size_t i=bank0; i < bankn; i++) + { + // We make tasks for loading + if (bankNumEvents[i] > 0) + pool.schedule( new LoadBankFromDiskTask(this, bankNames[i], classType, bankNumEvents[i], oldNeXusFileNames, + prog2, diskIOMutex, scheduler) ); + } + // Start and end all threads + pool.joinAll(); + diskIOMutex.reset(); + delete prog2; + + + //Info reporting + const std::size_t eventsLoaded = WS->getNumberEvents(); + g_log.information() << "Read " << eventsLoaded << " events" + << ". Shortest TOF: " << shortest_tof << " microsec; longest TOF: " + << longest_tof << " microsec." << std::endl; + + if (shortest_tof < 0) + g_log.warning() << "The shortest TOF was negative! At least 1 event has an invalid time-of-flight." << std::endl; + if (bad_tofs > 0) + g_log.warning() << "Found " << bad_tofs << " events with TOF > 2e8. This may indicate errors in the raw TOF data." << std::endl; + + //Now, create a default X-vector for histogramming, with just 2 bins. + Kernel::cow_ptr axis; + MantidVec& xRef = axis.access(); + xRef.resize(2,0.0); + if ( eventsLoaded > 0) + { + xRef[0] = shortest_tof - 1; //Just to make sure the bins hold it all + xRef[1] = longest_tof + 1; + } + //Set the binning axis using this. + WS->setAllX(axis); + + // if there is time_of_flight load it + loadTimeOfFlight(m_filename, WS, m_top_entry_name,classType); + } + + //----------------------------------------------------------------------------- + /** + * Create a blank event workspace + * @returns A shared pointer to a new empty EventWorkspace object + */ + EventWorkspace_sptr LoadEventNexus::createEmptyEventWorkspace() + { + // Create the output workspace + EventWorkspace_sptr eventWS(new EventWorkspace()); + //Make sure to initialize. + // We can use dummy numbers for arguments, for event workspace it doesn't matter + eventWS->initialize(1,1,1); + + // Set the units + eventWS->getAxis(0)->unit() = UnitFactory::Instance().create("TOF"); + eventWS->setYUnit("Counts"); + + return eventWS; + } + + + //----------------------------------------------------------------------------- + /** Load the run number and other meta data from the given bank */ + void LoadEventNexus::loadEntryMetadata(const std::string &nexusfilename, Mantid::API::MatrixWorkspace_sptr WS, + const std::string &entry_name) + { + // Open the file + ::NeXus::File file(nexusfilename); + file.openGroup(entry_name, "NXentry"); + + // get the title + file.openData("title"); + if (file.getInfo().type == ::NeXus::CHAR) { + string title = file.getStrData(); + if (!title.empty()) + WS->setTitle(title); + } + file.closeData(); + + // get the notes + try { + file.openData("notes"); + if (file.getInfo().type == ::NeXus::CHAR) { + string notes = file.getStrData(); + if (!notes.empty()) + WS->mutableRun().addProperty("file_notes", notes); + } + file.closeData(); + } catch (::NeXus::Exception &) { + // let it drop on floor + } + + // Get the run number + file.openData("run_number"); + string run(""); + if (file.getInfo().type == ::NeXus::CHAR) { + run = file.getStrData(); + }else if (file.isDataInt()){ + // inside ISIS the run_number type is int32 + vector value; + file.getData(value); + if (value.size() > 0) + run = boost::lexical_cast(value[0]); + } + if (!run.empty()) { + WS->mutableRun().addProperty("run_number", run); + } + file.closeData(); + + // get the duration + file.openData("duration"); + std::vector duration; + file.getDataCoerce(duration); + if (duration.size() == 1) + { + // get the units + std::vector infos = file.getAttrInfos(); + std::string units(""); + for (std::vector::const_iterator it = infos.begin(); it != infos.end(); ++it) + { + if (it->name.compare("units") == 0) + { + units = file.getStrAttr(*it); + break; + } + } + + // set the property + WS->mutableRun().addProperty("duration", duration[0], units); + } + file.closeData(); + + // close the file + file.close(); + } + + + //----------------------------------------------------------------------------- + /** Load the instrument from the nexus file or if not found from the IDF file + * specified by the info in the Nexus file + * + * @param nexusfilename :: The Nexus file name + * @param localWorkspace :: MatrixWorkspace in which to put the instrument geometry + * @param top_entry_name :: entry name at the top of the Nexus file + * @param alg :: Handle of the algorithm + * @return true if successful + */ + bool LoadEventNexus::loadInstrument(const std::string & nexusfilename, MatrixWorkspace_sptr localWorkspace, + const std::string & top_entry_name, Algorithm * alg) + { + bool foundInstrument = runLoadIDFFromNexus( nexusfilename, localWorkspace, top_entry_name, alg); + if (!foundInstrument) foundInstrument = runLoadInstrument( nexusfilename, localWorkspace, top_entry_name, alg ); + return foundInstrument; + } + + //----------------------------------------------------------------------------- + /** Load the instrument from the nexus file + * + * @param nexusfilename :: The name of the nexus file being loaded + * @param localWorkspace :: MatrixWorkspace in which to put the instrument geometry + * @param top_entry_name :: entry name at the top of the Nexus file + * @param alg :: Handle of the algorithm + * @return true if successful + */ + bool LoadEventNexus::runLoadIDFFromNexus(const std::string & nexusfilename, API::MatrixWorkspace_sptr localWorkspace, + const std::string & top_entry_name, Algorithm * alg) + { + // Test if IDF exists in file, move on quickly if not + try { + ::NeXus::File nxsfile(nexusfilename); + nxsfile.openPath(top_entry_name+"/instrument/instrument_xml"); + } catch (::NeXus::Exception&) { + alg->getLogger().information("No instrument definition found in "+nexusfilename+" at "+top_entry_name+"/instrument"); + return false; + } + + IAlgorithm_sptr loadInst= alg->createChildAlgorithm("LoadIDFFromNexus"); + + // Now execute the Child Algorithm. Catch and log any error, but don't stop. + try + { + loadInst->setPropertyValue("Filename", nexusfilename); + loadInst->setProperty ("Workspace", localWorkspace); + loadInst->setPropertyValue("InstrumentParentPath",top_entry_name); + loadInst->execute(); + } + catch( std::invalid_argument&) + { + alg->getLogger().error("Invalid argument to LoadIDFFromNexus Child Algorithm "); + } + catch (std::runtime_error&) + { + alg->getLogger().debug("No instrument definition found in "+nexusfilename+" at "+top_entry_name+"/instrument"); + } + + if ( !loadInst->isExecuted() ) alg->getLogger().information("No IDF loaded from Nexus file."); + return loadInst->isExecuted(); + } + /** method used to return instrument name for some old ISIS files where it is not written properly within the instrument + * @param hFile :: A reference to the NeXus file opened at the root entry + */ + std::string LoadEventNexus::readInstrumentFromISIS_VMSCompat(::NeXus::File &hFile) + { + std::string instrumentName(""); + try + { + hFile.openGroup("isis_vms_compat","IXvms"); + } + catch(std::runtime_error &) + { + return instrumentName; + } + try + { + hFile.openData("NAME"); + } + catch(std::runtime_error &) + { + hFile.closeGroup(); + return instrumentName; + } + + instrumentName = hFile.getStrData(); + hFile.closeData(); + hFile.closeGroup(); + + return instrumentName; + } + + + //----------------------------------------------------------------------------- + /** Load the instrument definition file specified by info in the NXS file. + * + * @param nexusfilename :: Used to pick the instrument. + * @param localWorkspace :: MatrixWorkspace in which to put the instrument geometry + * @param top_entry_name :: entry name at the top of the NXS file + * @param alg :: Handle of the algorithm + * @return true if successful + */ + bool LoadEventNexus::runLoadInstrument(const std::string &nexusfilename, MatrixWorkspace_sptr localWorkspace, + const std::string & top_entry_name, Algorithm * alg) + { + string instrument = ""; + + // Get the instrument name + ::NeXus::File nxfile(nexusfilename); + //Start with the base entry + nxfile.openGroup(top_entry_name, "NXentry"); + // Open the instrument + nxfile.openGroup("instrument", "NXinstrument"); + try + { + nxfile.openData("name"); + instrument = nxfile.getStrData(); + alg->getLogger().debug() << "Instrument name read from NeXus file is " << instrument << std::endl; + } + catch ( ::NeXus::Exception &) + { + // Try to fall back to isis compatibility options + nxfile.closeGroup(); + instrument = readInstrumentFromISIS_VMSCompat(nxfile); + if (instrument.empty()) + { + // Get the instrument name from the file instead + size_t n = nexusfilename.rfind('/'); + if (n != std::string::npos) + { + std::string temp = nexusfilename.substr(n+1, nexusfilename.size()-n-1); + n = temp.find('_'); + if (n != std::string::npos && n > 0) + { + instrument = temp.substr(0, n); + } + } + } + } + if (instrument.compare("POWGEN3") == 0) // hack for powgen b/c of bad long name + instrument = "POWGEN"; + if (instrument.compare("NOM") == 0) // hack for nomad + instrument = "NOMAD"; + + if (instrument.empty()) + throw std::runtime_error("Could not find the instrument name in the NXS file or using the filename. Cannot load instrument!"); + + // Now let's close the file as we don't need it anymore to load the instrument. + nxfile.close(); + + // do the actual work + IAlgorithm_sptr loadInst= alg->createChildAlgorithm("LoadInstrument"); + + // Now execute the Child Algorithm. Catch and log any error, but don't stop. + bool executionSuccessful(true); + try + { + loadInst->setPropertyValue("InstrumentName", instrument); + loadInst->setProperty ("Workspace", localWorkspace); + loadInst->setProperty("RewriteSpectraMap", false); + loadInst->execute(); + + // Populate the instrument parameters in this workspace - this works around a bug + localWorkspace->populateInstrumentParameters(); + } catch (std::invalid_argument& e) + { + alg->getLogger().information() << "Invalid argument to LoadInstrument Child Algorithm : " << e.what() << std::endl; + executionSuccessful = false; + } catch (std::runtime_error& e) + { + alg->getLogger().information("Unable to successfully run LoadInstrument Child Algorithm"); + alg->getLogger().information(e.what()); + executionSuccessful = false; + } + + // If loading instrument definition file fails + if (!executionSuccessful) + { + alg->getLogger().error() << "Error loading Instrument definition file\n"; + return false; + } + + // Ticket #2049: Cleanup all loadinstrument members to a single instance + // If requested update the instrument to positions in the data file + const Geometry::ParameterMap & pmap = localWorkspace->instrumentParameters(); + if( !pmap.contains(localWorkspace->getInstrument()->getComponentID(),"det-pos-source") ) + return executionSuccessful; + + boost::shared_ptr updateDets = pmap.get(localWorkspace->getInstrument()->getComponentID(),"det-pos-source"); + std::string value = updateDets->value(); + if(value.substr(0,8) == "datafile" ) + { + IAlgorithm_sptr updateInst = alg->createChildAlgorithm("UpdateInstrumentFromFile"); + updateInst->setProperty("Workspace", localWorkspace); + updateInst->setPropertyValue("Filename", nexusfilename); + if(value == "datafile-ignore-phi" ) + { + updateInst->setProperty("IgnorePhi", true); + alg->getLogger().information("Detector positions in IDF updated with positions in the data file except for the phi values"); + } + else + { + alg->getLogger().information("Detector positions in IDF updated with positions in the data file"); + } + // We want this to throw if it fails to warn the user that the information is not correct. + updateInst->execute(); + } + + return executionSuccessful; + } + + + //----------------------------------------------------------------------------- + /** + * Create the required spectra mapping. If the file contains an isis_vms_compat block then + * the mapping is read from there, otherwise a 1:1 map with the instrument is created (along + * with the associated spectra axis) + * @param workspace :: The workspace to contain the spectra mapping + * @param bankNames :: Bank names that are in Nexus file + */ + void LoadEventNexus::deleteBanks(API::MatrixWorkspace_sptr workspace, std::vector bankNames) + { + Instrument_sptr inst = boost::const_pointer_cast(workspace->getInstrument()->baseInstrument()); + //Build a list of Rectangular Detectors + std::vector > detList; + for (int i=0; i < inst->nelements(); i++) + { + boost::shared_ptr det; + boost::shared_ptr assem; + boost::shared_ptr assem2; + + det = boost::dynamic_pointer_cast( (*inst)[i] ); + if (det) + { + detList.push_back(det); + } + else + { + //Also, look in the first sub-level for RectangularDetectors (e.g. PG3). + // We are not doing a full recursive search since that will be very long for lots of pixels. + assem = boost::dynamic_pointer_cast( (*inst)[i] ); + if (assem) + { + for (int j=0; j < assem->nelements(); j++) { - //Also, look in the second sub-level for RectangularDetectors (e.g. PG3). - // We are not doing a full recursive search since that will be very long for lots of pixels. - assem2 = boost::dynamic_pointer_cast( (*assem)[j] ); - if (assem2) + det = boost::dynamic_pointer_cast( (*assem)[j] ); + if (det) { - for (int k=0; k < assem2->nelements(); k++) + detList.push_back(det); + + } + else + { + //Also, look in the second sub-level for RectangularDetectors (e.g. PG3). + // We are not doing a full recursive search since that will be very long for lots of pixels. + assem2 = boost::dynamic_pointer_cast( (*assem)[j] ); + if (assem2) { - det = boost::dynamic_pointer_cast( (*assem2)[k] ); - if (det) + for (int k=0; k < assem2->nelements(); k++) { - detList.push_back(det); + det = boost::dynamic_pointer_cast( (*assem2)[k] ); + if (det) + { + detList.push_back(det); + } } } } @@ -2047,650 +2085,649 @@ void LoadEventNexus::deleteBanks(API::MatrixWorkspace_sptr workspace, std::vecto } } } - } - if (detList.size() == 0) return; - for (int i = 0; i(detList.size()); i++) - { + if (detList.size() == 0) return; + for (int i = 0; i(detList.size()); i++) + { bool keep = false; boost::shared_ptr det = detList[i]; std::string det_name = det->getName(); for (int j = 0; j(bankNames.size()); j++) { - size_t pos = bankNames[j].find("_events"); - if(det_name.compare(bankNames[j].substr(0,pos)) == 0) keep = true; - if(keep) break; + size_t pos = bankNames[j].find("_events"); + if(det_name.compare(bankNames[j].substr(0,pos)) == 0) keep = true; + if(keep) break; } if (!keep) { - boost::shared_ptr parent = inst->getComponentByName(det_name); - std::vector children; - boost::shared_ptr asmb = boost::dynamic_pointer_cast(parent); - asmb->getChildren(children, false); - for (int col = 0; col(children.size()); col++) - { - boost::shared_ptr asmb2 = boost::dynamic_pointer_cast(children[col]); - std::vector grandchildren; - asmb2->getChildren(grandchildren,false); + boost::shared_ptr parent = inst->getComponentByName(det_name); + std::vector children; + boost::shared_ptr asmb = boost::dynamic_pointer_cast(parent); + asmb->getChildren(children, false); + for (int col = 0; col(children.size()); col++) + { + boost::shared_ptr asmb2 = boost::dynamic_pointer_cast(children[col]); + std::vector grandchildren; + asmb2->getChildren(grandchildren,false); - for (int row = 0; row(grandchildren.size()); row++) - { - Detector* d = dynamic_cast(const_cast(grandchildren[row].get())); - inst->removeDetector(d); - } + for (int row = 0; row(grandchildren.size()); row++) + { + Detector* d = dynamic_cast(const_cast(grandchildren[row].get())); + inst->removeDetector(d); } - IComponent* comp = dynamic_cast(detList[i].get()); - inst->remove(comp); + } + IComponent* comp = dynamic_cast(detList[i].get()); + inst->remove(comp); } - } + } return; -} -//----------------------------------------------------------------------------- -/** - * Create the required spectra mapping. If the file contains an isis_vms_compat block then - * the mapping is read from there, otherwise a 1:1 map with the instrument is created (along - * with the associated spectra axis) - * @param nxsfile :: The name of a nexus file to load the mapping from - * @param monitorsOnly :: Load only the monitors is true - * @param bankNames :: An optional bank name for loading specified banks - */ -void LoadEventNexus::createSpectraMapping(const std::string &nxsfile, - const bool monitorsOnly, const std::vector &bankNames) -{ - bool spectramap = false; - // set up the - if( !monitorsOnly && !bankNames.empty() ) - { - std::vector allDets; - - for (auto name = bankNames.begin(); name != bankNames.end(); ++name) - { - // Only build the map for the single bank - std::vector dets; - WS->getInstrument()->getDetectorsInBank(dets, (*name)); - if (dets.empty()) - throw std::runtime_error("Could not find the bank named '" + (*name) + - "' as a component assembly in the instrument tree; or it did not contain any detectors." - " Try unchecking SingleBankPixelsOnly."); - allDets.insert(allDets.end(), dets.begin(), dets.end()); } - if (!allDets.empty()) + //----------------------------------------------------------------------------- + /** + * Create the required spectra mapping. If the file contains an isis_vms_compat block then + * the mapping is read from there, otherwise a 1:1 map with the instrument is created (along + * with the associated spectra axis) + * @param nxsfile :: The name of a nexus file to load the mapping from + * @param monitorsOnly :: Load only the monitors is true + * @param bankNames :: An optional bank name for loading specified banks + */ + void LoadEventNexus::createSpectraMapping(const std::string &nxsfile, + const bool monitorsOnly, const std::vector &bankNames) { - WS->resizeTo(allDets.size()); - // Make an event list for each. - for(size_t wi=0; wi < allDets.size(); wi++) + bool spectramap = false; + // set up the + if( !monitorsOnly && !bankNames.empty() ) { - const detid_t detID = allDets[wi]->getID(); - WS->getSpectrum(wi)->setDetectorID(detID); - } - spectramap = true; - g_log.debug() << "Populated spectra map for select banks\n"; - } + std::vector allDets; - } - else - { - spectramap = loadSpectraMapping(nxsfile, monitorsOnly, m_top_entry_name); - // Did we load one? If so then the event ID is the spectrum number and not det ID - if( spectramap ) this->event_id_is_spec = true; - } + for (auto name = bankNames.begin(); name != bankNames.end(); ++name) + { + // Only build the map for the single bank + std::vector dets; + WS->getInstrument()->getDetectorsInBank(dets, (*name)); + if (dets.empty()) + throw std::runtime_error("Could not find the bank named '" + (*name) + + "' as a component assembly in the instrument tree; or it did not contain any detectors." + " Try unchecking SingleBankPixelsOnly."); + allDets.insert(allDets.end(), dets.begin(), dets.end()); + } + if (!allDets.empty()) + { + WS->resizeTo(allDets.size()); + // Make an event list for each. + for(size_t wi=0; wi < allDets.size(); wi++) + { + const detid_t detID = allDets[wi]->getID(); + WS->getSpectrum(wi)->setDetectorID(detID); + } + spectramap = true; + g_log.debug() << "Populated spectra map for select banks\n"; + } - if( !spectramap ) - { - g_log.debug() << "No custom spectra mapping found, continuing with default 1:1 mapping of spectrum:detectorID\n"; - // The default 1:1 will suffice but exclude the monitors as they are always in a separate workspace - WS->padSpectra(); - g_log.debug() << "Populated 1:1 spectra map for the whole instrument \n"; - } -} - -//----------------------------------------------------------------------------- -/** - * Returns whether the file contains monitors with events in them - * @returns True if the file contains monitors with event data, false otherwise - */ -bool LoadEventNexus::hasEventMonitors() -{ - bool result(false); - // Determine whether to load histograms or events - try - { - ::NeXus::File file(m_filename); - file.openPath(m_top_entry_name); - //Start with the base entry - typedef std::map string_map_t; - //Now we want to go through and find the monitors - string_map_t entries = file.getEntries(); - for( string_map_t::const_iterator it = entries.begin(); it != entries.end(); ++it) - { - if( it->second == "NXmonitor" ) + } + else { - file.openGroup(it->first, it->second); - break; + spectramap = loadSpectraMapping(nxsfile, monitorsOnly, m_top_entry_name); + // Did we load one? If so then the event ID is the spectrum number and not det ID + if( spectramap ) this->event_id_is_spec = true; } - } - file.openData("event_id"); - result = true; - file.close(); - } - catch(::NeXus::Exception &) - { - result = false; - } - return result; -} - -//----------------------------------------------------------------------------- -/** - * Load the Monitors from the NeXus file into a workspace. The original - * workspace name is used and appended with _monitors. - */ -void LoadEventNexus::runLoadMonitors() -{ - std::string mon_wsname = this->getProperty("OutputWorkspace"); - mon_wsname.append("_monitors"); - IAlgorithm_sptr loadMonitors = this->createChildAlgorithm("LoadNexusMonitors"); - try - { - g_log.information("Loading monitors from NeXus file..."); - loadMonitors->setPropertyValue("Filename", m_filename); - g_log.information() << "New workspace name for monitors: " << mon_wsname << std::endl; - loadMonitors->setPropertyValue("OutputWorkspace", mon_wsname); - loadMonitors->execute(); - MatrixWorkspace_sptr mons = loadMonitors->getProperty("OutputWorkspace"); - this->declareProperty(new WorkspaceProperty<>("MonitorWorkspace", - mon_wsname, Direction::Output), "Monitors from the Event NeXus file"); - this->setProperty("MonitorWorkspace", mons); - // Set the internal monitor workspace pointer as well - WS->setMonitorWorkspace(mons); - - filterDuringPause(mons); - } - catch (...) - { - g_log.error("Error while loading the monitors from the file. File may contain no monitors."); - } -} - -// -/** - * Load a spectra mapping from the given file. This currently checks for the existence of - * an isis_vms_compat block in the file, if it exists it pulls out the spectra mapping listed there - * @param filename :: A filename - * @param monitorsOnly :: If true then only the monitor spectra are loaded - * @param entry_name :: name of the NXentry to open. - * @returns True if the mapping was loaded or false if the block does not exist - */ -bool LoadEventNexus::loadSpectraMapping(const std::string& filename, const bool monitorsOnly, const std::string& entry_name ) -{ - ::NeXus::File file(filename); - try - { - g_log.debug() << "Attempting to load custom spectra mapping from '" << entry_name << "/isis_vms_compat'.\n"; - file.openPath(entry_name + "/isis_vms_compat"); - } - catch(::NeXus::Exception&) - { - return false; // Doesn't exist - } - - // The ISIS spectrum mapping is defined by 2 arrays in isis_vms_compat block: - // UDET - An array of detector IDs - // SPEC - An array of spectrum numbers - // There sizes must match. Hardware allows more than one detector ID to be mapped to a single spectrum - // and this is encoded in the SPEC/UDET arrays by repeating the spectrum number in the array - // for each mapped detector, e.g. - // - // 1 1001 - // 1 1002 - // 2 2001 - // 3 3001 - // - // defines 3 spectra, where the first spectrum contains 2 detectors - - // UDET - file.openData("UDET"); - std::vector udet; - file.getData(udet); - file.closeData(); - // SPEC - file.openData("SPEC"); - std::vector spec; - file.getData(spec); - file.closeData(); - // Close - file.closeGroup(); - file.close(); - - // The spec array will contain a spectrum number for each udet but the spectrum number - // may be the same for more that one detector - const size_t ndets(udet.size()); - if( ndets != spec.size() ) - { - std::ostringstream os; - os << "UDET/SPEC list size mismatch. UDET=" << udet.size() << ", SPEC=" << spec.size() << "\n"; - throw std::runtime_error(os.str()); - } - // Monitor filtering/selection - const std::vector monitors = WS->getInstrument()->getMonitors(); - const size_t nmons(monitors.size()); - if( monitorsOnly ) - { - g_log.debug() << "Loading only monitor spectra from " << filename << "\n"; - // Find the det_ids in the udet array. - WS->resizeTo(nmons); - for( size_t i = 0; i < nmons; ++i ) - { - // Find the index in the udet array - const detid_t & id = monitors[i]; - std::vector::const_iterator it = std::find(udet.begin(), udet.end(), id); - if( it != udet.end() ) + if( !spectramap ) { - auto spectrum = WS->getSpectrum(i); - const specid_t & specNo = spec[it - udet.begin()]; - spectrum->setSpectrumNo(specNo); - spectrum->setDetectorID(id); + g_log.debug() << "No custom spectra mapping found, continuing with default 1:1 mapping of spectrum:detectorID\n"; + // The default 1:1 will suffice but exclude the monitors as they are always in a separate workspace + WS->padSpectra(); + g_log.debug() << "Populated 1:1 spectra map for the whole instrument \n"; } } - } - else - { - g_log.debug() << "Loading only detector spectra from " << filename << "\n"; - SpectrumDetectorMapping mapping(spec,udet, monitors); - WS->resizeTo(mapping.getMapping().size()); - // Make sure spectrum numbers are correct - auto uniqueSpectra = mapping.getSpectrumNumbers(); - auto itend = uniqueSpectra.end(); - size_t counter = 0; - for(auto it = uniqueSpectra.begin(); it != itend; ++it) + + //----------------------------------------------------------------------------- + /** + * Returns whether the file contains monitors with events in them + * @returns True if the file contains monitors with event data, false otherwise + */ + bool LoadEventNexus::hasEventMonitors() { - WS->getSpectrum(counter)->setSpectrumNo(*it); - ++counter; + bool result(false); + // Determine whether to load histograms or events + try + { + ::NeXus::File file(m_filename); + file.openPath(m_top_entry_name); + //Start with the base entry + typedef std::map string_map_t; + //Now we want to go through and find the monitors + string_map_t entries = file.getEntries(); + for( string_map_t::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + if( it->second == "NXmonitor" ) + { + file.openGroup(it->first, it->second); + break; + } + } + file.openData("event_id"); + result = true; + file.close(); + } + catch(::NeXus::Exception &) + { + result = false; + } + return result; } - // Fill detectors based on this mapping - WS->updateSpectraUsing(mapping); - } - return true; -} - -/** - * Set the filters on TOF. - * @param monitors :: If true check the monitor properties else use the standard ones - */ -void LoadEventNexus::setTimeFilters(const bool monitors) -{ - //Get the limits to the filter - std::string prefix("Filter"); - if(monitors) prefix += "Mon"; - filter_tof_min = getProperty(prefix + "ByTofMin"); - filter_tof_max = getProperty(prefix + "ByTofMax"); - if ( (filter_tof_min == EMPTY_DBL()) && (filter_tof_max == EMPTY_DBL())) - { - //Nothing specified. Include everything - filter_tof_min = -1e20; - filter_tof_max = +1e20; - } - else if ( (filter_tof_min != EMPTY_DBL()) && (filter_tof_max != EMPTY_DBL())) - { - //Both specified. Keep these values - } - else - { - std::string msg("You must specify both min & max or neither TOF filters"); - if(monitors) msg = " for the monitors."; - throw std::invalid_argument(msg); - } - -} - -//----------------------------------------------------------------------------- -// ISIS event corrections -//----------------------------------------------------------------------------- -/** - * Check if time_of_flight can be found in the file and load it - * @param nexusfilename :: The name of the ISIS nexus event file. - * @param WS :: The event workspace which events will be modified. - * @param entry_name :: An NXentry tag in the file - * @param classType :: The type of the events: either detector or monitor - */ -void LoadEventNexus::loadTimeOfFlight(const std::string &nexusfilename, DataObjects::EventWorkspace_sptr WS, - const std::string &entry_name, const std::string &classType) -{ - bool done = false; - // Open the file - ::NeXus::File file(nexusfilename); - file.openGroup(entry_name, "NXentry"); - - typedef std::map string_map_t; - string_map_t entries = file.getEntries(); - - if (entries.find("detector_1_events") == entries.end()) - {// not an ISIS file - return; - } - - // try if monitors have their own bins - if (classType == "NXmonitor") - { - std::vector bankNames; - for (string_map_t::const_iterator it = entries.begin(); it != entries.end(); ++it) + //----------------------------------------------------------------------------- + /** + * Load the Monitors from the NeXus file into a workspace. The original + * workspace name is used and appended with _monitors. + */ + void LoadEventNexus::runLoadMonitors() { - std::string entry_name(it->first); - std::string entry_class(it->second); - if ( entry_class == classType ) + std::string mon_wsname = this->getProperty("OutputWorkspace"); + mon_wsname.append("_monitors"); + + IAlgorithm_sptr loadMonitors = this->createChildAlgorithm("LoadNexusMonitors"); + try + { + g_log.information("Loading monitors from NeXus file..."); + loadMonitors->setPropertyValue("Filename", m_filename); + g_log.information() << "New workspace name for monitors: " << mon_wsname << std::endl; + loadMonitors->setPropertyValue("OutputWorkspace", mon_wsname); + loadMonitors->execute(); + MatrixWorkspace_sptr mons = loadMonitors->getProperty("OutputWorkspace"); + this->declareProperty(new WorkspaceProperty<>("MonitorWorkspace", + mon_wsname, Direction::Output), "Monitors from the Event NeXus file"); + this->setProperty("MonitorWorkspace", mons); + // Set the internal monitor workspace pointer as well + WS->setMonitorWorkspace(mons); + + filterDuringPause(mons); + } + catch (...) { - bankNames.push_back( entry_name ); + g_log.error("Error while loading the monitors from the file. File may contain no monitors."); } } - for(size_t i = 0; i < bankNames.size(); ++i) + + // + /** + * Load a spectra mapping from the given file. This currently checks for the existence of + * an isis_vms_compat block in the file, if it exists it pulls out the spectra mapping listed there + * @param filename :: A filename + * @param monitorsOnly :: If true then only the monitor spectra are loaded + * @param entry_name :: name of the NXentry to open. + * @returns True if the mapping was loaded or false if the block does not exist + */ + bool LoadEventNexus::loadSpectraMapping(const std::string& filename, const bool monitorsOnly, const std::string& entry_name ) { - const std::string& mon = bankNames[i]; - file.openGroup(mon,classType); - entries = file.getEntries(); - string_map_t::const_iterator bins = entries.find("event_time_bins"); - if (bins == entries.end()) - { - //bins = entries.find("time_of_flight"); // I think time_of_flight doesn't work here - //if (bins == entries.end()) - //{ - done = false; - file.closeGroup(); - break; // done == false => use bins from the detectors - //} + ::NeXus::File file(filename); + try + { + g_log.debug() << "Attempting to load custom spectra mapping from '" << entry_name << "/isis_vms_compat'.\n"; + file.openPath(entry_name + "/isis_vms_compat"); } - done = true; - loadTimeOfFlightData(file,WS,bins->first,i,i+1); + catch(::NeXus::Exception&) + { + return false; // Doesn't exist + } + + // The ISIS spectrum mapping is defined by 2 arrays in isis_vms_compat block: + // UDET - An array of detector IDs + // SPEC - An array of spectrum numbers + // There sizes must match. Hardware allows more than one detector ID to be mapped to a single spectrum + // and this is encoded in the SPEC/UDET arrays by repeating the spectrum number in the array + // for each mapped detector, e.g. + // + // 1 1001 + // 1 1002 + // 2 2001 + // 3 3001 + // + // defines 3 spectra, where the first spectrum contains 2 detectors + + // UDET + file.openData("UDET"); + std::vector udet; + file.getData(udet); + file.closeData(); + // SPEC + file.openData("SPEC"); + std::vector spec; + file.getData(spec); + file.closeData(); + // Close file.closeGroup(); + file.close(); + + // The spec array will contain a spectrum number for each udet but the spectrum number + // may be the same for more that one detector + const size_t ndets(udet.size()); + if( ndets != spec.size() ) + { + std::ostringstream os; + os << "UDET/SPEC list size mismatch. UDET=" << udet.size() << ", SPEC=" << spec.size() << "\n"; + throw std::runtime_error(os.str()); + } + // Monitor filtering/selection + const std::vector monitors = WS->getInstrument()->getMonitors(); + const size_t nmons(monitors.size()); + if( monitorsOnly ) + { + g_log.debug() << "Loading only monitor spectra from " << filename << "\n"; + // Find the det_ids in the udet array. + WS->resizeTo(nmons); + for( size_t i = 0; i < nmons; ++i ) + { + // Find the index in the udet array + const detid_t & id = monitors[i]; + std::vector::const_iterator it = std::find(udet.begin(), udet.end(), id); + if( it != udet.end() ) + { + auto spectrum = WS->getSpectrum(i); + const specid_t & specNo = spec[it - udet.begin()]; + spectrum->setSpectrumNo(specNo); + spectrum->setDetectorID(id); + } + } + } + else + { + g_log.debug() << "Loading only detector spectra from " << filename << "\n"; + SpectrumDetectorMapping mapping(spec,udet, monitors); + WS->resizeTo(mapping.getMapping().size()); + // Make sure spectrum numbers are correct + auto uniqueSpectra = mapping.getSpectrumNumbers(); + auto itend = uniqueSpectra.end(); + size_t counter = 0; + for(auto it = uniqueSpectra.begin(); it != itend; ++it) + { + WS->getSpectrum(counter)->setSpectrumNo(*it); + ++counter; + } + // Fill detectors based on this mapping + WS->updateSpectraUsing(mapping); + } + return true; } - } - if (!done) - { - // first check detector_1_events - file.openGroup("detector_1_events", "NXevent_data"); - entries = file.getEntries(); - for(string_map_t::const_iterator it = entries.begin();it != entries.end(); ++it) + /** + * Set the filters on TOF. + * @param monitors :: If true check the monitor properties else use the standard ones + */ + void LoadEventNexus::setTimeFilters(const bool monitors) { - if (it->first == "time_of_flight" || it->first == "event_time_bins") + //Get the limits to the filter + std::string prefix("Filter"); + if(monitors) prefix += "Mon"; + + filter_tof_min = getProperty(prefix + "ByTofMin"); + filter_tof_max = getProperty(prefix + "ByTofMax"); + if ( (filter_tof_min == EMPTY_DBL()) && (filter_tof_max == EMPTY_DBL())) + { + //Nothing specified. Include everything + filter_tof_min = -1e20; + filter_tof_max = +1e20; + } + else if ( (filter_tof_min != EMPTY_DBL()) && (filter_tof_max != EMPTY_DBL())) { - loadTimeOfFlightData(file,WS,it->first); - done = true; + //Both specified. Keep these values } + else + { + std::string msg("You must specify both min & max or neither TOF filters"); + if(monitors) msg = " for the monitors."; + throw std::invalid_argument(msg); + } + } - file.closeGroup(); // detector_1_events - if (!done) // if time_of_flight was not found try instrument/dae/time_channels_# + //----------------------------------------------------------------------------- + // ISIS event corrections + //----------------------------------------------------------------------------- + /** + * Check if time_of_flight can be found in the file and load it + * @param nexusfilename :: The name of the ISIS nexus event file. + * @param WS :: The event workspace which events will be modified. + * @param entry_name :: An NXentry tag in the file + * @param classType :: The type of the events: either detector or monitor + */ + void LoadEventNexus::loadTimeOfFlight(const std::string &nexusfilename, DataObjects::EventWorkspace_sptr WS, + const std::string &entry_name, const std::string &classType) { - file.openGroup("instrument","NXinstrument"); - file.openGroup("dae","IXdae"); - entries = file.getEntries(); - size_t time_channels_number = 0; - for(string_map_t::const_iterator it = entries.begin();it != entries.end(); ++it) + bool done = false; + // Open the file + ::NeXus::File file(nexusfilename); + file.openGroup(entry_name, "NXentry"); + + typedef std::map string_map_t; + string_map_t entries = file.getEntries(); + + if (entries.find("detector_1_events") == entries.end()) + {// not an ISIS file + return; + } + + // try if monitors have their own bins + if (classType == "NXmonitor") { - // check if there are groups with names "time_channels_#" and select the one with the highest number - if (it->first.size() > 14 && it->first.substr(0,14) == "time_channels_") + std::vector bankNames; + for (string_map_t::const_iterator it = entries.begin(); it != entries.end(); ++it) + { + std::string entry_name(it->first); + std::string entry_class(it->second); + if ( entry_class == classType ) + { + bankNames.push_back( entry_name ); + } + } + for(size_t i = 0; i < bankNames.size(); ++i) { - size_t n = boost::lexical_cast(it->first.substr(14)); - if (n > time_channels_number) + const std::string& mon = bankNames[i]; + file.openGroup(mon,classType); + entries = file.getEntries(); + string_map_t::const_iterator bins = entries.find("event_time_bins"); + if (bins == entries.end()) { - time_channels_number = n; + //bins = entries.find("time_of_flight"); // I think time_of_flight doesn't work here + //if (bins == entries.end()) + //{ + done = false; + file.closeGroup(); + break; // done == false => use bins from the detectors + //} } + done = true; + loadTimeOfFlightData(file,WS,bins->first,i,i+1); + file.closeGroup(); } } - if (time_channels_number > 0) // the numbers start with 1 + + if (!done) { - file.openGroup("time_channels_" + boost::lexical_cast(time_channels_number),"IXtime_channels"); + // first check detector_1_events + file.openGroup("detector_1_events", "NXevent_data"); entries = file.getEntries(); for(string_map_t::const_iterator it = entries.begin();it != entries.end(); ++it) { if (it->first == "time_of_flight" || it->first == "event_time_bins") { loadTimeOfFlightData(file,WS,it->first); + done = true; } } - file.closeGroup(); + file.closeGroup(); // detector_1_events + + if (!done) // if time_of_flight was not found try instrument/dae/time_channels_# + { + file.openGroup("instrument","NXinstrument"); + file.openGroup("dae","IXdae"); + entries = file.getEntries(); + size_t time_channels_number = 0; + for(string_map_t::const_iterator it = entries.begin();it != entries.end(); ++it) + { + // check if there are groups with names "time_channels_#" and select the one with the highest number + if (it->first.size() > 14 && it->first.substr(0,14) == "time_channels_") + { + size_t n = boost::lexical_cast(it->first.substr(14)); + if (n > time_channels_number) + { + time_channels_number = n; + } + } + } + if (time_channels_number > 0) // the numbers start with 1 + { + file.openGroup("time_channels_" + boost::lexical_cast(time_channels_number),"IXtime_channels"); + entries = file.getEntries(); + for(string_map_t::const_iterator it = entries.begin();it != entries.end(); ++it) + { + if (it->first == "time_of_flight" || it->first == "event_time_bins") + { + loadTimeOfFlightData(file,WS,it->first); + } + } + file.closeGroup(); + } + file.closeGroup(); // dae + file.closeGroup(); // instrument + } } - file.closeGroup(); // dae - file.closeGroup(); // instrument + + file.close(); } - } - - file.close(); -} - -//----------------------------------------------------------------------------- -/** - * Load the time of flight data. file must have open the group containing "time_of_flight" data set. - * @param file :: The nexus file to read from. - * @param WS :: The event workspace to write to. - * @param binsName :: bins name - * @param start_wi :: First workspace index to process - * @param end_wi :: Last workspace index to process - */ -void LoadEventNexus::loadTimeOfFlightData(::NeXus::File& file, DataObjects::EventWorkspace_sptr WS, - const std::string& binsName,size_t start_wi, size_t end_wi) -{ - // first check if the data is already randomized - std::map entries; - file.getEntries(entries); - std::map::const_iterator shift = entries.find("event_time_offset_shift"); - if (shift != entries.end()) - { - std::string random; - file.readData("event_time_offset_shift",random); - if (random == "random") + + //----------------------------------------------------------------------------- + /** + * Load the time of flight data. file must have open the group containing "time_of_flight" data set. + * @param file :: The nexus file to read from. + * @param WS :: The event workspace to write to. + * @param binsName :: bins name + * @param start_wi :: First workspace index to process + * @param end_wi :: Last workspace index to process + */ + void LoadEventNexus::loadTimeOfFlightData(::NeXus::File& file, DataObjects::EventWorkspace_sptr WS, + const std::string& binsName,size_t start_wi, size_t end_wi) { - return; - } - } - - // if the data is not randomized randomize it uniformly within each bin - file.openData(binsName); - // time of flights of events - std::vector tof; - file.getData(tof); - // todo: try to find if tof can be reduced to just 3 numbers: start, end and dt - if (end_wi <= start_wi) - { - end_wi = WS->getNumberHistograms(); - } + // first check if the data is already randomized + std::map entries; + file.getEntries(entries); + std::map::const_iterator shift = entries.find("event_time_offset_shift"); + if (shift != entries.end()) + { + std::string random; + file.readData("event_time_offset_shift",random); + if (random == "random") + { + return; + } + } - // random number generator - boost::mt19937 rand_gen; + // if the data is not randomized randomize it uniformly within each bin + file.openData(binsName); + // time of flights of events + std::vector tof; + file.getData(tof); + // todo: try to find if tof can be reduced to just 3 numbers: start, end and dt + if (end_wi <= start_wi) + { + end_wi = WS->getNumberHistograms(); + } - // loop over spectra - for(size_t wi = start_wi; wi < end_wi; ++wi) - { - EventList& event_list = WS->getEventList(wi); - // sort the events - event_list.sortTof(); - std::vector& events = event_list.getEvents(); - if (events.empty()) continue; - size_t n = tof.size(); - // iterate over the events and time bins - std::vector::iterator ev = events.begin(); - std::vector::iterator ev_end = events.end(); - for(size_t i = 1; i < n; ++i) - { - double right = double(tof[i]); - // find the right boundary for the current event - if(ev != ev_end && right < ev->tof() ) - { - continue; - } - // count events which have the same right boundary - size_t m = 0; - while(ev != ev_end && ev->tof() < right) - { - ++ev; - ++m; // count events in the i-th bin - } - - if (m > 0) - {// m events in this bin - double left = double(tof[i-1]); - // spread the events uniformly inside the bin - boost::uniform_real<> distribution(left,right); - std::vector random_numbers(m); - for(std::vector::iterator it = random_numbers.begin(); it != random_numbers.end(); ++it) - { - *it = distribution(rand_gen); - } - std::sort(random_numbers.begin(),random_numbers.end()); - std::vector::iterator it = random_numbers.begin(); - for(std::vector::iterator ev1 = ev - m; ev1 != ev; ++ev1,++it) - { - ev1->m_tof = *it; - } - } - - } // for i - - event_list.sortTof(); - } // for wi - file.closeData(); -} - -/** Load information of the sample. It is valid only for ISIS it get the information from - * the group isis_vms_compat. - * - * If it does not find this group, it assumes that there is nothing to do. - * But, if the information is there, but not in the way it was expected, it will log the occurrence. - * - * @note: It does essentially the same thing of the method: LoadISISNexus2::loadSampleData - * - * @param file : handle to the nexus file - * @param WS : pointer to the workspace - */ -void LoadEventNexus::loadSampleDataISIScompatibility(::NeXus::File& file, Mantid::API::MatrixWorkspace_sptr WS){ - try - { - file.openGroup("isis_vms_compat", "IXvms"); - } - catch( ::NeXus::Exception & ) - { - // No problem, it just means that this entry does not exist - return; - } + // random number generator + boost::mt19937 rand_gen; - // read the data - try - { - std::vector spb; - std::vector rspb; - file.readData("SPB", spb); - file.readData("RSPB",rspb); - - WS->mutableSample().setGeometryFlag(spb[2]); // the flag is in the third value - WS->mutableSample().setThickness(rspb[3]); - WS->mutableSample().setHeight(rspb[4]); - WS->mutableSample().setWidth(rspb[5]); - } - catch ( ::NeXus::Exception & ex) - { - // it means that the data was not as expected, report the problem - std::stringstream s; - s << "Wrong definition found in isis_vms_compat :> " << ex.what(); - file.closeGroup(); - throw std::runtime_error(s.str()); - } + // loop over spectra + for(size_t wi = start_wi; wi < end_wi; ++wi) + { + EventList& event_list = WS->getEventList(wi); + // sort the events + event_list.sortTof(); + std::vector& events = event_list.getEvents(); + if (events.empty()) continue; + size_t n = tof.size(); + // iterate over the events and time bins + std::vector::iterator ev = events.begin(); + std::vector::iterator ev_end = events.end(); + for(size_t i = 1; i < n; ++i) + { + double right = double(tof[i]); + // find the right boundary for the current event + if(ev != ev_end && right < ev->tof() ) + { + continue; + } + // count events which have the same right boundary + size_t m = 0; + while(ev != ev_end && ev->tof() < right) + { + ++ev; + ++m; // count events in the i-th bin + } - file.closeGroup(); -} + if (m > 0) + {// m events in this bin + double left = double(tof[i-1]); + // spread the events uniformly inside the bin + boost::uniform_real<> distribution(left,right); + std::vector random_numbers(m); + for(std::vector::iterator it = random_numbers.begin(); it != random_numbers.end(); ++it) + { + *it = distribution(rand_gen); + } + std::sort(random_numbers.begin(),random_numbers.end()); + std::vector::iterator it = random_numbers.begin(); + for(std::vector::iterator ev1 = ev - m; ev1 != ev; ++ev1,++it) + { + ev1->m_tof = *it; + } + } -void LoadEventNexus::filterDuringPause(API::MatrixWorkspace_sptr workspace) -{ - try { - if ( ( ! ConfigService::Instance().hasProperty("loadeventnexus.keeppausedevents") ) && ( WS->run().getLogData("pause")->size() > 1 ) ) - { - g_log.notice("Filtering out events when the run was marked as paused. " - "Set the loadeventnexus.keeppausedevents configuration property to override this."); - - auto filter = createChildAlgorithm("FilterByLogValue"); - filter->setProperty("InputWorkspace", workspace); - filter->setProperty("OutputWorkspace", workspace); - filter->setProperty("LogName", "pause"); - // The log value is set to 1 when the run is paused, 0 otherwise. - filter->setProperty("MinimumValue", 0.0); - filter->setProperty("MaximumValue", 0.0); - filter->setProperty("LogBoundary", "Left"); - filter->execute(); + } // for i + + event_list.sortTof(); + } // for wi + file.closeData(); } - } catch ( Exception::NotFoundError& ) { - // No "pause" log, just carry on - } -} - - -/** Load the instrument from the nexus file - * - * @param nexusfilename :: The name of the nexus file being loaded - * @param localWorkspace :: MatrixWorkspace in which to put the instrument geometry - * @param alg :: Handle of the algorithm - * @param returnpulsetimes :: flag to return shared pointer for BankPulseTimes, otherwise NULL. - * @return true if successful - */ -boost::shared_ptr LoadEventNexus::runLoadNexusLogs(const std::string &nexusfilename, - API::MatrixWorkspace_sptr localWorkspace, - API::Algorithm &alg, bool returnpulsetimes) -{ - // --------------------- Load DAS Logs ----------------- - //The pulse times will be empty if not specified in the DAS logs. - // BankPulseTimes * out = NULL; - boost::shared_ptr out; - API::IAlgorithm_sptr loadLogs = alg.createChildAlgorithm("LoadNexusLogs"); - - // Now execute the Child Algorithm. Catch and log any error, but don't stop. - try - { - alg.getLogger().information() << "Loading logs from NeXus file..." << "\n"; - loadLogs->setPropertyValue("Filename", nexusfilename); - loadLogs->setProperty ("Workspace", localWorkspace); - loadLogs->execute(); - //If successful, we can try to load the pulse times - Kernel::TimeSeriesProperty * log = dynamic_cast *>( - localWorkspace->mutableRun().getProperty("proton_charge") ); - const std::vector temp = log->timesAsVector(); - // if (returnpulsetimes) out = new BankPulseTimes(temp); - if (returnpulsetimes) - out = boost::make_shared(temp); + /** Load information of the sample. It is valid only for ISIS it get the information from + * the group isis_vms_compat. + * + * If it does not find this group, it assumes that there is nothing to do. + * But, if the information is there, but not in the way it was expected, it will log the occurrence. + * + * @note: It does essentially the same thing of the method: LoadISISNexus2::loadSampleData + * + * @param file : handle to the nexus file + * @param WS : pointer to the workspace + */ + void LoadEventNexus::loadSampleDataISIScompatibility(::NeXus::File& file, Mantid::API::MatrixWorkspace_sptr WS){ + try + { + file.openGroup("isis_vms_compat", "IXvms"); + } + catch( ::NeXus::Exception & ) + { + // No problem, it just means that this entry does not exist + return; + } - // Use the first pulse as the run_start time. - if (!temp.empty()) - { - if (temp[0] < Kernel::DateAndTime("1991-01-01T00:00:00")) - alg.getLogger().warning() << "Found entries in the proton_charge sample log with invalid pulse time!\n"; + // read the data + try + { + std::vector spb; + std::vector rspb; + file.readData("SPB", spb); + file.readData("RSPB",rspb); + + WS->mutableSample().setGeometryFlag(spb[2]); // the flag is in the third value + WS->mutableSample().setThickness(rspb[3]); + WS->mutableSample().setHeight(rspb[4]); + WS->mutableSample().setWidth(rspb[5]); + } + catch ( ::NeXus::Exception & ex) + { + // it means that the data was not as expected, report the problem + std::stringstream s; + s << "Wrong definition found in isis_vms_compat :> " << ex.what(); + file.closeGroup(); + throw std::runtime_error(s.str()); + } - Kernel::DateAndTime run_start = localWorkspace->getFirstPulseTime(); - // add the start of the run as a ISO8601 date/time string. The start = first non-zero time. - // (this is used in LoadInstrument to find the right instrument file to use). - localWorkspace->mutableRun().addProperty("run_start", run_start.toISO8601String(), true ); - } - else - { - alg.getLogger().warning() << "Empty proton_charge sample log. You will not be able to filter by time.\n"; + file.closeGroup(); } - /// Attempt to make a gonoimeter from the logs - try + + void LoadEventNexus::filterDuringPause(API::MatrixWorkspace_sptr workspace) { - Geometry::Goniometer gm; - gm.makeUniversalGoniometer(); - localWorkspace->mutableRun().setGoniometer(gm, true); + try { + if ( ( ! ConfigService::Instance().hasProperty("loadeventnexus.keeppausedevents") ) && ( WS->run().getLogData("pause")->size() > 1 ) ) + { + g_log.notice("Filtering out events when the run was marked as paused. " + "Set the loadeventnexus.keeppausedevents configuration property to override this."); + + auto filter = createChildAlgorithm("FilterByLogValue"); + filter->setProperty("InputWorkspace", workspace); + filter->setProperty("OutputWorkspace", workspace); + filter->setProperty("LogName", "pause"); + // The log value is set to 1 when the run is paused, 0 otherwise. + filter->setProperty("MinimumValue", 0.0); + filter->setProperty("MaximumValue", 0.0); + filter->setProperty("LogBoundary", "Left"); + filter->execute(); + } + } catch ( Exception::NotFoundError& ) { + // No "pause" log, just carry on + } } - catch(std::runtime_error &) + + + /** Load the instrument from the nexus file + * + * @param nexusfilename :: The name of the nexus file being loaded + * @param localWorkspace :: MatrixWorkspace in which to put the instrument geometry + * @param alg :: Handle of the algorithm + * @param returnpulsetimes :: flag to return shared pointer for BankPulseTimes, otherwise NULL. + * @return true if successful + */ + boost::shared_ptr LoadEventNexus::runLoadNexusLogs(const std::string &nexusfilename, + API::MatrixWorkspace_sptr localWorkspace, + API::Algorithm &alg, bool returnpulsetimes) { + // --------------------- Load DAS Logs ----------------- + //The pulse times will be empty if not specified in the DAS logs. + // BankPulseTimes * out = NULL; + boost::shared_ptr out; + API::IAlgorithm_sptr loadLogs = alg.createChildAlgorithm("LoadNexusLogs"); + + // Now execute the Child Algorithm. Catch and log any error, but don't stop. + try + { + alg.getLogger().information() << "Loading logs from NeXus file..." << "\n"; + loadLogs->setPropertyValue("Filename", nexusfilename); + loadLogs->setProperty ("Workspace", localWorkspace); + loadLogs->execute(); + + //If successful, we can try to load the pulse times + Kernel::TimeSeriesProperty * log = dynamic_cast *>( + localWorkspace->mutableRun().getProperty("proton_charge") ); + const std::vector temp = log->timesAsVector(); + // if (returnpulsetimes) out = new BankPulseTimes(temp); + if (returnpulsetimes) + out = boost::make_shared(temp); + + // Use the first pulse as the run_start time. + if (!temp.empty()) + { + if (temp[0] < Kernel::DateAndTime("1991-01-01T00:00:00")) + alg.getLogger().warning() << "Found entries in the proton_charge sample log with invalid pulse time!\n"; + + Kernel::DateAndTime run_start = localWorkspace->getFirstPulseTime(); + // add the start of the run as a ISO8601 date/time string. The start = first non-zero time. + // (this is used in LoadInstrument to find the right instrument file to use). + localWorkspace->mutableRun().addProperty("run_start", run_start.toISO8601String(), true ); + } + else + { + alg.getLogger().warning() << "Empty proton_charge sample log. You will not be able to filter by time.\n"; + } + /// Attempt to make a gonoimeter from the logs + try + { + Geometry::Goniometer gm; + gm.makeUniversalGoniometer(); + localWorkspace->mutableRun().setGoniometer(gm, true); + } + catch(std::runtime_error &) + { + } + } + catch (...) + { + alg.getLogger().error() << "Error while loading Logs from SNS Nexus. Some sample logs may be missing." << "\n"; + return out; + } + return out; } - } - catch (...) - { - alg.getLogger().error() << "Error while loading Logs from SNS Nexus. Some sample logs may be missing." << "\n"; - return out; - } - return out; -} -} // namespace DataHandling + } // namespace DataHandling } // namespace Mantid diff --git a/Code/Mantid/Framework/DataHandling/src/LoadNexusMonitors.cpp b/Code/Mantid/Framework/DataHandling/src/LoadNexusMonitors.cpp index 7418eaf89e6a..56bc58ea0284 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadNexusMonitors.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadNexusMonitors.cpp @@ -42,7 +42,7 @@ LoadNexusMonitors::~LoadNexusMonitors() { } -/// Initialisation method. +/// Initialization method. void LoadNexusMonitors::init() { declareProperty(new API::FileProperty("Filename", "", API::FileProperty::Load, @@ -76,12 +76,12 @@ void LoadNexusMonitors::exec() string_map_t entries = file.getEntries(); for (it = entries.begin(); it != entries.end(); ++it) { - if ( ((it->first == "entry") || (it->first == "raw_data_1")) && (it->second == "NXentry") ) - { - file.openGroup(it->first, it->second); + if ( ((it->first == "entry") || (it->first == "raw_data_1")) && (it->second == "NXentry") ) + { + file.openGroup(it->first, it->second); m_top_entry_name = it->first; - break; - } + break; + } } prog1.report(); @@ -353,14 +353,25 @@ void LoadNexusMonitors::exec() // Need to get the instrument name from the file std::string instrumentName; file.openGroup("instrument", "NXinstrument"); - file.openData("name"); - instrumentName = file.getStrData(); + try + { + file.openData("name"); + instrumentName = file.getStrData(); + // Now let's close the file as we don't need it anymore to load the instrument. + file.closeData(); + file.closeGroup(); // Close the NXentry + file.close(); + + } + catch(std::runtime_error &) // no correct instrument definition (old ISIS file, fall back to isis_vms_compat) + { + file.closeGroup(); // Close the instrument NXentry + instrumentName =LoadEventNexus::readInstrumentFromISIS_VMSCompat(file); + file.close(); + } + g_log.debug() << "Instrument name read from NeXus file is " << instrumentName << std::endl; - // Now let's close the file as we don't need it anymore to load the instrument. - file.closeData(); - file.closeGroup(); // Close the NXentry - file.close(); this->WS->getAxis(0)->unit() = Kernel::UnitFactory::Instance().create("TOF"); this->WS->setYUnit("Counts"); From eaf7cccdd980f7cbc91a46776ebf4c255666951b Mon Sep 17 00:00:00 2001 From: Alex Buts Date: Tue, 7 Oct 2014 13:41:40 +0100 Subject: [PATCH 05/48] refs #10309 unit test for the changes --- .../DataHandling/test/LoadNexusMonitorsTest.h | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/Code/Mantid/Framework/DataHandling/test/LoadNexusMonitorsTest.h b/Code/Mantid/Framework/DataHandling/test/LoadNexusMonitorsTest.h index 35762dab4986..9243e4295ed3 100644 --- a/Code/Mantid/Framework/DataHandling/test/LoadNexusMonitorsTest.h +++ b/Code/Mantid/Framework/DataHandling/test/LoadNexusMonitorsTest.h @@ -95,6 +95,32 @@ class LoadNexusMonitorsTest : public CxxTest::TestSuite TS_ASSERT( ld.isExecuted() ); } + void testBrokenISISFile() + { + // Just need to make sure it runs. + Mantid::API::FrameworkManager::Instance(); + LoadNexusMonitors ld; + std::string outws_name = "LOQ_49886_monitors"; + ld.initialize(); + ld.setPropertyValue("Filename", "LOQ49886.nxs"); + ld.setPropertyValue("OutputWorkspace", outws_name); + TS_ASSERT_THROWS_NOTHING( ld.execute() ); + TS_ASSERT( ld.isExecuted() ); + + MatrixWorkspace_sptr WS = AnalysisDataService::Instance().retrieveWS(outws_name); + //Valid WS and it is an MatrixWorkspace + TS_ASSERT( WS ); + //Correct number of monitors found + TS_ASSERT_EQUALS( WS->getNumberHistograms(), 2 ); + //Monitors data is correct + TS_ASSERT_EQUALS( WS->readY(0)[0], 0 ); + TS_ASSERT_EQUALS( WS->readY(1)[0], 0 ); + + TS_ASSERT_EQUALS( WS->readX(0)[0], 5.0 ); + TS_ASSERT_EQUALS( WS->readX(1)[5],1995.0 ); + + } + void test_10_monitors() { Poco::Path path(ConfigService::Instance().getTempDir().c_str()); From b578ede841a192e5ad2c519b33300b8edf39a56e Mon Sep 17 00:00:00 2001 From: Alex Buts Date: Tue, 7 Oct 2014 13:59:56 +0100 Subject: [PATCH 06/48] refs #10309 Typo in unit test --- Code/Mantid/Framework/DataHandling/test/LoadNexusMonitorsTest.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Mantid/Framework/DataHandling/test/LoadNexusMonitorsTest.h b/Code/Mantid/Framework/DataHandling/test/LoadNexusMonitorsTest.h index 9243e4295ed3..4cfec9b4c694 100644 --- a/Code/Mantid/Framework/DataHandling/test/LoadNexusMonitorsTest.h +++ b/Code/Mantid/Framework/DataHandling/test/LoadNexusMonitorsTest.h @@ -117,7 +117,7 @@ class LoadNexusMonitorsTest : public CxxTest::TestSuite TS_ASSERT_EQUALS( WS->readY(1)[0], 0 ); TS_ASSERT_EQUALS( WS->readX(0)[0], 5.0 ); - TS_ASSERT_EQUALS( WS->readX(1)[5],1995.0 ); + TS_ASSERT_EQUALS( WS->readX(1)[5],19995.0 ); } From ce95b6742782920fa944b581aa5368b0badb0bae Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Tue, 7 Oct 2014 17:05:19 +0200 Subject: [PATCH 07/48] Refs #10281. Creating vectors of symmetry operations. --- .../Crystal/SymmetryOperationFactory.h | 2 + .../src/Crystal/SymmetryOperationFactory.cpp | 21 ++++++++++ .../test/SymmetryOperationFactoryTest.h | 38 +++++++++++++++++++ 3 files changed, 61 insertions(+) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SymmetryOperationFactory.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SymmetryOperationFactory.h index 0f5d2d1a2fe3..fe4095486338 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SymmetryOperationFactory.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SymmetryOperationFactory.h @@ -57,6 +57,8 @@ namespace Geometry { public: SymmetryOperation createSymOp(const std::string &identifier); + std::vector createSymOps(const std::string &identifiers); + std::vector createSymOps(const std::vector &identifiers); void subscribeSymOp(const std::string &identifier); void unsubscribeSymOp(const std::string &identifier); diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/SymmetryOperationFactory.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/SymmetryOperationFactory.cpp index 688919bb73d8..f8a911561d77 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/SymmetryOperationFactory.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/SymmetryOperationFactory.cpp @@ -3,6 +3,7 @@ #include "MantidKernel/Exception.h" #include +#include namespace Mantid { @@ -19,6 +20,26 @@ SymmetryOperation SymmetryOperationFactoryImpl::createSymOp(const std::string &i return SymmetryOperation(m_prototypes[identifier]); } +/// Creates all symmetry operations in string (separated by semicolon). +std::vector SymmetryOperationFactoryImpl::createSymOps(const std::string &identifiers) +{ + std::vector symOpStrings; + boost::split(symOpStrings, identifiers, boost::is_any_of(";")); + + return createSymOps(symOpStrings); +} + +/// Creates all symmetry operations with the given strings (whitespaces at beginning and end are removed). +std::vector SymmetryOperationFactoryImpl::createSymOps(const std::vector &identifiers) +{ + std::vector symOps; + for(auto it = identifiers.begin(); it != identifiers.end(); ++it) { + symOps.push_back(createSymOp(boost::trim_copy(*it))); + } + + return symOps; +} + /// Subscribes a symmetry operation into the factory void SymmetryOperationFactoryImpl::subscribeSymOp(const std::string &identifier) { diff --git a/Code/Mantid/Framework/Geometry/test/SymmetryOperationFactoryTest.h b/Code/Mantid/Framework/Geometry/test/SymmetryOperationFactoryTest.h index 79242eee1bde..50db96be1f8b 100644 --- a/Code/Mantid/Framework/Geometry/test/SymmetryOperationFactoryTest.h +++ b/Code/Mantid/Framework/Geometry/test/SymmetryOperationFactoryTest.h @@ -47,6 +47,44 @@ class SymmetryOperationFactoryTest : public CxxTest::TestSuite TS_ASSERT_EQUALS(SymmetryOperationFactory::Instance().isSubscribed("x,y,z"), true); } + void testCreateSymOpsVector() + { + std::vector opStrings; + opStrings.push_back("x,y,z"); + + std::vector symOps = SymmetryOperationFactory::Instance().createSymOps(opStrings); + TS_ASSERT_EQUALS(symOps.size(), 1); + TS_ASSERT_EQUALS(symOps.front().identifier(), "x,y,z"); + + // Add another one + opStrings.push_back("-x,-y,-z"); + + TS_ASSERT_THROWS_NOTHING(symOps = SymmetryOperationFactory::Instance().createSymOps(opStrings)); + TS_ASSERT_EQUALS(symOps.size(), 2); + TS_ASSERT_EQUALS(symOps.front().identifier(), "x,y,z"); + TS_ASSERT_EQUALS(symOps.back().identifier(), "-x,-y,-z"); + + opStrings.push_back("doesNotWork"); + TS_ASSERT_THROWS(symOps = SymmetryOperationFactory::Instance().createSymOps(opStrings), Mantid::Kernel::Exception::ParseError); + } + + void testCreateSymOpsString() + { + std::string validOne("-x,-y,-z"); + std::string validTwo("-x,-y,-z; x+1/2,y+1/2,z+1/2"); + std::string validThree("-x,-y,-z; x+1/2,y+1/2,z+1/2; x,-y,z"); + + TS_ASSERT_THROWS_NOTHING(SymmetryOperationFactory::Instance().createSymOps(validOne)); + TS_ASSERT_THROWS_NOTHING(SymmetryOperationFactory::Instance().createSymOps(validTwo)); + TS_ASSERT_THROWS_NOTHING(SymmetryOperationFactory::Instance().createSymOps(validThree)); + + std::string invalidSep("-x,-y,-z | x+1/2,y+1/2,z+1/2"); + std::string invalidOne("-x,-y,-z; invalid"); + + TS_ASSERT_THROWS(SymmetryOperationFactory::Instance().createSymOps(invalidSep), Mantid::Kernel::Exception::ParseError); + TS_ASSERT_THROWS(SymmetryOperationFactory::Instance().createSymOps(invalidOne), Mantid::Kernel::Exception::ParseError); + } + void testUnsubscribe() { TS_ASSERT_EQUALS(SymmetryOperationFactory::Instance().isSubscribed("x,y,z"), true); From 93c1bedec25ace8ef1c59547ea18bbaae041af62 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Tue, 7 Oct 2014 17:05:40 +0200 Subject: [PATCH 08/48] Refs #10280. Added class to deal with lattice centering --- Code/Mantid/Framework/Geometry/CMakeLists.txt | 3 + .../MantidGeometry/Crystal/CenteringGroup.h | 95 +++++++++++++ .../Geometry/src/Crystal/CenteringGroup.cpp | 134 ++++++++++++++++++ .../Geometry/test/CenteringGroupTest.h | 65 +++++++++ 4 files changed, 297 insertions(+) create mode 100644 Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h create mode 100644 Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp create mode 100644 Code/Mantid/Framework/Geometry/test/CenteringGroupTest.h diff --git a/Code/Mantid/Framework/Geometry/CMakeLists.txt b/Code/Mantid/Framework/Geometry/CMakeLists.txt index 8d9a6e1fab06..2f2a79062784 100644 --- a/Code/Mantid/Framework/Geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/Geometry/CMakeLists.txt @@ -1,5 +1,6 @@ set ( SRC_FILES src/ComponentParser.cpp + src/Crystal/CenteringGroup.cpp src/Crystal/ConventionalCell.cpp src/Crystal/CrystalStructure.cpp src/Crystal/CyclicGroup.cpp @@ -107,6 +108,7 @@ set ( SRC_UNITY_IGNORE_FILES src/Instrument/CompAssembly.cpp set ( INC_FILES inc/MantidGeometry/ComponentParser.h + inc/MantidGeometry/Crystal/CenteringGroup.h inc/MantidGeometry/Crystal/ConventionalCell.h inc/MantidGeometry/Crystal/CrystalStructure.h inc/MantidGeometry/Crystal/CyclicGroup.h @@ -210,6 +212,7 @@ set ( TEST_FILES AlgebraTest.h BnIdTest.h BoundingBoxTest.h + CenteringGroupTest.h CompAssemblyTest.h ComponentHelperTest.h ComponentParserTest.h diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h new file mode 100644 index 000000000000..ba6aabef1b5e --- /dev/null +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h @@ -0,0 +1,95 @@ +#ifndef MANTID_GEOMETRY_CENTERINGGROUP_H_ +#define MANTID_GEOMETRY_CENTERINGGROUP_H_ + +#include "MantidGeometry/DllConfig.h" +#include "MantidGeometry/Crystal/Group.h" +#include + +namespace Mantid +{ +namespace Geometry +{ + +/** CenteringGroup + + A class that holds symmetry operations to describe a lattice + centering. + + @author Michael Wedel, Paul Scherrer Institut - SINQ + @date 07/10/2014 + + Copyright © 2014 PSI-MSS + + 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 MANTID_GEOMETRY_DLL CenteringGroup : public Group +{ +public: + enum CenteringType { + P, I, A, B, C, F, Robv, Rrev + }; + + CenteringGroup(const std::string ¢eringSymbol); + virtual ~CenteringGroup() { } + + CenteringType getType() const; + std::string getSymbol() const; + +protected: + CenteringType m_type; + std::string m_symbol; + +private: + /// Private helper class to keep this out of the interface of CenteringGroup. + class CenteringGroupCreationHelper + { + public: + static CenteringGroup::CenteringType getCenteringType(const std::string ¢eringSymbol); + + static std::vector getSymmetryOperations(CenteringGroup::CenteringType centeringType); + + protected: + CenteringGroupCreationHelper() { } + ~CenteringGroupCreationHelper() { } + + static std::vector getPrimitive(); + static std::vector getBodyCentered(); + static std::vector getACentered(); + static std::vector getBCentered(); + static std::vector getCCentered(); + static std::vector getFCentered(); + static std::vector getRobvCentered(); + static std::vector getRrevCentered(); + + static std::map m_centeringSymbolMap; + }; + +}; + +typedef boost::shared_ptr CenteringGroup_sptr; +typedef boost::shared_ptr CenteringGroup_const_sptr; + + + + + +} // namespace Geometry +} // namespace Mantid + +#endif /* MANTID_GEOMETRY_CENTERINGGROUP_H_ */ diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp new file mode 100644 index 000000000000..912f3a496d47 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp @@ -0,0 +1,134 @@ +#include "MantidGeometry/Crystal/CenteringGroup.h" +#include "MantidGeometry/Crystal/SymmetryOperationFactory.h" + +#include + +namespace Mantid +{ +namespace Geometry +{ + +/// String-based constructor which accepts centering symbols such as P, I or F. +CenteringGroup::CenteringGroup(const std::string ¢eringSymbol) : + Group(), + m_type(), + m_symbol() +{ + m_type = CenteringGroupCreationHelper::getCenteringType(centeringSymbol); + m_symbol = centeringSymbol.substr(0, 1); + + setSymmetryOperations(CenteringGroupCreationHelper::getSymmetryOperations(m_type)); +} + +/// Returns the centering type of the group (distinguishes between Rhombohedral obverse and reverse). +CenteringGroup::CenteringType CenteringGroup::getType() const +{ + return m_type; +} + +/// Returns the centering symbol, does not distinguish between Rhombohedral obverse and reverse. +std::string CenteringGroup::getSymbol() const +{ + return m_symbol; +} + +/// Map between string symbols and enum-values for centering type. +std::map CenteringGroup::CenteringGroupCreationHelper::m_centeringSymbolMap = + boost::assign::map_list_of + ("P", CenteringGroup::P) + ("I", CenteringGroup::I) + ("A", CenteringGroup::A) + ("B", CenteringGroup::B) + ("C", CenteringGroup::C) + ("F", CenteringGroup::F) + ("R", CenteringGroup::Robv) + ("Robv", CenteringGroup::Robv) + ("Rrev", CenteringGroup::Rrev); + +/// Returns centering type enum value if centering symbol exists, throws std::invalid_argument exception otherwise. +CenteringGroup::CenteringType CenteringGroup::CenteringGroupCreationHelper::getCenteringType(const std::string ¢eringSymbol) +{ + auto it = m_centeringSymbolMap.find(centeringSymbol); + + if(it == m_centeringSymbolMap.end()) { + throw std::invalid_argument("Centering does not exist: " + centeringSymbol); + } + + return m_centeringSymbolMap[centeringSymbol]; +} + +/// Returns a vector of symmetry operations for the given centering type or throws std::invalid_argument if an invalid value is supplied. +std::vector CenteringGroup::CenteringGroupCreationHelper::getSymmetryOperations(CenteringGroup::CenteringType centeringType) +{ + switch(centeringType) { + case CenteringGroup::P: + return getPrimitive(); + case CenteringGroup::I: + return getBodyCentered(); + case CenteringGroup::A: + return getACentered(); + case CenteringGroup::B: + return getBCentered(); + case CenteringGroup::C: + return getCCentered(); + case CenteringGroup::F: + return getFCentered(); + case CenteringGroup::Robv: + return getRobvCentered(); + case CenteringGroup::Rrev: + return getRrevCentered(); + default: + throw std::invalid_argument("Unknown centering type."); + } +} + +/// Returns symmetry operations for P-centering. +std::vector CenteringGroup::CenteringGroup::CenteringGroupCreationHelper::getPrimitive() +{ + return SymmetryOperationFactory::Instance().createSymOps("x,y,z"); +} + +/// Returns symmetry operations for I-centering. +std::vector CenteringGroup::CenteringGroupCreationHelper::getBodyCentered() +{ + return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y+1/2,z+1/2"); +} + +/// Returns symmetry operations for A-centering. +std::vector CenteringGroup::CenteringGroupCreationHelper::getACentered() +{ + return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x,y+1/2,z+1/2"); +} + +/// Returns symmetry operations for B-centering. +std::vector CenteringGroup::CenteringGroupCreationHelper::getBCentered() +{ + return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y,z+1/2"); +} + +/// Returns symmetry operations for C-centering. +std::vector CenteringGroup::CenteringGroupCreationHelper::getCCentered() +{ + return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y+1/2,z"); +} + +/// Returns symmetry operations for F-centering. +std::vector CenteringGroup::CenteringGroupCreationHelper::getFCentered() +{ + return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x,y+1/2,z+1/2; x+1/2,y,z+1/2; x+1/2,y+1/2,z"); +} + +/// Returns symmetry operations for R-centering, obverse setting. +std::vector CenteringGroup::CenteringGroupCreationHelper::getRobvCentered() +{ + return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/3,y+2/3,z+2/3; x+2/3,y+1/3,z+1/3"); +} + +/// Returns symmetry operations for R-centering, reverse setting. +std::vector CenteringGroup::CenteringGroupCreationHelper::getRrevCentered() +{ + return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/3,y+2/3,z+1/3; x+2/3,y+1/3,z+2/3"); +} + +} // namespace Geometry +} // namespace Mantid diff --git a/Code/Mantid/Framework/Geometry/test/CenteringGroupTest.h b/Code/Mantid/Framework/Geometry/test/CenteringGroupTest.h new file mode 100644 index 000000000000..092ce9dcc004 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/test/CenteringGroupTest.h @@ -0,0 +1,65 @@ +#ifndef MANTID_GEOMETRY_CENTERINGGROUPTEST_H_ +#define MANTID_GEOMETRY_CENTERINGGROUPTEST_H_ + +#include + +#include "MantidGeometry/Crystal/CenteringGroup.h" +#include "MantidGeometry/Crystal/SymmetryOperationFactory.h" + +using namespace Mantid::Geometry; + +class CenteringGroupTest : 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 CenteringGroupTest *createSuite() { return new CenteringGroupTest(); } + static void destroySuite( CenteringGroupTest *suite ) { delete suite; } + + void testValidCenterings() + { + testCenteringGroup("P", CenteringGroup::P, "P", SymmetryOperationFactory::Instance().createSymOps("x,y,z")); + testCenteringGroup("I", CenteringGroup::I, "I", SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y+1/2,z+1/2")); + testCenteringGroup("A", CenteringGroup::A, "A", SymmetryOperationFactory::Instance().createSymOps("x,y,z; x,y+1/2,z+1/2")); + testCenteringGroup("B", CenteringGroup::B, "B", SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y,z+1/2")); + testCenteringGroup("C", CenteringGroup::C, "C", SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y+1/2,z")); + testCenteringGroup("F", CenteringGroup::F, "F", SymmetryOperationFactory::Instance().createSymOps("x,y,z; x,y+1/2,z+1/2; x+1/2,y,z+1/2; x+1/2,y+1/2,z")); + testCenteringGroup("R", CenteringGroup::Robv, "R", SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/3,y+2/3,z+2/3; x+2/3,y+1/3,z+1/3")); + testCenteringGroup("Robv", CenteringGroup::Robv, "R", SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/3,y+2/3,z+2/3; x+2/3,y+1/3,z+1/3")); + testCenteringGroup("Rrev", CenteringGroup::Rrev, "R", SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/3,y+2/3,z+1/3; x+2/3,y+1/3,z+2/3")); + } + + void testInvalidCentering() + { + TS_ASSERT_THROWS(CenteringGroup group("G"), std::invalid_argument); + TS_ASSERT_THROWS(CenteringGroup group("f"), std::invalid_argument); + } + +private: + void testCenteringGroup(const std::string &symbol, CenteringGroup::CenteringType expectedCenteringType, const std::string &expectedSymbol, const std::vector &expectedOperations) + { + TSM_ASSERT_THROWS_NOTHING("Exception when trying to create " + symbol, CenteringGroup rawGroup(symbol)); + + Group_const_sptr group = GroupFactory::create(symbol); + std::vector ops = group->getSymmetryOperations(); + TSM_ASSERT_EQUALS("Unexpected number of operations for " + symbol, ops.size(), expectedOperations.size()); + + for(auto it = expectedOperations.begin(); it != expectedOperations.end(); ++it) { + TSM_ASSERT("Operation " + (*it).identifier() + " not found in " + symbol, symOpExistsInCollection(*it, ops)); + } + + CenteringGroup_const_sptr centeringGroup = boost::dynamic_pointer_cast(group); + TSM_ASSERT("Could not cast to pointer in " + symbol, centeringGroup); + TSM_ASSERT_EQUALS("CenteringType did not match for " + symbol, centeringGroup->getType(), expectedCenteringType); + TSM_ASSERT_EQUALS("CenteringString did not match for " + symbol, centeringGroup->getSymbol(), expectedSymbol); + } + + + bool symOpExistsInCollection(const SymmetryOperation &op, const std::vector &collection) + { + return std::find(collection.begin(), collection.end(), op) != collection.end(); + } +}; + + +#endif /* MANTID_GEOMETRY_CENTERINGGROUPTEST_H_ */ From d6d8875daf194b89e0c40c31d33e32e532af9c34 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Wed, 8 Oct 2014 09:39:12 +0200 Subject: [PATCH 09/48] Refs #10281. Moved helper class out of CenteringGroup --- .../MantidGeometry/Crystal/CenteringGroup.h | 48 +++++++++---------- .../Geometry/src/Crystal/CenteringGroup.cpp | 22 ++++----- 2 files changed, 33 insertions(+), 37 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h index ba6aabef1b5e..7379283f3919 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h @@ -54,38 +54,34 @@ class MANTID_GEOMETRY_DLL CenteringGroup : public Group protected: CenteringType m_type; std::string m_symbol; - -private: - /// Private helper class to keep this out of the interface of CenteringGroup. - class CenteringGroupCreationHelper - { - public: - static CenteringGroup::CenteringType getCenteringType(const std::string ¢eringSymbol); - - static std::vector getSymmetryOperations(CenteringGroup::CenteringType centeringType); - - protected: - CenteringGroupCreationHelper() { } - ~CenteringGroupCreationHelper() { } - - static std::vector getPrimitive(); - static std::vector getBodyCentered(); - static std::vector getACentered(); - static std::vector getBCentered(); - static std::vector getCCentered(); - static std::vector getFCentered(); - static std::vector getRobvCentered(); - static std::vector getRrevCentered(); - - static std::map m_centeringSymbolMap; - }; - }; typedef boost::shared_ptr CenteringGroup_sptr; typedef boost::shared_ptr CenteringGroup_const_sptr; +/// Helper class to keep this out of the interface of CenteringGroup. +class CenteringGroupCreationHelper +{ +public: + static CenteringGroup::CenteringType getCenteringType(const std::string ¢eringSymbol); + static std::vector getSymmetryOperations(CenteringGroup::CenteringType centeringType); + +protected: + CenteringGroupCreationHelper() { } + ~CenteringGroupCreationHelper() { } + + static std::vector getPrimitive(); + static std::vector getBodyCentered(); + static std::vector getACentered(); + static std::vector getBCentered(); + static std::vector getCCentered(); + static std::vector getFCentered(); + static std::vector getRobvCentered(); + static std::vector getRrevCentered(); + + static std::map m_centeringSymbolMap; +}; diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp index 912f3a496d47..8fd376cf9752 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp @@ -33,7 +33,7 @@ std::string CenteringGroup::getSymbol() const } /// Map between string symbols and enum-values for centering type. -std::map CenteringGroup::CenteringGroupCreationHelper::m_centeringSymbolMap = +std::map CenteringGroupCreationHelper::m_centeringSymbolMap = boost::assign::map_list_of ("P", CenteringGroup::P) ("I", CenteringGroup::I) @@ -46,7 +46,7 @@ std::map CenteringGroup::CenteringGr ("Rrev", CenteringGroup::Rrev); /// Returns centering type enum value if centering symbol exists, throws std::invalid_argument exception otherwise. -CenteringGroup::CenteringType CenteringGroup::CenteringGroupCreationHelper::getCenteringType(const std::string ¢eringSymbol) +CenteringGroup::CenteringType CenteringGroupCreationHelper::getCenteringType(const std::string ¢eringSymbol) { auto it = m_centeringSymbolMap.find(centeringSymbol); @@ -58,7 +58,7 @@ CenteringGroup::CenteringType CenteringGroup::CenteringGroupCreationHelper::getC } /// Returns a vector of symmetry operations for the given centering type or throws std::invalid_argument if an invalid value is supplied. -std::vector CenteringGroup::CenteringGroupCreationHelper::getSymmetryOperations(CenteringGroup::CenteringType centeringType) +std::vector CenteringGroupCreationHelper::getSymmetryOperations(CenteringGroup::CenteringType centeringType) { switch(centeringType) { case CenteringGroup::P: @@ -83,49 +83,49 @@ std::vector CenteringGroup::CenteringGroupCreationHelper::get } /// Returns symmetry operations for P-centering. -std::vector CenteringGroup::CenteringGroup::CenteringGroupCreationHelper::getPrimitive() +std::vector CenteringGroupCreationHelper::getPrimitive() { return SymmetryOperationFactory::Instance().createSymOps("x,y,z"); } /// Returns symmetry operations for I-centering. -std::vector CenteringGroup::CenteringGroupCreationHelper::getBodyCentered() +std::vector CenteringGroupCreationHelper::getBodyCentered() { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y+1/2,z+1/2"); } /// Returns symmetry operations for A-centering. -std::vector CenteringGroup::CenteringGroupCreationHelper::getACentered() +std::vector CenteringGroupCreationHelper::getACentered() { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x,y+1/2,z+1/2"); } /// Returns symmetry operations for B-centering. -std::vector CenteringGroup::CenteringGroupCreationHelper::getBCentered() +std::vector CenteringGroupCreationHelper::getBCentered() { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y,z+1/2"); } /// Returns symmetry operations for C-centering. -std::vector CenteringGroup::CenteringGroupCreationHelper::getCCentered() +std::vector CenteringGroupCreationHelper::getCCentered() { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y+1/2,z"); } /// Returns symmetry operations for F-centering. -std::vector CenteringGroup::CenteringGroupCreationHelper::getFCentered() +std::vector CenteringGroupCreationHelper::getFCentered() { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x,y+1/2,z+1/2; x+1/2,y,z+1/2; x+1/2,y+1/2,z"); } /// Returns symmetry operations for R-centering, obverse setting. -std::vector CenteringGroup::CenteringGroupCreationHelper::getRobvCentered() +std::vector CenteringGroupCreationHelper::getRobvCentered() { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/3,y+2/3,z+2/3; x+2/3,y+1/3,z+1/3"); } /// Returns symmetry operations for R-centering, reverse setting. -std::vector CenteringGroup::CenteringGroupCreationHelper::getRrevCentered() +std::vector CenteringGroupCreationHelper::getRrevCentered() { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/3,y+2/3,z+1/3; x+2/3,y+1/3,z+2/3"); } From 417b63577b95c504fd4b33f0733b5b9e4279645b Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Wed, 8 Oct 2014 14:49:55 +0200 Subject: [PATCH 10/48] Re #10281. Removing static members from SymmetryOperationSymbolParser --- .../Crystal/SymmetryOperationSymbolParser.h | 5 ++--- .../Crystal/SymmetryOperationSymbolParser.cpp | 16 +++++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SymmetryOperationSymbolParser.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SymmetryOperationSymbolParser.h index 800b6fbbde22..e4b4f24cece0 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SymmetryOperationSymbolParser.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SymmetryOperationSymbolParser.h @@ -91,9 +91,8 @@ namespace Geometry static bool isValidMatrixRow(const std::vector &matrixRow); - static boost::regex m_tokenRegex; - static boost::regex m_matrixRowRegex; - static boost::regex m_vectorComponentRegex; + static bool regexMembersInitialized(); + static void initializeRegexMembers(); }; diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/SymmetryOperationSymbolParser.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/SymmetryOperationSymbolParser.cpp index cf9df83eaf4d..783a117bb8f4 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/SymmetryOperationSymbolParser.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/SymmetryOperationSymbolParser.cpp @@ -9,10 +9,6 @@ namespace Mantid namespace Geometry { -boost::regex SymmetryOperationSymbolParser::m_tokenRegex = boost::regex("[+\\-]?((x|y|z)|(\\d/\\d))", boost::regex::icase); -boost::regex SymmetryOperationSymbolParser::m_matrixRowRegex = boost::regex("^[+\\-]?(x|y|z)", boost::regex::icase); -boost::regex SymmetryOperationSymbolParser::m_vectorComponentRegex = boost::regex("^[+\\-]?(\\d/\\d)", boost::regex::icase); - /// Default constructor SymmetryOperationSymbolParser::SymmetryOperationSymbolParser() { @@ -157,17 +153,23 @@ std::pair, RationalNumber> SymmetryOperationSymbolParser::parse size_t totalMatchedLength = 0; + // Regular expressions for different token types + boost::regex tokenRegex("[+\\-]?((x|y|z)|(\\d/\\d))", boost::regex::icase); + boost::regex matrixRowRegex("^[+\\-]?(x|y|z)", boost::regex::icase); + boost::regex vectorComponentRegex("^[+\\-]?(\\d/\\d)", boost::regex::icase); + + // Check how many tokens this string is composed of and iterate through them - boost::sregex_iterator iter(component.begin(), component.end(), m_tokenRegex); + boost::sregex_iterator iter(component.begin(), component.end(), tokenRegex); boost::sregex_iterator end; for(; iter != end; ++iter) { std::string currentString = iter->str(); totalMatchedLength += currentString.size(); // Try to handle the current token as either a matrix row (x, y, z) or a vector component (a/b) - if(boost::regex_match(currentString, m_matrixRowRegex)) { + if(boost::regex_match(currentString, matrixRowRegex)) { processMatrixRowToken(currentString, matrixRow); - } else if(boost::regex_match(currentString, m_vectorComponentRegex)) { + } else if(boost::regex_match(currentString, vectorComponentRegex)) { processVectorComponentToken(currentString, vectorComponent); } else { throw std::runtime_error("Failed to parse input: " + component); From 87910525cd8746053db901100a058c3999397ca9 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Wed, 8 Oct 2014 14:53:36 +0200 Subject: [PATCH 11/48] Refs #10281. Added string constructor to Group. --- .../inc/MantidGeometry/Crystal/Group.h | 1 + .../Framework/Geometry/src/Crystal/Group.cpp | 20 +++++++++++++++++++ .../Framework/Geometry/test/GroupTest.h | 18 +++++++++++++++++ 3 files changed, 39 insertions(+) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h index 725bc359922b..ca41b912c14e 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h @@ -46,6 +46,7 @@ class MANTID_GEOMETRY_DLL Group { public: Group(); + Group(const std::string &symmetryOperationString); Group(const std::vector &symmetryOperations); Group(const Group &other); Group &operator =(const Group &other); diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp index cc8e28e41a3a..85c45b384543 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp @@ -1,4 +1,5 @@ #include "MantidGeometry/Crystal/Group.h" +#include "MantidGeometry/Crystal/SymmetryOperationFactory.h" #include namespace Mantid @@ -15,6 +16,13 @@ Group::Group() : setSymmetryOperations(operation); } +Group::Group(const std::string &symmetryOperationString) : + m_allOperations(), + m_operationSet() +{ + setSymmetryOperations(SymmetryOperationFactory::Instance().createSymOps(symmetryOperationString)); +} + Group::Group(const std::vector &symmetryOperations) : m_allOperations(), m_operationSet() @@ -94,17 +102,29 @@ void Group::setSymmetryOperations(const std::vector &symmetry Group_const_sptr operator *(const Group_const_sptr &lhs, const Group_const_sptr &rhs) { + if(!lhs || !rhs) { + throw std::invalid_argument("One of the operands is null. Aborting."); + } + return boost::make_shared((*lhs) * (*rhs)); } std::vector operator *(const Group_const_sptr &lhs, const Kernel::V3D &rhs) { + if(!lhs) { + throw std::invalid_argument("Cannot use null pointer for multiplication."); + } + return (*lhs) * rhs; } bool operator ==(const Group_const_sptr &lhs, const Group_const_sptr &rhs) { + if(!lhs || !rhs) { + throw std::invalid_argument("One of the operands is null. Aborting."); + } + return (*lhs) == (*rhs); } diff --git a/Code/Mantid/Framework/Geometry/test/GroupTest.h b/Code/Mantid/Framework/Geometry/test/GroupTest.h index 0aefe9165e8d..75cfb8a0a1fa 100644 --- a/Code/Mantid/Framework/Geometry/test/GroupTest.h +++ b/Code/Mantid/Framework/Geometry/test/GroupTest.h @@ -24,6 +24,12 @@ class GroupTest : public CxxTest::TestSuite TS_ASSERT(group.getSymmetryOperations().front().isIdentity()); } + void testStringConstructor() + { + Group group("x,y,z; -x,-y,-z"); + + TS_ASSERT_EQUALS(group.order(), 2); + } void testConstructor() { @@ -168,6 +174,18 @@ class GroupTest : public CxxTest::TestSuite // Check that it is found in the list of symmetry operations of the new group TS_ASSERT_DIFFERS(std::find(opsOfThree.begin(), opsOfThree.end(), mirrorY), opsOfThree.end()); + + // Make sure that null-pointer do not work + Group_const_sptr null; + + TS_ASSERT_THROWS(null * null, std::invalid_argument); + TS_ASSERT_THROWS(null == null, std::invalid_argument); + TS_ASSERT_THROWS(null != null, std::invalid_argument); + TS_ASSERT_THROWS(three * null, std::invalid_argument); + TS_ASSERT_THROWS(null * three, std::invalid_argument); + + Mantid::Kernel::V3D coords(0.4, 0.3, 0.1); + TS_ASSERT_THROWS(null * coords, std::invalid_argument); } From e293a31cfb1beaf65250736d85f112c4f323127c Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Wed, 8 Oct 2014 14:54:25 +0200 Subject: [PATCH 12/48] Refs #10281. Added ProductGroup class --- Code/Mantid/Framework/Geometry/CMakeLists.txt | 3 + .../inc/MantidGeometry/Crystal/ProductGroup.h | 70 +++++++++++++++ .../Geometry/src/Crystal/ProductGroup.cpp | 58 ++++++++++++ .../Geometry/test/ProductGroupTest.h | 89 +++++++++++++++++++ 4 files changed, 220 insertions(+) create mode 100644 Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductGroup.h create mode 100644 Code/Mantid/Framework/Geometry/src/Crystal/ProductGroup.cpp create mode 100644 Code/Mantid/Framework/Geometry/test/ProductGroupTest.h diff --git a/Code/Mantid/Framework/Geometry/CMakeLists.txt b/Code/Mantid/Framework/Geometry/CMakeLists.txt index 2f2a79062784..d8b0c3ea1916 100644 --- a/Code/Mantid/Framework/Geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/Geometry/CMakeLists.txt @@ -10,6 +10,7 @@ set ( SRC_FILES src/Crystal/OrientedLattice.cpp src/Crystal/PointGroup.cpp src/Crystal/PointGroupFactory.cpp + src/Crystal/ProductGroup.cpp src/Crystal/ReducedCell.cpp src/Crystal/ReflectionCondition.cpp src/Crystal/ScalarUtils.cpp @@ -118,6 +119,7 @@ set ( INC_FILES inc/MantidGeometry/Crystal/OrientedLattice.h inc/MantidGeometry/Crystal/PointGroup.h inc/MantidGeometry/Crystal/PointGroupFactory.h + inc/MantidGeometry/Crystal/ProductGroup.h inc/MantidGeometry/Crystal/ReducedCell.h inc/MantidGeometry/Crystal/ReflectionCondition.h inc/MantidGeometry/Crystal/ScalarUtils.h @@ -269,6 +271,7 @@ set ( TEST_FILES PointGroupFactoryTest.h PointGroupTest.h PolygonEdgeTest.h + ProductGroupTest.h QuadrilateralTest.h RectangularDetectorPixelTest.h RectangularDetectorTest.h diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductGroup.h new file mode 100644 index 000000000000..338749341ba4 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductGroup.h @@ -0,0 +1,70 @@ +#ifndef MANTID_GEOMETRY_PRODUCTGROUP_H_ +#define MANTID_GEOMETRY_PRODUCTGROUP_H_ + +#include "MantidGeometry/DllConfig.h" +#include "MantidGeometry/Crystal/Group.h" + +namespace Mantid +{ +namespace Geometry +{ + +/** ProductGroup : + + ProductGroup objects can be constructed using a string which contains + a series of symmetry operations S_i, separated by semicolons: + + x,y,z; -x,-y,-z; x,-y,z + + For each symmetry operation, a CyclicGroup G_i object is created. These groups + are then multiplied to form a ProductGroup: + + P = G_1 * G_2 * G_3 * ... * G_n + + This way, groups with many symmetry operations can be generated from a small + set of generators. This is for example described in [1]. + + [1] Shmueli, U. Acta Crystallogr. A 40, 559–567 (1984). + http://dx.doi.org/10.1107/S0108767384001161 + + @author Michael Wedel, Paul Scherrer Institut - SINQ + @date 08/10/2014 + + Copyright © 2014 PSI-MSS + + 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 MANTID_GEOMETRY_DLL ProductGroup : public Group +{ +public: + ProductGroup(const std::string &generators); + ProductGroup(const std::vector &factorGroups); + virtual ~ProductGroup() { } + +protected: + Group_const_sptr getGeneratedGroup(const std::string &generators) const; + std::vector getFactorGroups(const std::vector &symmetryOperations) const; + Group_const_sptr getProductGroup(const std::vector &factorGroups) const; +}; + + +} // namespace Geometry +} // namespace Mantid + +#endif /* MANTID_GEOMETRY_PRODUCTGROUP_H_ */ diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/ProductGroup.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/ProductGroup.cpp new file mode 100644 index 000000000000..f941a2fde5a6 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/src/Crystal/ProductGroup.cpp @@ -0,0 +1,58 @@ +#include "MantidGeometry/Crystal/ProductGroup.h" + +#include "MantidGeometry/Crystal/SymmetryOperationFactory.h" +#include "MantidGeometry/Crystal/CyclicGroup.h" + +namespace Mantid +{ +namespace Geometry +{ + +/// String constructor with semicolon-separated symmetry operations +ProductGroup::ProductGroup(const std::string &generators) : + Group(*(getGeneratedGroup(generators))) +{ +} + +/// Constructor which directly takes a list of factor groups to form the product +ProductGroup::ProductGroup(const std::vector &factorGroups) : + Group(*(getProductGroup(factorGroups))) +{ +} + +/// Generates symmetry operations from the string, creates a CyclicGroup from each operation and multiplies them to form a factor group. +Group_const_sptr ProductGroup::getGeneratedGroup(const std::string &generators) const +{ + std::vector operations = SymmetryOperationFactory::Instance().createSymOps(generators); + std::vector factorGroups = getFactorGroups(operations); + + return getProductGroup(factorGroups); +} + +/// Returns a vector of cyclic groups for the given vector of symmetry operations +std::vector ProductGroup::getFactorGroups(const std::vector &symmetryOperations) const +{ + std::vector groups; + + for(auto it = symmetryOperations.begin(); it != symmetryOperations.end(); ++it) { + groups.push_back(GroupFactory::create((*it).identifier())); + } + + return groups; +} + +/// Multiplies all supplied groups and returns the result +Group_const_sptr ProductGroup::getProductGroup(const std::vector &factorGroups) const +{ + Group_const_sptr productGroup = boost::make_shared(*(factorGroups.front())); + + for(size_t i = 1; i < factorGroups.size(); ++i) { + productGroup = productGroup * factorGroups[i]; + } + + return productGroup; +} + + +} // namespace Geometry +} // namespace Mantid diff --git a/Code/Mantid/Framework/Geometry/test/ProductGroupTest.h b/Code/Mantid/Framework/Geometry/test/ProductGroupTest.h new file mode 100644 index 000000000000..a379536eef51 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/test/ProductGroupTest.h @@ -0,0 +1,89 @@ +#ifndef MANTID_GEOMETRY_PRODUCTGROUPTEST_H_ +#define MANTID_GEOMETRY_PRODUCTGROUPTEST_H_ + +#include + +#include "MantidGeometry/Crystal/ProductGroup.h" +#include "MantidGeometry/Crystal/CyclicGroup.h" +#include "MantidGeometry/Crystal/SymmetryOperationFactory.h" + +using namespace Mantid::Geometry; + +class ProductGroupTest : 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 ProductGroupTest *createSuite() { return new ProductGroupTest(); } + static void destroySuite( ProductGroupTest *suite ) { delete suite; } + + + void testStringConstructor() + { + TS_ASSERT_THROWS_NOTHING(ProductGroup group("x,y,z")); + + TS_ASSERT_THROWS_ANYTHING(ProductGroup group("x,y,z; doesnt work")); + TS_ASSERT_THROWS_ANYTHING(ProductGroup group("x,y,z| z,x,y")); + } + + void testVectorConstructor() + { + std::vector groups; + groups.push_back(GroupFactory::create("-x,-y,-z")); + groups.push_back(GroupFactory::create("x,-y,z")); + + TS_ASSERT_THROWS_NOTHING(ProductGroup group(groups)); + + Group_const_sptr null; + groups.push_back(null); + + TS_ASSERT_THROWS_ANYTHING(ProductGroup group(groups)); + } + + void testGetGeneratedGroup() + { + TestableProductGroup group; + + Group_const_sptr generatedGroup = group.getGeneratedGroup("-x,-y,-z; x,-y,z"); + + // Inversion generates 1, -1; Mirror 1, m [010] -> results in 1, -1, m [010], 2 [010] + TS_ASSERT_EQUALS(generatedGroup->order(), 4); + } + + void testGetFactorGroups() + { + TestableProductGroup group; + + std::vector symmetryOperations = SymmetryOperationFactory::Instance().createSymOps("-x,-y,-z; x,-y,z"); + std::vector generatedGroup = group.getFactorGroups(symmetryOperations); + // one group for each symmetry operation + TS_ASSERT_EQUALS(generatedGroup.size(), 2); + } + + void testGetProductGroup() + { + TestableProductGroup group; + + std::vector groups; + groups.push_back(GroupFactory::create("-x,-y,-z")); + groups.push_back(GroupFactory::create("x,-y,z")); + + Group_const_sptr productGroup = group.getProductGroup(groups); + + TS_ASSERT_EQUALS(productGroup->order(), 4); + } + +private: + class TestableProductGroup : public ProductGroup + { + friend class ProductGroupTest; + public: + TestableProductGroup() : + ProductGroup("x,y,z") { } + ~TestableProductGroup() { } + }; + +}; + + +#endif /* MANTID_GEOMETRY_PRODUCTGROUPTEST_H_ */ From 4d1072a27942917c17c70128c0b71a416de36e33 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Wed, 8 Oct 2014 15:05:57 +0200 Subject: [PATCH 13/48] Refs #10281. Added SpaceGroup class. --- Code/Mantid/Framework/Geometry/CMakeLists.txt | 3 + .../inc/MantidGeometry/Crystal/SpaceGroup.h | 96 +++++++++++++++++++ .../Geometry/src/Crystal/SpaceGroup.cpp | 58 +++++++++++ .../Framework/Geometry/test/SpaceGroupTest.h | 79 +++++++++++++++ 4 files changed, 236 insertions(+) create mode 100644 Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroup.h create mode 100644 Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroup.cpp create mode 100644 Code/Mantid/Framework/Geometry/test/SpaceGroupTest.h diff --git a/Code/Mantid/Framework/Geometry/CMakeLists.txt b/Code/Mantid/Framework/Geometry/CMakeLists.txt index d8b0c3ea1916..1d7f0eab23ed 100644 --- a/Code/Mantid/Framework/Geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/Geometry/CMakeLists.txt @@ -14,6 +14,7 @@ set ( SRC_FILES src/Crystal/ReducedCell.cpp src/Crystal/ReflectionCondition.cpp src/Crystal/ScalarUtils.cpp + src/Crystal/SpaceGroup.cpp src/Crystal/SymmetryOperation.cpp src/Crystal/SymmetryOperationFactory.cpp src/Crystal/SymmetryOperationSymbolParser.cpp @@ -123,6 +124,7 @@ set ( INC_FILES inc/MantidGeometry/Crystal/ReducedCell.h inc/MantidGeometry/Crystal/ReflectionCondition.h inc/MantidGeometry/Crystal/ScalarUtils.h + inc/MantidGeometry/Crystal/SpaceGroup.h inc/MantidGeometry/Crystal/SymmetryOperation.h inc/MantidGeometry/Crystal/SymmetryOperationFactory.h inc/MantidGeometry/Crystal/SymmetryOperationSymbolParser.h @@ -288,6 +290,7 @@ set ( TEST_FILES RulesUnionTest.h ScalarUtilsTest.h ShapeFactoryTest.h + SpaceGroupTest.h SphereTest.h SurfaceFactoryTest.h SurfaceTest.h diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroup.h new file mode 100644 index 000000000000..3b0887bdcd0c --- /dev/null +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroup.h @@ -0,0 +1,96 @@ +#ifndef MANTID_GEOMETRY_SPACEGROUP_H_ +#define MANTID_GEOMETRY_SPACEGROUP_H_ + +#include "MantidGeometry/DllConfig.h" +#include "MantidGeometry/Crystal/Group.h" +#include "MantidGeometry/Crystal/SymmetryOperation.h" +#include "MantidKernel/V3D.h" + +#include + +namespace Mantid +{ +namespace Geometry +{ + +/** SpaceGroup : + + A class for representing space groups, inheriting from Group. + + SpaceGroup-objects represent a space group, which is a set + of symmetry operations. Along with storing the operations themselves, + which is realized through inheriting from Group, SpaceGroup also + stores a number (space group number according to the International + Tables for Crystallography A) and a Hermann-Mauguin-symbol. + + SpaceGroup may for example be used to generate all equivalent positions + within the unit cell: + + SpaceGroup_const_sptr someGroup; + + V3D position(0.13, 0.54, 0.38); + std::vector equivalents = someGroup->getEquivalentPositions(position); + + The class should not be instantiated directly, see SpaceGroupFactory instead. + + @author Michael Wedel, Paul Scherrer Institut - SINQ + @date 03/10/2014 + + Copyright © 2014 PSI-MSS + + 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 MANTID_GEOMETRY_DLL SpaceGroup : public Group +{ +public: + SpaceGroup(size_t itNumber, const std::string &hmSymbol, const Group &group); + + SpaceGroup(const SpaceGroup &other); + SpaceGroup &operator =(const SpaceGroup &other); + + virtual ~SpaceGroup() { } + + size_t number() const; + std::string hmSymbol() const; + + template + std::vector getEquivalentPositions(const T &position) const + { + const std::vector &symmetryOperations = getSymmetryOperations(); + + std::set equivalents; + for(auto it = symmetryOperations.begin(); it != symmetryOperations.end(); ++it) { + equivalents.insert(Geometry::getWrappedVector((*it) * position)); + } + + return std::vector(equivalents.begin(), equivalents.end()); + } + +protected: + size_t m_number; + std::string m_hmSymbol; +}; + +typedef boost::shared_ptr SpaceGroup_sptr; +typedef boost::shared_ptr SpaceGroup_const_sptr; + +} // namespace Geometry +} // namespace Mantid + +#endif /* MANTID_GEOMETRY_SPACEGROUP_H_ */ diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroup.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroup.cpp new file mode 100644 index 000000000000..62ecbfd55647 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroup.cpp @@ -0,0 +1,58 @@ +#include "MantidGeometry/Crystal/SpaceGroup.h" + +namespace Mantid +{ +namespace Geometry +{ + +/** + * Constructor + * + * This constructor creates a space group with the symmetry operations contained + * in the Group-parameter and assigns the given number and symbol. + * + * @param itNumber :: Space group number according to International Tables for Crystallography A + * @param hmSymbol :: Herman-Mauguin symbol for the space group + * @param group :: Group that contains all symmetry operations (including centering). + */ +SpaceGroup::SpaceGroup(size_t itNumber, const std::string &hmSymbol, const Group &group) : + Group(group), + m_number(itNumber), + m_hmSymbol(hmSymbol) +{ +} + +/// Copy constructor +SpaceGroup::SpaceGroup(const SpaceGroup &other) : + Group(other), + m_number(other.m_number), + m_hmSymbol(other.m_hmSymbol) +{ + +} + +/// Assignment operator, utilizes Group's assignment operator +SpaceGroup &SpaceGroup::operator =(const SpaceGroup &other) +{ + Group::operator =(other); + + m_number = other.m_number; + m_hmSymbol = other.m_hmSymbol; + + return *this; +} + +/// Returns the stored space group number +size_t SpaceGroup::number() const +{ + return m_number; +} + +/// Returns the stored Hermann-Mauguin symbol +std::string SpaceGroup::hmSymbol() const +{ + return m_hmSymbol; +} + +} // namespace Geometry +} // namespace Mantid diff --git a/Code/Mantid/Framework/Geometry/test/SpaceGroupTest.h b/Code/Mantid/Framework/Geometry/test/SpaceGroupTest.h new file mode 100644 index 000000000000..861a5498309c --- /dev/null +++ b/Code/Mantid/Framework/Geometry/test/SpaceGroupTest.h @@ -0,0 +1,79 @@ +#ifndef MANTID_GEOMETRY_SPACEGROUPTEST_H_ +#define MANTID_GEOMETRY_SPACEGROUPTEST_H_ + +#include + +#include "MantidGeometry/Crystal/SpaceGroup.h" +#include "MantidGeometry/Crystal/SymmetryOperationFactory.h" +#include "MantidGeometry/Crystal/CyclicGroup.h" +#include "MantidKernel/V3D.h" + +#include + +using namespace Mantid::Geometry; +using Mantid::Kernel::V3D; + +class SpaceGroupTest : 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 SpaceGroupTest *createSuite() { return new SpaceGroupTest(); } + static void destroySuite( SpaceGroupTest *suite ) { delete suite; } + + + void testConstruction() + { + Group_const_sptr inversion = GroupFactory::create("-x,-y,-z"); + SpaceGroup p1bar(2, "P-1", *inversion); + + TS_ASSERT_EQUALS(p1bar.number(), 2); + TS_ASSERT_EQUALS(p1bar.hmSymbol(), "P-1"); + TS_ASSERT_EQUALS(p1bar.order(), 2); + TS_ASSERT_EQUALS(p1bar.getSymmetryOperations().size(), 2); + } + + void testNumber() + { + TestableSpaceGroup empty; + TS_ASSERT_EQUALS(empty.number(), 0); + + empty.m_number = 2; + TS_ASSERT_EQUALS(empty.number(), 2); + } + + void testSymbol() + { + TestableSpaceGroup empty; + TS_ASSERT_EQUALS(empty.hmSymbol(), ""); + + empty.m_hmSymbol = "Test"; + TS_ASSERT_EQUALS(empty.hmSymbol(), "Test"); + } + + void testAssignmentOperator() + { + Group_const_sptr inversion = GroupFactory::create("-x,-y,-z"); + SpaceGroup p1bar(2, "P-1", *inversion); + + SpaceGroup other = p1bar; + + TS_ASSERT_EQUALS(other.number(), p1bar.number()); + TS_ASSERT_EQUALS(other.hmSymbol(), p1bar.hmSymbol()); + TS_ASSERT_EQUALS(other.order(), p1bar.order()); + } + +private: + class TestableSpaceGroup : public SpaceGroup { + friend class SpaceGroupTest; + public: + TestableSpaceGroup() : + SpaceGroup(0, "", Group()) + { } + + ~TestableSpaceGroup() { } + }; +}; + + +#endif /* MANTID_GEOMETRY_SPACEGROUPTEST_H_ */ From b67b10b230783fca273683d33fb4a0d730625e6f Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Wed, 8 Oct 2014 17:30:07 +0200 Subject: [PATCH 14/48] Refs #10281. Checkpointing SpaceGroupFactory. --- Code/Mantid/Framework/Geometry/CMakeLists.txt | 3 + .../Crystal/SpaceGroupFactory.h | 117 ++++++++++ .../src/Crystal/SpaceGroupFactory.cpp | 203 ++++++++++++++++++ .../Geometry/test/SpaceGroupFactoryTest.h | 199 +++++++++++++++++ 4 files changed, 522 insertions(+) create mode 100644 Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h create mode 100644 Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp create mode 100644 Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h diff --git a/Code/Mantid/Framework/Geometry/CMakeLists.txt b/Code/Mantid/Framework/Geometry/CMakeLists.txt index 1d7f0eab23ed..d75af6e00620 100644 --- a/Code/Mantid/Framework/Geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/Geometry/CMakeLists.txt @@ -15,6 +15,7 @@ set ( SRC_FILES src/Crystal/ReflectionCondition.cpp src/Crystal/ScalarUtils.cpp src/Crystal/SpaceGroup.cpp + src/Crystal/SpaceGroupFactory.cpp src/Crystal/SymmetryOperation.cpp src/Crystal/SymmetryOperationFactory.cpp src/Crystal/SymmetryOperationSymbolParser.cpp @@ -125,6 +126,7 @@ set ( INC_FILES inc/MantidGeometry/Crystal/ReflectionCondition.h inc/MantidGeometry/Crystal/ScalarUtils.h inc/MantidGeometry/Crystal/SpaceGroup.h + inc/MantidGeometry/Crystal/SpaceGroupFactory.h inc/MantidGeometry/Crystal/SymmetryOperation.h inc/MantidGeometry/Crystal/SymmetryOperationFactory.h inc/MantidGeometry/Crystal/SymmetryOperationSymbolParser.h @@ -290,6 +292,7 @@ set ( TEST_FILES RulesUnionTest.h ScalarUtilsTest.h ShapeFactoryTest.h + SpaceGroupFactoryTest.h SpaceGroupTest.h SphereTest.h SurfaceFactoryTest.h diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h new file mode 100644 index 000000000000..0538e128330a --- /dev/null +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h @@ -0,0 +1,117 @@ +#ifndef MANTID_GEOMETRY_SPACEGROUPFACTORY_H_ +#define MANTID_GEOMETRY_SPACEGROUPFACTORY_H_ + +#include "MantidGeometry/DllConfig.h" +#include "MantidKernel/SingletonHolder.h" +#include "MantidGeometry/Crystal/SpaceGroup.h" +#include "MantidKernel/RegistrationHelper.h" + +#include + +namespace Mantid +{ +namespace Geometry +{ + +/** SpaceGroupFactory + + Factory for SpaceGroups. When a space group is subscribed, it + creates a prototype object and stores that. All space group + creations through the factory are just copy-constructions. + + Space groups can be subscribed using one of two available methods, + either by supplying ALL symmetry operations (this is called a "tabulated" + space group) or a set of generators (from International Tables for + Crystallography A). + + @author Michael Wedel, Paul Scherrer Institut - SINQ + @date 08/10/2014 + + Copyright © 2014 PSI-MSS + + 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 MANTID_GEOMETRY_DLL SpaceGroupFactoryImpl +{ +public: + virtual ~SpaceGroupFactoryImpl() { } + + SpaceGroup_const_sptr createSpaceGroup(const std::string &hmSymbol) const; + SpaceGroup_const_sptr createSpaceGroup(size_t number) const; + + bool isSubscribed(const std::string &hmSymbol) const; + bool isSubscribed(size_t number) const; + + std::vector subscribedSpaceGroupSymbols() const; + std::vector subscribedSpaceGroupNumbers() const; + + void unsubscribeSpaceGroup(const std::string &hmSymbol); + void unsubscribeSpaceGroup(size_t number); + + void subscribeGeneratedSpaceGroup(size_t number, const std::string &hmSymbol, const std::string &generators); + void subscribeTabulatedSpaceGroup(size_t number, const std::string &hmSymbol, const std::string &symmetryOperations); + +protected: + void throwIfSubscribed(size_t number, const std::string &hmSymbol); + + std::string getCenteringString(const std::string &hmSymbol) const; + Group_const_sptr getGeneratedGroup(const std::string &generators, const std::string ¢eringSymbol) const; + Group_const_sptr getTabulatedGroup(const std::string &symmetryOperations) const; + + SpaceGroup_const_sptr getPrototype(Group_const_sptr generatingGroup, size_t number, const std::string &hmSymbol) const; + + void subscribe(const SpaceGroup_const_sptr &prototype); + + SpaceGroup_const_sptr constructFromPrototype(const SpaceGroup_const_sptr prototype) const; + + std::map m_prototypesByNumber; + std::map m_prototypesBySymbol; + + SpaceGroupFactoryImpl(); + +private: + friend struct Mantid::Kernel::CreateUsingNew; +}; + +// This is taken from FuncMinimizerFactory +#ifdef _WIN32 + template class MANTID_GEOMETRY_DLL Mantid::Kernel::SingletonHolder; +#endif + +typedef Mantid::Kernel::SingletonHolder SpaceGroupFactory; + + +} // namespace Geometry +} // namespace Mantid + +#define DECLARE_GENERATED_SPACE_GROUP(number,hmSymbol,generators) \ + namespace { \ + Mantid::Kernel::RegistrationHelper register_symop_##number( \ +((Mantid::Geometry::SpaceGroupFactory::Instance().subscribeGeneratedSpaceGroup(number, hmSymbol, generators)) \ + , 0)); \ + } + +#define DECLARE_TABULATED_SPACE_GROUP(number,hmSymbol,symmetryOperations) \ + namespace { \ + Mantid::Kernel::RegistrationHelper register_symop_##number( \ +((Mantid::Geometry::SpaceGroupFactory::Instance().subscribeTabulatedSpaceGroup(number, hmSymbol, symmetryOperations)) \ + , 0)); \ + } + +#endif /* MANTID_GEOMETRY_SPACEGROUPFACTORY_H_ */ diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp new file mode 100644 index 000000000000..d5dd88c4c110 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp @@ -0,0 +1,203 @@ +#include "MantidGeometry/Crystal/SpaceGroupFactory.h" +#include "MantidGeometry/Crystal/SymmetryOperationFactory.h" + +#include "MantidGeometry/Crystal/ProductGroup.h" +#include "MantidGeometry/Crystal/CenteringGroup.h" + +#include "MantidKernel/LibraryManager.h" + +#include + +namespace Mantid +{ +namespace Geometry +{ + +/// Creates a space group given the Hermann-Mauguin symbol, throws std::invalid_argument if symbol is not registered. +SpaceGroup_const_sptr SpaceGroupFactoryImpl::createSpaceGroup(const std::string &hmSymbol) const +{ + if(!isSubscribed(hmSymbol)) { + throw std::invalid_argument("Space group with symbol '" + hmSymbol + "' is not registered."); + } + + return constructFromPrototype(m_prototypesBySymbol.find(hmSymbol)->second); +} + +/// Creates a space group given the ITA space group number, throws std::invalid_argument if number is not registered. +SpaceGroup_const_sptr SpaceGroupFactoryImpl::createSpaceGroup(size_t number) const +{ + if(!isSubscribed(number)) { + throw std::invalid_argument("Space group with requested number is not registered."); + } + + return constructFromPrototype(m_prototypesByNumber.find(number)->second); +} + +/// Returns true if space group with given symbol is subscribed. +bool SpaceGroupFactoryImpl::isSubscribed(const std::string &hmSymbol) const +{ + return m_prototypesBySymbol.find(hmSymbol) != m_prototypesBySymbol.end(); +} + +/// Returns true if space group with given number is subscribed. +bool SpaceGroupFactoryImpl::isSubscribed(size_t number) const +{ + return m_prototypesByNumber.find(number) != m_prototypesByNumber.end(); +} + +/// Returns a vector with all subscribed space group symbols. +std::vector SpaceGroupFactoryImpl::subscribedSpaceGroupSymbols() const +{ + std::vector symbols; + symbols.reserve(m_prototypesBySymbol.size()); + + for(auto it = m_prototypesBySymbol.begin(); it != m_prototypesBySymbol.end(); ++it) { + symbols.push_back(it->first); + } + + return symbols; +} + +/// Returns a vector with all subscribed space group numbers. +std::vector SpaceGroupFactoryImpl::subscribedSpaceGroupNumbers() const +{ + std::vector numbers; + numbers.reserve(m_prototypesByNumber.size()); + + for(auto it = m_prototypesByNumber.begin(); it != m_prototypesByNumber.end(); ++it) { + numbers.push_back(it->first); + } + + return numbers; +} + +/// Unsubscribes the space group with the given Hermann-Mauguin symbol, but throws std::invalid_argument if symbol is not registered. +void SpaceGroupFactoryImpl::unsubscribeSpaceGroup(const std::string &hmSymbol) +{ + if(!isSubscribed(hmSymbol)) { + throw std::invalid_argument("Cannot unsubscribe space group that is not registered."); + } + + auto eraseSymbol = m_prototypesBySymbol.find(hmSymbol); + SpaceGroup_const_sptr spaceGroup = eraseSymbol->second; + + auto eraseNumber = m_prototypesByNumber.find(spaceGroup->number()); + m_prototypesByNumber.erase(eraseNumber); + m_prototypesBySymbol.erase(eraseSymbol); +} + +/// Unsubscribes the space group with the given number, but throws std::invalid_argument if number is not registered. +void SpaceGroupFactoryImpl::unsubscribeSpaceGroup(size_t number) +{ + if(!isSubscribed(number)) { + throw std::invalid_argument("Cannot unsubscribe space group that is not registered."); + } + + auto eraseNumber = m_prototypesByNumber.find(number); + SpaceGroup_const_sptr spaceGroup = eraseNumber->second; + + auto eraseSymbol = m_prototypesBySymbol.find(spaceGroup->hmSymbol()); + m_prototypesBySymbol.erase(eraseSymbol); + m_prototypesByNumber.erase(eraseNumber); +} + +/** + * Subscribes a space group into the factory using generators + * + * With this method one can register a space group that is generated by an algorithm + * based on the instructions in [1]. Currently it's important that the Herrman- + * Mauguin symbol starts with an upper case letter, because that is used to generate + * centering translations (it should be upper case anyway). + * + * The method will throw an exception if the number or symbol is already registered. + * + * [1] Shmueli, U. Acta Crystallogr. A 40, 559–567 (1984). + http://dx.doi.org/10.1107/S0108767384001161 + * + * @param number :: Space group number according to International Tables for Crystallography A + * @param hmSymbol :: Herrman-Mauguin symbol with upper case first letter (centering). + * @param generators :: + */ +void SpaceGroupFactoryImpl::subscribeGeneratedSpaceGroup(size_t number, const std::string &hmSymbol, const std::string &generators) +{ + throwIfSubscribed(number, hmSymbol); + + // Generate factor group and centering group + std::string centeringSymbol = getCenteringString(hmSymbol); + Group_const_sptr generatingGroup = getGeneratedGroup(generators, centeringSymbol); + + SpaceGroup_const_sptr prototype = getPrototype(generatingGroup, number, hmSymbol); + subscribe(prototype); +} + +void SpaceGroupFactoryImpl::subscribeTabulatedSpaceGroup(size_t number, const std::string &hmSymbol, const std::string &symmetryOperations) +{ + throwIfSubscribed(number, hmSymbol); + + // Generate a group using the supplied symmetry operations + Group_const_sptr generatingGroup = getTabulatedGroup(symmetryOperations); + + SpaceGroup_const_sptr prototype = getPrototype(generatingGroup, number, hmSymbol); + subscribe(prototype); +} + +SpaceGroup_const_sptr SpaceGroupFactoryImpl::getPrototype(Group_const_sptr generatingGroup, size_t number, const std::string &hmSymbol) const +{ + if(!generatingGroup) { + throw std::runtime_error("Could not create Group from supplied symmetry operations."); + } + + return boost::make_shared(number, hmSymbol, *generatingGroup); +} + +SpaceGroup_const_sptr SpaceGroupFactoryImpl::constructFromPrototype(const SpaceGroup_const_sptr prototype) const +{ + return boost::make_shared(*prototype); +} + +void SpaceGroupFactoryImpl::throwIfSubscribed(size_t number, const std::string &hmSymbol) +{ + if(isSubscribed(number) || isSubscribed(hmSymbol)) { + throw std::invalid_argument("Space group with this number/symbol is already registered."); + } +} + +void SpaceGroupFactoryImpl::subscribe(const SpaceGroup_const_sptr &prototype) +{ + m_prototypesByNumber.insert(std::make_pair(prototype->number(), prototype)); + m_prototypesBySymbol.insert(std::make_pair(prototype->hmSymbol(), prototype)); +} + +Group_const_sptr SpaceGroupFactoryImpl::getTabulatedGroup(const std::string &symmetryOperations) const +{ + return GroupFactory::create(symmetryOperations); +} + +std::string SpaceGroupFactoryImpl::getCenteringString(const std::string &hmSymbol) const +{ + return hmSymbol.substr(0, 1); +} + +Group_const_sptr SpaceGroupFactoryImpl::getGeneratedGroup(const std::string &generators, const std::string ¢eringSymbol) const +{ + Group_const_sptr baseGroup = GroupFactory::create(generators); + Group_const_sptr centeringGroup = GroupFactory::create(centeringSymbol); + + return baseGroup * centeringGroup; +} + +SpaceGroupFactoryImpl::SpaceGroupFactoryImpl() : + m_prototypesByNumber(), + m_prototypesBySymbol() +{ + Kernel::LibraryManager::Instance(); +} + +DECLARE_TABULATED_SPACE_GROUP(1, "P1", "x,y,z"); + +DECLARE_GENERATED_SPACE_GROUP(2, "P-1", "-x,-y,-z"); +DECLARE_GENERATED_SPACE_GROUP(225, "Fm-3m", "-x,-y,z; -x,y,-z; z,x,y; y,x,-z; -x,-y,-z"); +DECLARE_GENERATED_SPACE_GROUP(229, "Im-3m", "-x,-y,z; -x,y,-z; z,x,y; y,x,-z; -x,-y,-z"); + +} // namespace Geometry +} // namespace Mantid diff --git a/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h b/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h new file mode 100644 index 000000000000..87975794d1d1 --- /dev/null +++ b/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h @@ -0,0 +1,199 @@ +#ifndef MANTID_GEOMETRY_SPACEGROUPFACTORYTEST_H_ +#define MANTID_GEOMETRY_SPACEGROUPFACTORYTEST_H_ + +#include + +#include "MantidGeometry/Crystal/SpaceGroupFactory.h" + +using namespace Mantid::Geometry; +using Mantid::Kernel::V3D; + +class SpaceGroupFactoryTest : 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 SpaceGroupFactoryTest *createSuite() { return new SpaceGroupFactoryTest(); } + static void destroySuite( SpaceGroupFactoryTest *suite ) { delete suite; } + + void testInstance() + { + TS_ASSERT_THROWS_NOTHING(SpaceGroupFactory::Instance()); + } + + void testSubscribeGeneratedSpaceGroup() + { + TestableSpaceGroupFactory factory; + + TS_ASSERT(!factory.isSubscribed(2)); + TS_ASSERT(!factory.isSubscribed("P-1")); + + TS_ASSERT_THROWS_NOTHING(factory.subscribeGeneratedSpaceGroup(2, "P-1", "-x,-y,-z")); + + TS_ASSERT(factory.isSubscribed(2)); + TS_ASSERT(factory.isSubscribed("P-1")); + + // subscribing twice does not work + TS_ASSERT_THROWS(factory.subscribeGeneratedSpaceGroup(2, "P-1", "-x,-y,-z"), std::invalid_argument); + + // neither does with a tabulated space group + TS_ASSERT_THROWS(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z"), std::invalid_argument); + + TS_ASSERT_THROWS(factory.subscribeGeneratedSpaceGroup(2, "FAKE", "-x,-y,-z"), std::invalid_argument); + TS_ASSERT_THROWS(factory.subscribeGeneratedSpaceGroup(3, "P-1", "-x,-y,-z"), std::invalid_argument); + + // invalid generators are caught before anything is done + TS_ASSERT_THROWS_ANYTHING(factory.subscribeGeneratedSpaceGroup(4, "Fake", "invalid")); + + TS_ASSERT(!factory.isSubscribed(4)); + TS_ASSERT(!factory.isSubscribed("Fake")); + } + + void testSubscribeTabulatedSpaceGroup() + { + TestableSpaceGroupFactory factory; + + TS_ASSERT(!factory.isSubscribed(2)); + TS_ASSERT(!factory.isSubscribed("P-1")); + + TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z")); + + TS_ASSERT(factory.isSubscribed(2)); + TS_ASSERT(factory.isSubscribed("P-1")); + + // subscribing twice does not work + TS_ASSERT_THROWS(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z"), std::invalid_argument); + + // neither does with a generated space group + TS_ASSERT_THROWS(factory.subscribeGeneratedSpaceGroup(2, "P-1", "-x,-y,-z"), std::invalid_argument); + + TS_ASSERT_THROWS(factory.subscribeTabulatedSpaceGroup(2, "FAKE", "-x,-y,-z"), std::invalid_argument); + TS_ASSERT_THROWS(factory.subscribeTabulatedSpaceGroup(3, "P-1", "-x,-y,-z"), std::invalid_argument); + + // invalid generators are caught before anything is done + TS_ASSERT_THROWS_ANYTHING(factory.subscribeTabulatedSpaceGroup(4, "Fake", "invalid")); + + TS_ASSERT(!factory.isSubscribed(4)); + TS_ASSERT(!factory.isSubscribed("Fake")); + } + + void testIsSubscribed() + { + TestableSpaceGroupFactory factory; + + TS_ASSERT(!factory.isSubscribed(1)); + + TS_ASSERT(!factory.isSubscribed(2)); + TS_ASSERT(!factory.isSubscribed("P-1")); + + TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z")); + + TS_ASSERT(factory.isSubscribed(2)); + TS_ASSERT(factory.isSubscribed("P-1")); + + TS_ASSERT(!factory.isSubscribed(1)); + } + + void testSubscribedSpaceGroupSymbols() + { + TestableSpaceGroupFactory factory; + + TS_ASSERT(factory.subscribedSpaceGroupSymbols().empty()); + + TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z")); + + std::vector symbols = factory.subscribedSpaceGroupSymbols(); + TS_ASSERT_EQUALS(symbols.size(), 1); + TS_ASSERT_DIFFERS(std::find(symbols.begin(), symbols.end(), "P-1"), symbols.end()); + + TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(1, "P1", "x,y,z")); + symbols = factory.subscribedSpaceGroupSymbols(); + TS_ASSERT_EQUALS(symbols.size(), 2); + TS_ASSERT_DIFFERS(std::find(symbols.begin(), symbols.end(), "P1"), symbols.end()); + } + + void testSubscribedSpaceGroupNumbers() + { + TestableSpaceGroupFactory factory; + + TS_ASSERT(factory.subscribedSpaceGroupNumbers().empty()); + + TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z")); + + std::vector numbers = factory.subscribedSpaceGroupNumbers(); + TS_ASSERT_EQUALS(numbers.size(), 1); + TS_ASSERT_DIFFERS(std::find(numbers.begin(), numbers.end(), 2), numbers.end()); + + TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(1, "P1", "x,y,z")); + numbers = factory.subscribedSpaceGroupNumbers(); + TS_ASSERT_EQUALS(numbers.size(), 2); + TS_ASSERT_DIFFERS(std::find(numbers.begin(), numbers.end(), 1), numbers.end()); + } + + void testUnsubscribeNumber() + { + TestableSpaceGroupFactory factory; + + TS_ASSERT_THROWS(factory.unsubscribeSpaceGroup(2), std::invalid_argument); + + TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z")); + TS_ASSERT_THROWS_NOTHING(factory.unsubscribeSpaceGroup(2)); + + TS_ASSERT_THROWS(factory.unsubscribeSpaceGroup("P-1"), std::invalid_argument); + } + + void testUnsubscribeSymbol() + { + TestableSpaceGroupFactory factory; + + TS_ASSERT_THROWS(factory.unsubscribeSpaceGroup("P-1"), std::invalid_argument); + + TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z")); + TS_ASSERT_THROWS_NOTHING(factory.unsubscribeSpaceGroup("P-1")); + + TS_ASSERT_THROWS(factory.unsubscribeSpaceGroup(2), std::invalid_argument); + } + + void testCreateSpaceGroupNumber() + { + TestableSpaceGroupFactory factory; + + TS_ASSERT_THROWS(factory.createSpaceGroup(2), std::invalid_argument); + TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z")); + + TS_ASSERT_THROWS_NOTHING(factory.createSpaceGroup(2)); + + SpaceGroup_const_sptr spaceGroup = factory.createSpaceGroup(2); + TS_ASSERT_EQUALS(spaceGroup->order(), 2); + } + + void testSpaceGroup() + { + SpaceGroup_const_sptr sgBCC = SpaceGroupFactory::Instance().createSpaceGroup(229); + + std::cout << "Space group: " << sgBCC->hmSymbol() << " (" << sgBCC->number() << "):" << std::endl; + std::cout << " Order: " << sgBCC->order() << std::endl; + + std::cout << " Equivalent positions:" << std::endl; + std::cout << " 96l ("; + + V3D general(0.54, 0.43, 0.12); + std::vector equivs = sgBCC->getEquivalentPositions(general); + + std::cout << equivs.size() << " equivalents)." << std::endl; + } + + +private: + class TestableSpaceGroupFactory : public SpaceGroupFactoryImpl + { + friend class SpaceGroupFactoryTest; + public: + TestableSpaceGroupFactory() : SpaceGroupFactoryImpl() { } + ~TestableSpaceGroupFactory() { } + }; + +}; + + +#endif /* MANTID_GEOMETRY_SPACEGROUPFACTORYTEST_H_ */ From 42151b78810ac058a4e3947c1406d59b37ddf747 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Thu, 9 Oct 2014 09:58:28 +0200 Subject: [PATCH 15/48] Refs #10281. Changed SpaceGroupFactory --- .../Crystal/SpaceGroupFactory.h | 9 +-- .../src/Crystal/SpaceGroupFactory.cpp | 79 ++++++++----------- .../Geometry/test/SpaceGroupFactoryTest.h | 47 ++++++----- 3 files changed, 61 insertions(+), 74 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h index 0538e128330a..8f0dd3988295 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h @@ -53,22 +53,21 @@ class MANTID_GEOMETRY_DLL SpaceGroupFactoryImpl virtual ~SpaceGroupFactoryImpl() { } SpaceGroup_const_sptr createSpaceGroup(const std::string &hmSymbol) const; - SpaceGroup_const_sptr createSpaceGroup(size_t number) const; bool isSubscribed(const std::string &hmSymbol) const; bool isSubscribed(size_t number) const; std::vector subscribedSpaceGroupSymbols() const; + std::vector subscribedSpaceGroupSymbols(size_t number) const; std::vector subscribedSpaceGroupNumbers() const; void unsubscribeSpaceGroup(const std::string &hmSymbol); - void unsubscribeSpaceGroup(size_t number); void subscribeGeneratedSpaceGroup(size_t number, const std::string &hmSymbol, const std::string &generators); void subscribeTabulatedSpaceGroup(size_t number, const std::string &hmSymbol, const std::string &symmetryOperations); protected: - void throwIfSubscribed(size_t number, const std::string &hmSymbol); + void throwIfSubscribed(const std::string &hmSymbol); std::string getCenteringString(const std::string &hmSymbol) const; Group_const_sptr getGeneratedGroup(const std::string &generators, const std::string ¢eringSymbol) const; @@ -80,8 +79,8 @@ class MANTID_GEOMETRY_DLL SpaceGroupFactoryImpl SpaceGroup_const_sptr constructFromPrototype(const SpaceGroup_const_sptr prototype) const; - std::map m_prototypesByNumber; - std::map m_prototypesBySymbol; + std::multimap m_numberMap; + std::map m_prototypes; SpaceGroupFactoryImpl(); diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp index d5dd88c4c110..1e8a0754095f 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp @@ -20,51 +20,55 @@ SpaceGroup_const_sptr SpaceGroupFactoryImpl::createSpaceGroup(const std::string throw std::invalid_argument("Space group with symbol '" + hmSymbol + "' is not registered."); } - return constructFromPrototype(m_prototypesBySymbol.find(hmSymbol)->second); -} - -/// Creates a space group given the ITA space group number, throws std::invalid_argument if number is not registered. -SpaceGroup_const_sptr SpaceGroupFactoryImpl::createSpaceGroup(size_t number) const -{ - if(!isSubscribed(number)) { - throw std::invalid_argument("Space group with requested number is not registered."); - } - - return constructFromPrototype(m_prototypesByNumber.find(number)->second); + return constructFromPrototype(m_prototypes.find(hmSymbol)->second); } /// Returns true if space group with given symbol is subscribed. bool SpaceGroupFactoryImpl::isSubscribed(const std::string &hmSymbol) const { - return m_prototypesBySymbol.find(hmSymbol) != m_prototypesBySymbol.end(); + return m_prototypes.find(hmSymbol) != m_prototypes.end(); } /// Returns true if space group with given number is subscribed. bool SpaceGroupFactoryImpl::isSubscribed(size_t number) const { - return m_prototypesByNumber.find(number) != m_prototypesByNumber.end(); + return m_numberMap.find(number) != m_numberMap.end(); } /// Returns a vector with all subscribed space group symbols. std::vector SpaceGroupFactoryImpl::subscribedSpaceGroupSymbols() const { std::vector symbols; - symbols.reserve(m_prototypesBySymbol.size()); + symbols.reserve(m_prototypes.size()); - for(auto it = m_prototypesBySymbol.begin(); it != m_prototypesBySymbol.end(); ++it) { + for(auto it = m_prototypes.begin(); it != m_prototypes.end(); ++it) { symbols.push_back(it->first); } return symbols; } +/// Returns a vector with all symbols that correspond to a space group number +std::vector SpaceGroupFactoryImpl::subscribedSpaceGroupSymbols(size_t number) const +{ + std::vector symbols; + + auto keyPair = m_numberMap.equal_range(number); + + for(auto it = keyPair.first; it != keyPair.second; ++it) { + symbols.push_back(it->second); + } + + return symbols; +} + /// Returns a vector with all subscribed space group numbers. std::vector SpaceGroupFactoryImpl::subscribedSpaceGroupNumbers() const { std::vector numbers; - numbers.reserve(m_prototypesByNumber.size()); + numbers.reserve(m_numberMap.size()); - for(auto it = m_prototypesByNumber.begin(); it != m_prototypesByNumber.end(); ++it) { + for(auto it = m_numberMap.begin(); it != m_numberMap.end(); it = m_numberMap.upper_bound(it->first)) { numbers.push_back(it->first); } @@ -78,27 +82,12 @@ void SpaceGroupFactoryImpl::unsubscribeSpaceGroup(const std::string &hmSymbol) throw std::invalid_argument("Cannot unsubscribe space group that is not registered."); } - auto eraseSymbol = m_prototypesBySymbol.find(hmSymbol); + auto eraseSymbol = m_prototypes.find(hmSymbol); SpaceGroup_const_sptr spaceGroup = eraseSymbol->second; - auto eraseNumber = m_prototypesByNumber.find(spaceGroup->number()); - m_prototypesByNumber.erase(eraseNumber); - m_prototypesBySymbol.erase(eraseSymbol); -} - -/// Unsubscribes the space group with the given number, but throws std::invalid_argument if number is not registered. -void SpaceGroupFactoryImpl::unsubscribeSpaceGroup(size_t number) -{ - if(!isSubscribed(number)) { - throw std::invalid_argument("Cannot unsubscribe space group that is not registered."); - } - - auto eraseNumber = m_prototypesByNumber.find(number); - SpaceGroup_const_sptr spaceGroup = eraseNumber->second; - - auto eraseSymbol = m_prototypesBySymbol.find(spaceGroup->hmSymbol()); - m_prototypesBySymbol.erase(eraseSymbol); - m_prototypesByNumber.erase(eraseNumber); + auto eraseNumber = m_numberMap.find(spaceGroup->number()); + m_numberMap.erase(eraseNumber); + m_prototypes.erase(eraseSymbol); } /** @@ -120,7 +109,7 @@ void SpaceGroupFactoryImpl::unsubscribeSpaceGroup(size_t number) */ void SpaceGroupFactoryImpl::subscribeGeneratedSpaceGroup(size_t number, const std::string &hmSymbol, const std::string &generators) { - throwIfSubscribed(number, hmSymbol); + throwIfSubscribed(hmSymbol); // Generate factor group and centering group std::string centeringSymbol = getCenteringString(hmSymbol); @@ -132,7 +121,7 @@ void SpaceGroupFactoryImpl::subscribeGeneratedSpaceGroup(size_t number, const st void SpaceGroupFactoryImpl::subscribeTabulatedSpaceGroup(size_t number, const std::string &hmSymbol, const std::string &symmetryOperations) { - throwIfSubscribed(number, hmSymbol); + throwIfSubscribed(hmSymbol); // Generate a group using the supplied symmetry operations Group_const_sptr generatingGroup = getTabulatedGroup(symmetryOperations); @@ -155,17 +144,17 @@ SpaceGroup_const_sptr SpaceGroupFactoryImpl::constructFromPrototype(const SpaceG return boost::make_shared(*prototype); } -void SpaceGroupFactoryImpl::throwIfSubscribed(size_t number, const std::string &hmSymbol) +void SpaceGroupFactoryImpl::throwIfSubscribed(const std::string &hmSymbol) { - if(isSubscribed(number) || isSubscribed(hmSymbol)) { - throw std::invalid_argument("Space group with this number/symbol is already registered."); + if(isSubscribed(hmSymbol)) { + throw std::invalid_argument("Space group with this symbol is already registered."); } } void SpaceGroupFactoryImpl::subscribe(const SpaceGroup_const_sptr &prototype) { - m_prototypesByNumber.insert(std::make_pair(prototype->number(), prototype)); - m_prototypesBySymbol.insert(std::make_pair(prototype->hmSymbol(), prototype)); + m_numberMap.insert(std::make_pair(prototype->number(), prototype->hmSymbol())); + m_prototypes.insert(std::make_pair(prototype->hmSymbol(), prototype)); } Group_const_sptr SpaceGroupFactoryImpl::getTabulatedGroup(const std::string &symmetryOperations) const @@ -187,8 +176,8 @@ Group_const_sptr SpaceGroupFactoryImpl::getGeneratedGroup(const std::string &gen } SpaceGroupFactoryImpl::SpaceGroupFactoryImpl() : - m_prototypesByNumber(), - m_prototypesBySymbol() + m_numberMap(), + m_prototypes() { Kernel::LibraryManager::Instance(); } diff --git a/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h b/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h index 87975794d1d1..7709155475ab 100644 --- a/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h +++ b/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h @@ -36,10 +36,14 @@ class SpaceGroupFactoryTest : public CxxTest::TestSuite // subscribing twice does not work TS_ASSERT_THROWS(factory.subscribeGeneratedSpaceGroup(2, "P-1", "-x,-y,-z"), std::invalid_argument); + + // but having a different symbol for the same number is ok. + TS_ASSERT_THROWS_NOTHING(factory.subscribeGeneratedSpaceGroup(2, "F-1", "-x,-y,-z")) + // neither does with a tabulated space group TS_ASSERT_THROWS(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z"), std::invalid_argument); - TS_ASSERT_THROWS(factory.subscribeGeneratedSpaceGroup(2, "FAKE", "-x,-y,-z"), std::invalid_argument); + // Different number with same symbol - does not work TS_ASSERT_THROWS(factory.subscribeGeneratedSpaceGroup(3, "P-1", "-x,-y,-z"), std::invalid_argument); // invalid generators are caught before anything is done @@ -64,10 +68,13 @@ class SpaceGroupFactoryTest : public CxxTest::TestSuite // subscribing twice does not work TS_ASSERT_THROWS(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z"), std::invalid_argument); + // but having a different symbol for the same number is ok. + TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(2, "F-1", "x,y,z; -x,-y,-z")) + // neither does with a generated space group TS_ASSERT_THROWS(factory.subscribeGeneratedSpaceGroup(2, "P-1", "-x,-y,-z"), std::invalid_argument); - TS_ASSERT_THROWS(factory.subscribeTabulatedSpaceGroup(2, "FAKE", "-x,-y,-z"), std::invalid_argument); + // Different number with same symbol - does not work TS_ASSERT_THROWS(factory.subscribeTabulatedSpaceGroup(3, "P-1", "-x,-y,-z"), std::invalid_argument); // invalid generators are caught before anything is done @@ -128,18 +135,25 @@ class SpaceGroupFactoryTest : public CxxTest::TestSuite numbers = factory.subscribedSpaceGroupNumbers(); TS_ASSERT_EQUALS(numbers.size(), 2); TS_ASSERT_DIFFERS(std::find(numbers.begin(), numbers.end(), 1), numbers.end()); + + // Subscribing the same number twice should not influence vector size + TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(1, "F1", "x,y,z")); + numbers = factory.subscribedSpaceGroupNumbers(); + TS_ASSERT_EQUALS(numbers.size(), 2); } - void testUnsubscribeNumber() + void testSubscribedSpaceGroupSymbolsForNumber() { TestableSpaceGroupFactory factory; + factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z"); + factory.subscribeTabulatedSpaceGroup(2, "F-1", "x,y,z; -x,-y,-z"); + factory.subscribeTabulatedSpaceGroup(1, "P1", "x,y,z"); - TS_ASSERT_THROWS(factory.unsubscribeSpaceGroup(2), std::invalid_argument); - - TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z")); - TS_ASSERT_THROWS_NOTHING(factory.unsubscribeSpaceGroup(2)); + std::vector symbols = factory.subscribedSpaceGroupSymbols(1); + TS_ASSERT_EQUALS(symbols.size(), 1); - TS_ASSERT_THROWS(factory.unsubscribeSpaceGroup("P-1"), std::invalid_argument); + symbols = factory.subscribedSpaceGroupSymbols(2); + TS_ASSERT_EQUALS(symbols.size(), 2); } void testUnsubscribeSymbol() @@ -150,26 +164,11 @@ class SpaceGroupFactoryTest : public CxxTest::TestSuite TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z")); TS_ASSERT_THROWS_NOTHING(factory.unsubscribeSpaceGroup("P-1")); - - TS_ASSERT_THROWS(factory.unsubscribeSpaceGroup(2), std::invalid_argument); - } - - void testCreateSpaceGroupNumber() - { - TestableSpaceGroupFactory factory; - - TS_ASSERT_THROWS(factory.createSpaceGroup(2), std::invalid_argument); - TS_ASSERT_THROWS_NOTHING(factory.subscribeTabulatedSpaceGroup(2, "P-1", "x,y,z; -x,-y,-z")); - - TS_ASSERT_THROWS_NOTHING(factory.createSpaceGroup(2)); - - SpaceGroup_const_sptr spaceGroup = factory.createSpaceGroup(2); - TS_ASSERT_EQUALS(spaceGroup->order(), 2); } void testSpaceGroup() { - SpaceGroup_const_sptr sgBCC = SpaceGroupFactory::Instance().createSpaceGroup(229); + SpaceGroup_const_sptr sgBCC = SpaceGroupFactory::Instance().createSpaceGroup("Im-3m"); std::cout << "Space group: " << sgBCC->hmSymbol() << " (" << sgBCC->number() << "):" << std::endl; std::cout << " Order: " << sgBCC->order() << std::endl; From 992f7fc8bdd9a264879f2d1964960ba62806c4c1 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Thu, 9 Oct 2014 15:50:06 +0200 Subject: [PATCH 16/48] Refs #10281. Updating documentation. --- .../MantidGeometry/Crystal/CenteringGroup.h | 17 ++++- .../inc/MantidGeometry/Crystal/CyclicGroup.h | 43 +++++++++++- .../inc/MantidGeometry/Crystal/Group.h | 70 ++++++++++++++++++- .../inc/MantidGeometry/Crystal/ProductGroup.h | 26 ++++--- .../Geometry/src/Crystal/CyclicGroup.cpp | 2 + .../Framework/Geometry/src/Crystal/Group.cpp | 26 ++++++- 6 files changed, 169 insertions(+), 15 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h index 7379283f3919..5088280134f7 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h @@ -12,8 +12,21 @@ namespace Geometry /** CenteringGroup - A class that holds symmetry operations to describe a lattice - centering. + This class is mostly a convenience class. It takes a bravais lattice symbol + (P, I, A, B, C, F, R) and forms a group that contains all translations + connected to the centering. This is for example used in the space group + generation process. + + In addition to the inherited interface of Group, CenteringGroup provides + methods that provide some meta information, namely the "name" of the + centering operation. While CenteringGroup::getSymbol() returns a string, + CenteringGroup::getType() returns a value of the enum type + CenteringGroup::CenteringType. + + Important differences occur in the handling of Rhombohedral centering. + CenteringType distinguishes between obverse (Robv) and reverse (Rrev) + setting. These can be given explicitly as strings for construction. When + only "R" is provided, the obverse setting is assumed. @author Michael Wedel, Paul Scherrer Institut - SINQ @date 07/10/2014 diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h index 8b3817a57330..7cece6f8f7b6 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h @@ -14,8 +14,47 @@ namespace Geometry /** CyclicGroup : - A class that represents a cyclic group of symmetry operations. - It just constructs a Group by multiplying itself "order" times. + A cyclic group G has the property that it can be represented by + powers of one symmetry operation S of order n: + + G = { S^1, S^2, ..., S^n = S^0 = I } + + The operation S^m is defined as carrying out the multiplication + S * S * ... * S. To illustrate this, a four-fold rotation around + the z-axis is considered. The symmetry operation representing the + transformation by this symmetry element is "-y,x,z". This is also the + first member of the resulting group: + + S^1 = S = -y,x,z + + Then, multiplying this by itself: + + S^2 = S * S = -x,-y,z + S^3 = S * S * S = y,-x,z + S^4 = S * S * S * S = x,y,z = I + + Thus, the cyclic group G resulting from the operation "-y,x,z" contains + the following members: + + G = { S^1, S^2, S^3, I } = { -y,x,z; -x,-y,z; y,-x,z; x,y,z } + + This example shows in fact how the point group "4" can be generated as + a cyclic group by the generator S = -y,x,z. Details about this + are given for example in [1]. + + In code, the example is very concise: + + Group_const_sptr pointGroup4 = GroupFactory::create("-y,x,z"); + + This is much more convenient than having to construct a Group, + where all four symmetry operations would have to be supplied. + + Related to this class is ProductGroup, which provides an easy way + to express a group that is the product of multiple cyclic groups + (such as some point groups). + + [1] Shmueli, U. Acta Crystallogr. A 40, 559–567 (1984). + http://dx.doi.org/10.1107/S0108767384001161 @author Michael Wedel, Paul Scherrer Institut - SINQ @date 03/10/2014 diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h index ca41b912c14e..72309acfce96 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h @@ -17,7 +17,74 @@ namespace Geometry /** Group : - A class representing a group of symmetry operations. + The class Group represents a set of symmetry operations (or + symmetry group). It can be constructed by providing a vector + of the symmetry operations it consists of. Another possibility + is using a string (see SymmetryOperationFactory for format). + + Upon construction of Group, the vector of symmetry operations + is potentially reduced to a set of unique operations, because + each operation may occur only once. + + The number of symmetry operations in the group determines its + so-called order, it can be queried with the member function + Group::order(). If one needs to process the symmetry operation + themselves, they can be obtained by Group::getSymmetryOperations(). + + A common task is to apply all symmetry operations of a group to + a point (given in the form of a Kernel::V3D-object). The multiplication + operator for carrying out this operation has been overloaded + to perform this task: + + vector coordinates = Group * V3D + + Please note that a set of unique coordinates is produced, which means + that the number of coordinates in the vector may be smaller than the + order of the group, depending on the input. This is because the + components of V3D are mapped onto the interval [0, 1). + + Two groups A and B can be combined by a multiplication operation, provided by + the corresponding overloaded operator: + + Group A, B; + Group C = A * B + + In this operation each element of A is multiplied with each element of B + and from the resulting list a new group is constructed. For better illustration, + an example is provided. Group A has two symmetry operations: identity ("x,y,z") + and inversion ("-x,-y,-z"). Group B also consists of two operations: + identity ("x,y,z") and a rotation around the y-axis ("-x,y,-z"). In terms + of symmetry elements, the groups are defined like this: + + A := { 1, -1 }; B := { 1, 2 [010] } + + The following table shows all multiplications that are carried out and their + results (for multiplication of symmetry operations see SymmetryOperation): + A + | x,y,z -x,-y,-z + --------+------------------------ + x,y,z | x,y,z -x,-y,-z + B | + -x,y,-z | -x,y,-z x,-y,z + + The resulting group contains the three elements of A and B (1, -1, 2 [010]), + but also one new element that is the result of multiplying "x,y,z" and "-x,y,-z", + which is "x,-y,z" - the operation resulting from a mirror plane perpendicular + to the y-axis. In fact, this example demonstrated how the combination of + two crystallographic point groups (see PointGroup documentation and wiki) + "-1" and "2" results in a new point group "2/m". + + Most of the time it's not required to use Group directly, there are several + sub-classes that implement different behavior (CenteringGroup, CyclicGroup, + ProductGroup) and are easier to handle. For construction there is a simple + "factory function", that works for all Group-based classes which provide a + string-based constructor: + + Group_const_sptr group = GroupFactory::create("-x,-y,-z"); + + However, the most useful sub-class is SpaceGroup, which comes with its + own factory. For detailed information about the respective sub-classes, + please consult their documentation. @author Michael Wedel, Paul Scherrer Institut - SINQ @date 03/10/2014 @@ -74,6 +141,7 @@ typedef boost::shared_ptr Group_sptr; typedef boost::shared_ptr Group_const_sptr; namespace GroupFactory { + /// Creates a Group sub-class of type T if T has a constructor that takes a string. template Group_const_sptr create(const std::string &initializationString) { diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductGroup.h index 338749341ba4..43cb91b5833d 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductGroup.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductGroup.h @@ -11,18 +11,28 @@ namespace Geometry /** ProductGroup : - ProductGroup objects can be constructed using a string which contains - a series of symmetry operations S_i, separated by semicolons: + ProductGroup expands a bit on the explanations given in + CyclicGroup. As shown for example in [1], some point groups + cannot be expressed solely as a cyclic group. Instead it's + necessary to multiply two or three cyclic groups to obtain all + symmetry operations of that group. - x,y,z; -x,-y,-z; x,-y,z + For this purpose, ProductGroup was created. It takes a set of + n symmetry operations, each of which is seen as a generator of + a cyclic group C_i. The resulting n groups ("factor groups") are + multiplied to form a product group G: - For each symmetry operation, a CyclicGroup G_i object is created. These groups - are then multiplied to form a ProductGroup: + G = C_1 * C_2 * ... * C_n - P = G_1 * G_2 * G_3 * ... * G_n + Where C_i is generated by the symmetry operation S_i. The notation + in code to generate even large groups from a few generators + becomes very short using this class: - This way, groups with many symmetry operations can be generated from a small - set of generators. This is for example described in [1]. + Group_const_sptr pointGroup422 = GroupFactory::create("-y,x,z; x,-y,-z"); + + This is for example used in SpaceGroupFactory to create space groups + from a small set of generators supplied in the International Tables + for Crystallography A. [1] Shmueli, U. Acta Crystallogr. A 40, 559–567 (1984). http://dx.doi.org/10.1107/S0108767384001161 diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp index b9bb8302672b..b65c308663c2 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/CyclicGroup.cpp @@ -14,11 +14,13 @@ CyclicGroup::CyclicGroup(const std::string &symmetryOperationString) : } +/// Construct CyclicGroup from a SymmetryOperation object. CyclicGroup::CyclicGroup(const SymmetryOperation &symmetryOperation) : Group(generateAllOperations(symmetryOperation)) { } +/// Returns a vector with all symmetry operations that are part of the cyclic group defined by the generating operation. std::vector CyclicGroup::generateAllOperations(const SymmetryOperation &operation) const { std::vector symOps(1, operation); diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp index 85c45b384543..411b3428553e 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/Group.cpp @@ -7,7 +7,7 @@ namespace Mantid namespace Geometry { -/// Construct a group group from a given set of operations. This is +/// Default constructor. Creates a group with one symmetry operation (identity). Group::Group() : m_allOperations(), m_operationSet() @@ -16,6 +16,7 @@ Group::Group() : setSymmetryOperations(operation); } +/// Uses SymmetryOperationFactory to create a vector of symmetry operations from the string. Group::Group(const std::string &symmetryOperationString) : m_allOperations(), m_operationSet() @@ -23,6 +24,7 @@ Group::Group(const std::string &symmetryOperationString) : setSymmetryOperations(SymmetryOperationFactory::Instance().createSymOps(symmetryOperationString)); } +/// Constructs a group from the symmetry operations in the vector, duplicates are removed. Group::Group(const std::vector &symmetryOperations) : m_allOperations(), m_operationSet() @@ -30,6 +32,7 @@ Group::Group(const std::vector &symmetryOperations) : setSymmetryOperations(symmetryOperations); } +/// Copy constructor. Group::Group(const Group &other) : m_allOperations(other.m_allOperations), m_operationSet(other.m_operationSet) @@ -37,6 +40,7 @@ Group::Group(const Group &other) : } +/// Assignment operator. Group &Group::operator =(const Group &other) { m_allOperations = other.m_allOperations; @@ -45,16 +49,27 @@ Group &Group::operator =(const Group &other) return *this; } +/// Returns the order of the group, which is the number of symmetry operations. size_t Group::order() const { return m_allOperations.size(); } +/// Returns a vector with all symmetry operations. std::vector Group::getSymmetryOperations() const { return m_allOperations; } +/** + * Multiplication operator of two groups. + * + * Multiplies each element of this group with each element of the other + * group, as described in the class documentation. + * + * @param other :: A group. + * @return The product resulting from the group multiplication. + */ Group Group::operator *(const Group &other) const { std::vector result; @@ -69,6 +84,7 @@ Group Group::operator *(const Group &other) const return Group(result); } +/// Returns a unique set of Kernel::V3D resulting from applying all symmetry operations, vectors are wrapped to [0, 1). std::vector Group::operator *(const Kernel::V3D &vector) const { std::set result; @@ -80,16 +96,19 @@ std::vector Group::operator *(const Kernel::V3D &vector) const return std::vector(result.begin(), result.end()); } +/// Returns true if both groups contain the same set of symmetry operations. bool Group::operator ==(const Group &other) const { return m_operationSet == other.m_operationSet; } +/// Returns true if groups are different from eachother. bool Group::operator !=(const Group &other) const { return !(this->operator ==(other)); } +/// Assigns symmetry operations, throws std::invalid_argument if vector is empty. void Group::setSymmetryOperations(const std::vector &symmetryOperations) { if(symmetryOperations.size() < 1) { @@ -100,6 +119,7 @@ void Group::setSymmetryOperations(const std::vector &symmetry m_allOperations = std::vector(m_operationSet.begin(), m_operationSet.end()); } +/// Convenience operator* for directly multiplying groups using shared pointers. Group_const_sptr operator *(const Group_const_sptr &lhs, const Group_const_sptr &rhs) { if(!lhs || !rhs) { @@ -109,6 +129,7 @@ Group_const_sptr operator *(const Group_const_sptr &lhs, const Group_const_sptr return boost::make_shared((*lhs) * (*rhs)); } +/// Convenience operator* for getting a vector of V3D using shared pointers. std::vector operator *(const Group_const_sptr &lhs, const Kernel::V3D &rhs) { if(!lhs) { @@ -118,7 +139,7 @@ std::vector operator *(const Group_const_sptr &lhs, const Kernel::V return (*lhs) * rhs; } - +/// Equality operator for shared pointers. bool operator ==(const Group_const_sptr &lhs, const Group_const_sptr &rhs) { if(!lhs || !rhs) { @@ -128,6 +149,7 @@ bool operator ==(const Group_const_sptr &lhs, const Group_const_sptr &rhs) return (*lhs) == (*rhs); } +/// Inequality operator for shared pointers. bool operator !=(const Group_const_sptr &lhs, const Group_const_sptr &rhs) { return !(operator ==(lhs, rhs)); From eed522ec4f2eca7d009d38b34ca23d8e02ca777d Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Thu, 9 Oct 2014 15:50:46 +0200 Subject: [PATCH 17/48] Refs #10281. More work on SpaceGroupFactory. --- .../Crystal/SpaceGroupFactory.h | 24 +++++++++++---- .../src/Crystal/SpaceGroupFactory.cpp | 29 +++++++++++++++---- .../Geometry/test/SpaceGroupFactoryTest.h | 2 +- 3 files changed, 44 insertions(+), 11 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h index 8f0dd3988295..21c6bce2eb8c 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/SpaceGroupFactory.h @@ -99,18 +99,32 @@ typedef Mantid::Kernel::SingletonHolder SpaceGroupFactory } // namespace Geometry } // namespace Mantid +/* Macros for compile time space group registration + * + * The macros are a bit different than in other factories, + * because there is no identifier that can be used to generate + * a unique name for each RegistrationHelper instance. + * + * Instead, the __COUNTER__ macro is used, which is available + * in many compilers and is incremented every time it's called. + * + * Solution was found here: http://stackoverflow.com/a/1295338 + */ +#define SPGF_CONCAT_IMPL(x, y) x##y +#define SPGF_CONCAT(x, y) SPGF_CONCAT_IMPL(x, y) + #define DECLARE_GENERATED_SPACE_GROUP(number,hmSymbol,generators) \ namespace { \ - Mantid::Kernel::RegistrationHelper register_symop_##number( \ + Mantid::Kernel::RegistrationHelper SPGF_CONCAT(register_spacegroup_, __COUNTER__)( \ ((Mantid::Geometry::SpaceGroupFactory::Instance().subscribeGeneratedSpaceGroup(number, hmSymbol, generators)) \ , 0)); \ } #define DECLARE_TABULATED_SPACE_GROUP(number,hmSymbol,symmetryOperations) \ - namespace { \ - Mantid::Kernel::RegistrationHelper register_symop_##number( \ + namespace { \ +Mantid::Kernel::RegistrationHelper SPGF_CONCAT(register_spacegroup_, __COUNTER__)( \ ((Mantid::Geometry::SpaceGroupFactory::Instance().subscribeTabulatedSpaceGroup(number, hmSymbol, symmetryOperations)) \ - , 0)); \ - } +, 0)); \ +} #endif /* MANTID_GEOMETRY_SPACEGROUPFACTORY_H_ */ diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp index 1e8a0754095f..b1796bd45774 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp @@ -101,7 +101,7 @@ void SpaceGroupFactoryImpl::unsubscribeSpaceGroup(const std::string &hmSymbol) * The method will throw an exception if the number or symbol is already registered. * * [1] Shmueli, U. Acta Crystallogr. A 40, 559–567 (1984). - http://dx.doi.org/10.1107/S0108767384001161 + * http://dx.doi.org/10.1107/S0108767384001161 * * @param number :: Space group number according to International Tables for Crystallography A * @param hmSymbol :: Herrman-Mauguin symbol with upper case first letter (centering). @@ -119,6 +119,7 @@ void SpaceGroupFactoryImpl::subscribeGeneratedSpaceGroup(size_t number, const st subscribe(prototype); } +/// Subscribes a "tabulated space group" into the factory where all symmetry operations need to be supplied, including centering. void SpaceGroupFactoryImpl::subscribeTabulatedSpaceGroup(size_t number, const std::string &hmSymbol, const std::string &symmetryOperations) { throwIfSubscribed(hmSymbol); @@ -130,6 +131,7 @@ void SpaceGroupFactoryImpl::subscribeTabulatedSpaceGroup(size_t number, const st subscribe(prototype); } +/// Creatings a prototype instance of SpaceGroup using the supplied parameters. SpaceGroup_const_sptr SpaceGroupFactoryImpl::getPrototype(Group_const_sptr generatingGroup, size_t number, const std::string &hmSymbol) const { if(!generatingGroup) { @@ -139,11 +141,13 @@ SpaceGroup_const_sptr SpaceGroupFactoryImpl::getPrototype(Group_const_sptr gener return boost::make_shared(number, hmSymbol, *generatingGroup); } +/// Returns a copy-constructed instance of the supplied space group prototype object. SpaceGroup_const_sptr SpaceGroupFactoryImpl::constructFromPrototype(const SpaceGroup_const_sptr prototype) const { return boost::make_shared(*prototype); } +/// Throws std::invalid_argument if a space group with the given Hermann-Mauguin symbol is already registered in the factory. void SpaceGroupFactoryImpl::throwIfSubscribed(const std::string &hmSymbol) { if(isSubscribed(hmSymbol)) { @@ -151,12 +155,14 @@ void SpaceGroupFactoryImpl::throwIfSubscribed(const std::string &hmSymbol) } } +/// Stores the given prototype in the space group factory. void SpaceGroupFactoryImpl::subscribe(const SpaceGroup_const_sptr &prototype) { m_numberMap.insert(std::make_pair(prototype->number(), prototype->hmSymbol())); m_prototypes.insert(std::make_pair(prototype->hmSymbol(), prototype)); } +/// Returns a group with the given symmetry operations. Group_const_sptr SpaceGroupFactoryImpl::getTabulatedGroup(const std::string &symmetryOperations) const { return GroupFactory::create(symmetryOperations); @@ -167,6 +173,18 @@ std::string SpaceGroupFactoryImpl::getCenteringString(const std::string &hmSymbo return hmSymbol.substr(0, 1); } +/** + * Returns a group constructed from a generator string and a centering symbol + * + * Generators have to be provided as a semicolon separated list of symmetry operations + * in x,y,z format, for example "-x,-y,-z; -x,y,z; -y,x,z". A ProductGroup using this + * string is constructed. Centering symbol has to be supported by CenteringGroup. The + * group is then calculated as the product of these two groups. + * + * @param generators :: Semicolon separated list of symmetry operations. + * @param centeringSymbol :: Symbol for the lattice centering (see CenteringGroup). + * @return Resulting group. + */ Group_const_sptr SpaceGroupFactoryImpl::getGeneratedGroup(const std::string &generators, const std::string ¢eringSymbol) const { Group_const_sptr baseGroup = GroupFactory::create(generators); @@ -175,6 +193,7 @@ Group_const_sptr SpaceGroupFactoryImpl::getGeneratedGroup(const std::string &gen return baseGroup * centeringGroup; } +/// Constructor cannot be called, since SingletonHolder is used. SpaceGroupFactoryImpl::SpaceGroupFactoryImpl() : m_numberMap(), m_prototypes() @@ -182,11 +201,11 @@ SpaceGroupFactoryImpl::SpaceGroupFactoryImpl() : Kernel::LibraryManager::Instance(); } -DECLARE_TABULATED_SPACE_GROUP(1, "P1", "x,y,z"); +DECLARE_TABULATED_SPACE_GROUP(1, "P 1", "x,y,z"); -DECLARE_GENERATED_SPACE_GROUP(2, "P-1", "-x,-y,-z"); -DECLARE_GENERATED_SPACE_GROUP(225, "Fm-3m", "-x,-y,z; -x,y,-z; z,x,y; y,x,-z; -x,-y,-z"); -DECLARE_GENERATED_SPACE_GROUP(229, "Im-3m", "-x,-y,z; -x,y,-z; z,x,y; y,x,-z; -x,-y,-z"); +DECLARE_GENERATED_SPACE_GROUP(2, "P -1", "-x,-y,-z"); +DECLARE_GENERATED_SPACE_GROUP(225, "F m -3 m", "-x,-y,z; -x,y,-z; z,x,y; y,x,-z; -x,-y,-z"); +DECLARE_GENERATED_SPACE_GROUP(229, "I m -3 m", "-x,-y,z; -x,y,-z; z,x,y; y,x,-z; -x,-y,-z"); } // namespace Geometry } // namespace Mantid diff --git a/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h b/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h index 7709155475ab..8d5a45c30983 100644 --- a/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h +++ b/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h @@ -168,7 +168,7 @@ class SpaceGroupFactoryTest : public CxxTest::TestSuite void testSpaceGroup() { - SpaceGroup_const_sptr sgBCC = SpaceGroupFactory::Instance().createSpaceGroup("Im-3m"); + SpaceGroup_const_sptr sgBCC = SpaceGroupFactory::Instance().createSpaceGroup("I m -3 m"); std::cout << "Space group: " << sgBCC->hmSymbol() << " (" << sgBCC->number() << "):" << std::endl; std::cout << " Order: " << sgBCC->order() << std::endl; From 7ee2b583df11f883f9a767b14c3c2e01532509cf Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Thu, 9 Oct 2014 16:18:13 +0200 Subject: [PATCH 18/48] Refs #10281. Removing unused include. --- Code/Mantid/Framework/Geometry/test/SpaceGroupTest.h | 2 -- 1 file changed, 2 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/test/SpaceGroupTest.h b/Code/Mantid/Framework/Geometry/test/SpaceGroupTest.h index 861a5498309c..92aabd1b36c9 100644 --- a/Code/Mantid/Framework/Geometry/test/SpaceGroupTest.h +++ b/Code/Mantid/Framework/Geometry/test/SpaceGroupTest.h @@ -8,8 +8,6 @@ #include "MantidGeometry/Crystal/CyclicGroup.h" #include "MantidKernel/V3D.h" -#include - using namespace Mantid::Geometry; using Mantid::Kernel::V3D; From 95d3167b4f210e9784afb81f39feb6e587a373c0 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Thu, 9 Oct 2014 16:22:23 +0200 Subject: [PATCH 19/48] Refs #10281. Removed static variable from CenteringGroupCreationHelper --- .../MantidGeometry/Crystal/CenteringGroup.h | 2 -- .../Geometry/src/Crystal/CenteringGroup.cpp | 32 ++++++++++--------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h index 5088280134f7..21e58d28a278 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h @@ -92,8 +92,6 @@ class CenteringGroupCreationHelper static std::vector getFCentered(); static std::vector getRobvCentered(); static std::vector getRrevCentered(); - - static std::map m_centeringSymbolMap; }; diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp index 8fd376cf9752..d77163854c61 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp @@ -33,28 +33,30 @@ std::string CenteringGroup::getSymbol() const } /// Map between string symbols and enum-values for centering type. -std::map CenteringGroupCreationHelper::m_centeringSymbolMap = - boost::assign::map_list_of - ("P", CenteringGroup::P) - ("I", CenteringGroup::I) - ("A", CenteringGroup::A) - ("B", CenteringGroup::B) - ("C", CenteringGroup::C) - ("F", CenteringGroup::F) - ("R", CenteringGroup::Robv) - ("Robv", CenteringGroup::Robv) - ("Rrev", CenteringGroup::Rrev); /// Returns centering type enum value if centering symbol exists, throws std::invalid_argument exception otherwise. CenteringGroup::CenteringType CenteringGroupCreationHelper::getCenteringType(const std::string ¢eringSymbol) { - auto it = m_centeringSymbolMap.find(centeringSymbol); - - if(it == m_centeringSymbolMap.end()) { + std::map centeringSymbolMap = + boost::assign::map_list_of + ("P", CenteringGroup::P) + ("I", CenteringGroup::I) + ("A", CenteringGroup::A) + ("B", CenteringGroup::B) + ("C", CenteringGroup::C) + ("F", CenteringGroup::F) + ("R", CenteringGroup::Robv) + ("Robv", CenteringGroup::Robv) + ("Rrev", CenteringGroup::Rrev); + + + auto it = centeringSymbolMap.find(centeringSymbol); + + if(it == centeringSymbolMap.end()) { throw std::invalid_argument("Centering does not exist: " + centeringSymbol); } - return m_centeringSymbolMap[centeringSymbol]; + return centeringSymbolMap[centeringSymbol]; } /// Returns a vector of symmetry operations for the given centering type or throws std::invalid_argument if an invalid value is supplied. From 711e675fa753fd411ebfc0e2d395cf7b4c288cf0 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Thu, 9 Oct 2014 17:53:07 +0200 Subject: [PATCH 20/48] Refs #10281. Changed CenteringGroupCreationHelper again. --- .../MantidGeometry/Crystal/CenteringGroup.h | 28 ++++----- .../Geometry/src/Crystal/CenteringGroup.cpp | 57 ++++++++++--------- 2 files changed, 44 insertions(+), 41 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h index 21e58d28a278..a3e729bb7760 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h @@ -76,22 +76,24 @@ typedef boost::shared_ptr CenteringGroup_const_sptr; class CenteringGroupCreationHelper { public: - static CenteringGroup::CenteringType getCenteringType(const std::string ¢eringSymbol); + CenteringGroupCreationHelper(); + ~CenteringGroupCreationHelper() { } - static std::vector getSymmetryOperations(CenteringGroup::CenteringType centeringType); + CenteringGroup::CenteringType getCenteringType(const std::string ¢eringSymbol) const; -protected: - CenteringGroupCreationHelper() { } - ~CenteringGroupCreationHelper() { } + std::vector getSymmetryOperations(CenteringGroup::CenteringType centeringType) const; - static std::vector getPrimitive(); - static std::vector getBodyCentered(); - static std::vector getACentered(); - static std::vector getBCentered(); - static std::vector getCCentered(); - static std::vector getFCentered(); - static std::vector getRobvCentered(); - static std::vector getRrevCentered(); +protected: + std::vector getPrimitive() const; + std::vector getBodyCentered() const; + std::vector getACentered() const; + std::vector getBCentered() const; + std::vector getCCentered() const; + std::vector getFCentered() const; + std::vector getRobvCentered() const; + std::vector getRrevCentered() const; + + std::map m_centeringSymbolMap; }; diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp index d77163854c61..32293e24b3b2 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp @@ -14,10 +14,11 @@ CenteringGroup::CenteringGroup(const std::string ¢eringSymbol) : m_type(), m_symbol() { - m_type = CenteringGroupCreationHelper::getCenteringType(centeringSymbol); + CenteringGroupCreationHelper helper; + m_type = helper.getCenteringType(centeringSymbol); m_symbol = centeringSymbol.substr(0, 1); - setSymmetryOperations(CenteringGroupCreationHelper::getSymmetryOperations(m_type)); + setSymmetryOperations(helper.getSymmetryOperations(m_type)); } /// Returns the centering type of the group (distinguishes between Rhombohedral obverse and reverse). @@ -32,35 +33,35 @@ std::string CenteringGroup::getSymbol() const return m_symbol; } -/// Map between string symbols and enum-values for centering type. /// Returns centering type enum value if centering symbol exists, throws std::invalid_argument exception otherwise. -CenteringGroup::CenteringType CenteringGroupCreationHelper::getCenteringType(const std::string ¢eringSymbol) +CenteringGroupCreationHelper::CenteringGroupCreationHelper() : + m_centeringSymbolMap() { - std::map centeringSymbolMap = - boost::assign::map_list_of - ("P", CenteringGroup::P) - ("I", CenteringGroup::I) - ("A", CenteringGroup::A) - ("B", CenteringGroup::B) - ("C", CenteringGroup::C) - ("F", CenteringGroup::F) - ("R", CenteringGroup::Robv) - ("Robv", CenteringGroup::Robv) - ("Rrev", CenteringGroup::Rrev); - + m_centeringSymbolMap.insert(std::make_pair("P", CenteringGroup::P)); + m_centeringSymbolMap.insert(std::make_pair("I", CenteringGroup::I)); + m_centeringSymbolMap.insert(std::make_pair("A", CenteringGroup::A)); + m_centeringSymbolMap.insert(std::make_pair("B", CenteringGroup::B)); + m_centeringSymbolMap.insert(std::make_pair("C", CenteringGroup::C)); + m_centeringSymbolMap.insert(std::make_pair("F", CenteringGroup::F)); + m_centeringSymbolMap.insert(std::make_pair("R", CenteringGroup::Robv)); + m_centeringSymbolMap.insert(std::make_pair("Robv", CenteringGroup::Robv)); + m_centeringSymbolMap.insert(std::make_pair("Rrev", CenteringGroup::Rrev)); +} - auto it = centeringSymbolMap.find(centeringSymbol); +CenteringGroup::CenteringType CenteringGroupCreationHelper::getCenteringType(const std::string ¢eringSymbol) const +{ + auto it = m_centeringSymbolMap.find(centeringSymbol); - if(it == centeringSymbolMap.end()) { + if(it == m_centeringSymbolMap.end()) { throw std::invalid_argument("Centering does not exist: " + centeringSymbol); } - return centeringSymbolMap[centeringSymbol]; + return it->second; } /// Returns a vector of symmetry operations for the given centering type or throws std::invalid_argument if an invalid value is supplied. -std::vector CenteringGroupCreationHelper::getSymmetryOperations(CenteringGroup::CenteringType centeringType) +std::vector CenteringGroupCreationHelper::getSymmetryOperations(CenteringGroup::CenteringType centeringType) const { switch(centeringType) { case CenteringGroup::P: @@ -85,49 +86,49 @@ std::vector CenteringGroupCreationHelper::getSymmetryOperatio } /// Returns symmetry operations for P-centering. -std::vector CenteringGroupCreationHelper::getPrimitive() +std::vector CenteringGroupCreationHelper::getPrimitive() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z"); } /// Returns symmetry operations for I-centering. -std::vector CenteringGroupCreationHelper::getBodyCentered() +std::vector CenteringGroupCreationHelper::getBodyCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y+1/2,z+1/2"); } /// Returns symmetry operations for A-centering. -std::vector CenteringGroupCreationHelper::getACentered() +std::vector CenteringGroupCreationHelper::getACentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x,y+1/2,z+1/2"); } /// Returns symmetry operations for B-centering. -std::vector CenteringGroupCreationHelper::getBCentered() +std::vector CenteringGroupCreationHelper::getBCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y,z+1/2"); } /// Returns symmetry operations for C-centering. -std::vector CenteringGroupCreationHelper::getCCentered() +std::vector CenteringGroupCreationHelper::getCCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y+1/2,z"); } /// Returns symmetry operations for F-centering. -std::vector CenteringGroupCreationHelper::getFCentered() +std::vector CenteringGroupCreationHelper::getFCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x,y+1/2,z+1/2; x+1/2,y,z+1/2; x+1/2,y+1/2,z"); } /// Returns symmetry operations for R-centering, obverse setting. -std::vector CenteringGroupCreationHelper::getRobvCentered() +std::vector CenteringGroupCreationHelper::getRobvCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/3,y+2/3,z+2/3; x+2/3,y+1/3,z+1/3"); } /// Returns symmetry operations for R-centering, reverse setting. -std::vector CenteringGroupCreationHelper::getRrevCentered() +std::vector CenteringGroupCreationHelper::getRrevCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/3,y+2/3,z+1/3; x+2/3,y+1/3,z+2/3"); } From 589a3a86189d38e73582f6e243d03716500f26a4 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Fri, 10 Oct 2014 08:48:14 +0200 Subject: [PATCH 21/48] Refs #10281. Decided to change CenteringGroup helper once more Having a singleton is better than having a static member variable which depends on initialization order. And it's better than to fill the symbol/type map every time a centering group is constructed. --- .../MantidGeometry/Crystal/CenteringGroup.h | 15 ++++-- .../Geometry/src/Crystal/CenteringGroup.cpp | 54 +++++++++---------- 2 files changed, 39 insertions(+), 30 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h index a3e729bb7760..6d4a4862d3eb 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CenteringGroup.h @@ -5,6 +5,9 @@ #include "MantidGeometry/Crystal/Group.h" #include +#include "MantidKernel/SingletonHolder.h" + + namespace Mantid { namespace Geometry @@ -73,11 +76,10 @@ typedef boost::shared_ptr CenteringGroup_sptr; typedef boost::shared_ptr CenteringGroup_const_sptr; /// Helper class to keep this out of the interface of CenteringGroup. -class CenteringGroupCreationHelper +class MANTID_GEOMETRY_DLL CenteringGroupCreatorImpl { public: - CenteringGroupCreationHelper(); - ~CenteringGroupCreationHelper() { } + ~CenteringGroupCreatorImpl() { } CenteringGroup::CenteringType getCenteringType(const std::string ¢eringSymbol) const; @@ -92,11 +94,18 @@ class CenteringGroupCreationHelper std::vector getFCentered() const; std::vector getRobvCentered() const; std::vector getRrevCentered() const; + CenteringGroupCreatorImpl(); std::map m_centeringSymbolMap; +private: + friend struct Mantid::Kernel::CreateUsingNew; }; +#ifdef _WIN32 + template class MANTID_GEOMETRY_DLL Mantid::Kernel::SingletonHolder; +#endif +typedef Mantid::Kernel::SingletonHolder CenteringGroupCreator; } // namespace Geometry } // namespace Mantid diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp index 32293e24b3b2..e8d2ba62de0b 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/CenteringGroup.cpp @@ -14,11 +14,10 @@ CenteringGroup::CenteringGroup(const std::string ¢eringSymbol) : m_type(), m_symbol() { - CenteringGroupCreationHelper helper; - m_type = helper.getCenteringType(centeringSymbol); + m_type = CenteringGroupCreator::Instance().getCenteringType(centeringSymbol); m_symbol = centeringSymbol.substr(0, 1); - setSymmetryOperations(helper.getSymmetryOperations(m_type)); + setSymmetryOperations(CenteringGroupCreator::Instance().getSymmetryOperations(m_type)); } /// Returns the centering type of the group (distinguishes between Rhombohedral obverse and reverse). @@ -35,21 +34,7 @@ std::string CenteringGroup::getSymbol() const /// Returns centering type enum value if centering symbol exists, throws std::invalid_argument exception otherwise. -CenteringGroupCreationHelper::CenteringGroupCreationHelper() : - m_centeringSymbolMap() -{ - m_centeringSymbolMap.insert(std::make_pair("P", CenteringGroup::P)); - m_centeringSymbolMap.insert(std::make_pair("I", CenteringGroup::I)); - m_centeringSymbolMap.insert(std::make_pair("A", CenteringGroup::A)); - m_centeringSymbolMap.insert(std::make_pair("B", CenteringGroup::B)); - m_centeringSymbolMap.insert(std::make_pair("C", CenteringGroup::C)); - m_centeringSymbolMap.insert(std::make_pair("F", CenteringGroup::F)); - m_centeringSymbolMap.insert(std::make_pair("R", CenteringGroup::Robv)); - m_centeringSymbolMap.insert(std::make_pair("Robv", CenteringGroup::Robv)); - m_centeringSymbolMap.insert(std::make_pair("Rrev", CenteringGroup::Rrev)); -} - -CenteringGroup::CenteringType CenteringGroupCreationHelper::getCenteringType(const std::string ¢eringSymbol) const +CenteringGroup::CenteringType CenteringGroupCreatorImpl::getCenteringType(const std::string ¢eringSymbol) const { auto it = m_centeringSymbolMap.find(centeringSymbol); @@ -61,7 +46,7 @@ CenteringGroup::CenteringType CenteringGroupCreationHelper::getCenteringType(con } /// Returns a vector of symmetry operations for the given centering type or throws std::invalid_argument if an invalid value is supplied. -std::vector CenteringGroupCreationHelper::getSymmetryOperations(CenteringGroup::CenteringType centeringType) const +std::vector CenteringGroupCreatorImpl::getSymmetryOperations(CenteringGroup::CenteringType centeringType) const { switch(centeringType) { case CenteringGroup::P: @@ -86,52 +71,67 @@ std::vector CenteringGroupCreationHelper::getSymmetryOperatio } /// Returns symmetry operations for P-centering. -std::vector CenteringGroupCreationHelper::getPrimitive() const +std::vector CenteringGroupCreatorImpl::getPrimitive() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z"); } /// Returns symmetry operations for I-centering. -std::vector CenteringGroupCreationHelper::getBodyCentered() const +std::vector CenteringGroupCreatorImpl::getBodyCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y+1/2,z+1/2"); } /// Returns symmetry operations for A-centering. -std::vector CenteringGroupCreationHelper::getACentered() const +std::vector CenteringGroupCreatorImpl::getACentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x,y+1/2,z+1/2"); } /// Returns symmetry operations for B-centering. -std::vector CenteringGroupCreationHelper::getBCentered() const +std::vector CenteringGroupCreatorImpl::getBCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y,z+1/2"); } /// Returns symmetry operations for C-centering. -std::vector CenteringGroupCreationHelper::getCCentered() const +std::vector CenteringGroupCreatorImpl::getCCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/2,y+1/2,z"); } /// Returns symmetry operations for F-centering. -std::vector CenteringGroupCreationHelper::getFCentered() const +std::vector CenteringGroupCreatorImpl::getFCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x,y+1/2,z+1/2; x+1/2,y,z+1/2; x+1/2,y+1/2,z"); } /// Returns symmetry operations for R-centering, obverse setting. -std::vector CenteringGroupCreationHelper::getRobvCentered() const +std::vector CenteringGroupCreatorImpl::getRobvCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/3,y+2/3,z+2/3; x+2/3,y+1/3,z+1/3"); } /// Returns symmetry operations for R-centering, reverse setting. -std::vector CenteringGroupCreationHelper::getRrevCentered() const +std::vector CenteringGroupCreatorImpl::getRrevCentered() const { return SymmetryOperationFactory::Instance().createSymOps("x,y,z; x+1/3,y+2/3,z+1/3; x+2/3,y+1/3,z+2/3"); } +CenteringGroupCreatorImpl::CenteringGroupCreatorImpl() : + m_centeringSymbolMap() +{ + m_centeringSymbolMap.insert(std::make_pair("P", CenteringGroup::P)); + m_centeringSymbolMap.insert(std::make_pair("I", CenteringGroup::I)); + m_centeringSymbolMap.insert(std::make_pair("A", CenteringGroup::A)); + m_centeringSymbolMap.insert(std::make_pair("B", CenteringGroup::B)); + m_centeringSymbolMap.insert(std::make_pair("C", CenteringGroup::C)); + m_centeringSymbolMap.insert(std::make_pair("F", CenteringGroup::F)); + m_centeringSymbolMap.insert(std::make_pair("R", CenteringGroup::Robv)); + m_centeringSymbolMap.insert(std::make_pair("Robv", CenteringGroup::Robv)); + m_centeringSymbolMap.insert(std::make_pair("Rrev", CenteringGroup::Rrev)); +} + + } // namespace Geometry } // namespace Mantid From 14ce7da805cef84f2955ccea74f870acc9d780ed Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Fri, 10 Oct 2014 08:48:35 +0200 Subject: [PATCH 22/48] Refs #10281. Removing output generating unit tests --- .../Framework/Geometry/test/CyclicGroupTest.h | 40 ------------------- .../Geometry/test/SpaceGroupFactoryTest.h | 17 -------- 2 files changed, 57 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h b/Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h index 70e21bd53093..5d40bfb3142c 100644 --- a/Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h +++ b/Code/Mantid/Framework/Geometry/test/CyclicGroupTest.h @@ -55,46 +55,6 @@ class CyclicGroupTest : public CxxTest::TestSuite Group_const_sptr groupFive = groupFour * groupThree; TS_ASSERT_EQUALS(groupFive->order(), 8); } - - void testSpaceGroup() - { - // Small test, constructing Fm-3m (225) from the generators listed in ITA - Group_const_sptr group1 = GroupFactory::create("-x,-y,z"); - Group_const_sptr group2 = GroupFactory::create("-x,y,-z"); - Group_const_sptr group3 = GroupFactory::create("z,x,y"); - Group_const_sptr group4 = GroupFactory::create("y,x,-z"); - Group_const_sptr group5 = GroupFactory::create("-x,-y,-z"); - - // Make a translation group "F" - std::vector lops; - lops.push_back(SymmetryOperation("x,y,z")); - lops.push_back(SymmetryOperation("x,y+1/2,z+1/2")); - lops.push_back(SymmetryOperation("x+1/2,y+1/2,z")); - lops.push_back(SymmetryOperation("x+1/2,y,z+1/2")); - - Group_const_sptr translationGroup = boost::make_shared(lops); - - // Generate space group by multiplying the generating groups - Group_const_sptr fm3barm = group1 * group2 * group3 * group4 * group5 * translationGroup; - - // Output the symmetry operations including Centering in x,y,z-form - std::cout << std::endl; - std::cout << "Order: " << fm3barm->order() << std::endl; - std::vector ops = fm3barm->getSymmetryOperations(); - for(auto it = ops.begin(); it != ops.end(); ++it) { - std::cout << (*it).identifier() << std::endl; - } - - V3D w2a(0, 0, 0); - std::vector w2at = fm3barm * w2a; - std::cout << "Equivalents of " << w2a << ":" << std::endl; - for(auto it = w2at.begin(); it != w2at.end(); ++it) { - std::cout << " " << (*it) << std::endl; - } - } - - - }; diff --git a/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h b/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h index 8d5a45c30983..5803c6dfd543 100644 --- a/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h +++ b/Code/Mantid/Framework/Geometry/test/SpaceGroupFactoryTest.h @@ -166,23 +166,6 @@ class SpaceGroupFactoryTest : public CxxTest::TestSuite TS_ASSERT_THROWS_NOTHING(factory.unsubscribeSpaceGroup("P-1")); } - void testSpaceGroup() - { - SpaceGroup_const_sptr sgBCC = SpaceGroupFactory::Instance().createSpaceGroup("I m -3 m"); - - std::cout << "Space group: " << sgBCC->hmSymbol() << " (" << sgBCC->number() << "):" << std::endl; - std::cout << " Order: " << sgBCC->order() << std::endl; - - std::cout << " Equivalent positions:" << std::endl; - std::cout << " 96l ("; - - V3D general(0.54, 0.43, 0.12); - std::vector equivs = sgBCC->getEquivalentPositions(general); - - std::cout << equivs.size() << " equivalents)." << std::endl; - } - - private: class TestableSpaceGroupFactory : public SpaceGroupFactoryImpl { From fd637c9e7a815f287836fbe3bf609d02666f81c2 Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Fri, 10 Oct 2014 14:03:55 +0200 Subject: [PATCH 23/48] Refs #10281. Exporting SpaceGroup and Factory to python --- .../mantid/geometry/CMakeLists.txt | 2 + .../geometry/src/Exports/SpaceGroup.cpp | 58 +++++++++++++++++++ .../src/Exports/SpaceGroupFactory.cpp | 50 ++++++++++++++++ .../python/mantid/geometry/CMakeLists.txt | 1 + .../python/mantid/geometry/SpaceGroupTest.py | 31 ++++++++++ 5 files changed, 142 insertions(+) create mode 100644 Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroup.cpp create mode 100644 Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroupFactory.cpp create mode 100644 Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/SpaceGroupTest.py diff --git a/Code/Mantid/Framework/PythonInterface/mantid/geometry/CMakeLists.txt b/Code/Mantid/Framework/PythonInterface/mantid/geometry/CMakeLists.txt index 03831457f7fd..7c79f4f4afa8 100644 --- a/Code/Mantid/Framework/PythonInterface/mantid/geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/PythonInterface/mantid/geometry/CMakeLists.txt @@ -26,6 +26,8 @@ set ( EXPORT_FILES src/Exports/Object.cpp src/Exports/PointGroup.cpp src/Exports/PointGroupFactory.cpp + src/Exports/SpaceGroup.cpp + src/Exports/SpaceGroupFactory.cpp src/Exports/SymmetryOperation.cpp src/Exports/SymmetryOperationFactory.cpp ) diff --git a/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroup.cpp b/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroup.cpp new file mode 100644 index 000000000000..2980298978d9 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroup.cpp @@ -0,0 +1,58 @@ + +#include "MantidGeometry/Crystal/SpaceGroup.h" +#include "MantidPythonInterface/kernel/Converters/PyObjectToV3D.h" + +#include +#include +#include +#include +#include + +using Mantid::Geometry::SpaceGroup; +using Mantid::Geometry::SymmetryOperation; + +using namespace boost::python; + +namespace // +{ + using namespace Mantid::PythonInterface; + + boost::python::list getEquivalentPositions(SpaceGroup & self, const object& point) + { + const std::vector &equivalents = self.getEquivalentPositions(Converters::PyObjectToV3D(point)()); + + boost::python::list pythonEquivalents; + for(auto it = equivalents.begin(); it != equivalents.end(); ++it) { + pythonEquivalents.append(*it); + } + + return pythonEquivalents; + } + + std::vector getSymmetryOperationStrings(SpaceGroup & self) + { + const std::vector &symOps = self.getSymmetryOperations(); + + std::vector pythonSymOps; + for(auto it = symOps.begin(); it != symOps.end(); ++it) { + pythonSymOps.push_back((*it).identifier()); + } + + return pythonSymOps; + } + +} + +void export_SpaceGroup() +{ + register_ptr_to_python >(); + + class_("SpaceGroup", no_init) + .def("order", &SpaceGroup::order) + .def("getSymmetryOperationStrings", &getSymmetryOperationStrings) + .def("number", &SpaceGroup::number) + .def("hmSymbol", &SpaceGroup::hmSymbol) + .def("getEquivalentPositions", &getEquivalentPositions, "Returns an array with all symmetry equivalents of the supplied HKL.") + ; +} + diff --git a/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroupFactory.cpp b/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroupFactory.cpp new file mode 100644 index 000000000000..2b1e504e9099 --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroupFactory.cpp @@ -0,0 +1,50 @@ +#include "MantidGeometry/Crystal/SpaceGroupFactory.h" +#include "MantidPythonInterface/kernel/PythonObjectInstantiator.h" + +#include + +using namespace Mantid::Geometry; +using namespace boost::python; + +namespace +{ + using namespace Mantid::PythonInterface; + + std::vector allSpaceGroupSymbols(SpaceGroupFactoryImpl &self) + { + return self.subscribedSpaceGroupSymbols(); + } + + std::vector spaceGroupSymbolsForNumber(SpaceGroupFactoryImpl &self, size_t number) + { + return self.subscribedSpaceGroupSymbols(number); + } + + bool isSubscribedSymbol(SpaceGroupFactoryImpl &self, const std::string &symbol) + { + return self.isSubscribed(symbol); + } + + bool isSubscribedNumber(SpaceGroupFactoryImpl &self, size_t number) + { + return self.isSubscribed(number); + } + +} + +void export_SpaceGroupFactory() +{ + + class_("SpaceGroupFactoryImpl", no_init) + .def("isSubscribedSymbol", &isSubscribedSymbol) + .def("isSubscribedNumber", &isSubscribedNumber) + .def("createSpaceGroup", &SpaceGroupFactoryImpl::createSpaceGroup) + .def("allSubscribedSpaceGroupSymbols", &allSpaceGroupSymbols) + .def("subscribedSpaceGroupSymbols", &spaceGroupSymbolsForNumber) + .def("subscribedSpaceGroupNumbers", &SpaceGroupFactoryImpl::subscribedSpaceGroupNumbers) + .def("Instance", &SpaceGroupFactory::Instance, return_value_policy(), + "Returns a reference to the SpaceGroupFactory singleton") + .staticmethod("Instance") + ; +} + diff --git a/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/CMakeLists.txt b/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/CMakeLists.txt index a6dc06eed663..e62846514130 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/CMakeLists.txt @@ -11,6 +11,7 @@ set ( TEST_PY_FILES ReferenceFrameTest.py UnitCellTest.py PointGroupTest.py + SpaceGroupTest.py SymmetryOperationTest.py ) diff --git a/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/SpaceGroupTest.py b/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/SpaceGroupTest.py new file mode 100644 index 000000000000..db6e8a16261d --- /dev/null +++ b/Code/Mantid/Framework/PythonInterface/test/python/mantid/geometry/SpaceGroupTest.py @@ -0,0 +1,31 @@ +import unittest +from mantid.geometry import SpaceGroup, SpaceGroupFactoryImpl + +class SpaceGroupTest(unittest.TestCase): + + def test_creation(self): + self.assertRaises(ValueError, SpaceGroupFactoryImpl.Instance().createSpaceGroup, "none") + + SpaceGroupFactoryImpl.Instance().createSpaceGroup("I m -3 m") + + def test_interface(self): + spaceGroup = SpaceGroupFactoryImpl.Instance().createSpaceGroup("P -1") + self.assertEquals(spaceGroup.hmSymbol(), "P -1") + self.assertEquals(spaceGroup.order(), 2) + + symOpStrings = spaceGroup.getSymmetryOperationStrings() + + self.assertEqual(len(symOpStrings), 2) + self.assertTrue("x,y,z" in symOpStrings) + self.assertTrue("-x,-y,-z" in symOpStrings) + + def test_equivalentPositions(self): + spaceGroup = SpaceGroupFactoryImpl.Instance().createSpaceGroup("P -1") + + position = [0.34, 0.3, 0.4] + equivalentPositions = spaceGroup.getEquivalentPositions(position) + + self.assertEqual(len(equivalentPositions), 2) + +if __name__ == '__main__': + unittest.main() \ No newline at end of file From f3069b8dd7a4336106a94ece8054c26d1b64ef2d Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Fri, 10 Oct 2014 16:24:30 +0200 Subject: [PATCH 24/48] Refs #10281. Added some space groups to the factory Unfortunately I don't have time right now to add all of them, so I will make a new ticket for that. --- .../src/Crystal/SpaceGroupFactory.cpp | 34 ++++++++++++++++--- 1 file changed, 29 insertions(+), 5 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp index b1796bd45774..7e18079d91df 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp @@ -201,11 +201,35 @@ SpaceGroupFactoryImpl::SpaceGroupFactoryImpl() : Kernel::LibraryManager::Instance(); } -DECLARE_TABULATED_SPACE_GROUP(1, "P 1", "x,y,z"); - -DECLARE_GENERATED_SPACE_GROUP(2, "P -1", "-x,-y,-z"); -DECLARE_GENERATED_SPACE_GROUP(225, "F m -3 m", "-x,-y,z; -x,y,-z; z,x,y; y,x,-z; -x,-y,-z"); -DECLARE_GENERATED_SPACE_GROUP(229, "I m -3 m", "-x,-y,z; -x,y,-z; z,x,y; y,x,-z; -x,-y,-z"); +/* Space groups according to International Tables for Crystallography, + * using the generators specified there. + * + * When two origin choices are possible, only the first is given. + */ +// Triclinic +DECLARE_TABULATED_SPACE_GROUP(1, "P 1", "x,y,z") +DECLARE_GENERATED_SPACE_GROUP(2, "P -1", "-x,-y,-z") + +// Monoclinic +DECLARE_GENERATED_SPACE_GROUP(3, "P 1 2 1", "-x,y,-z") +DECLARE_GENERATED_SPACE_GROUP(4, "P 1 21 1", "-x,y+1/2,-z") +DECLARE_GENERATED_SPACE_GROUP(5, "C 1 2 1", "-x,y,-z") +DECLARE_GENERATED_SPACE_GROUP(6, "P 1 m 1", "x,-y,z") +DECLARE_GENERATED_SPACE_GROUP(7, "P 1 c 1", "x,-y,z+1/2") +DECLARE_GENERATED_SPACE_GROUP(8, "C 1 m 1", "x,-y,z") +DECLARE_GENERATED_SPACE_GROUP(9, "C 1 c 1", "x,-y,z+1/2") +DECLARE_GENERATED_SPACE_GROUP(10, "P 1 2/m 1", "-x,y,-z; -x,-y,-z") +DECLARE_GENERATED_SPACE_GROUP(11, "P 1 21/m 1", "-x,y+1/2,-z; -x,-y,-z") +DECLARE_GENERATED_SPACE_GROUP(12, "C 1 2/m 1", "-x,y,-z; -x,-y,-z") +DECLARE_GENERATED_SPACE_GROUP(13, "P 1 2/c 1", "-x,y,-z+1/2; -x,-y,-z") +DECLARE_GENERATED_SPACE_GROUP(14, "P 1 21/c 1", "-x,y+1/2,-z+1/2; -x,-y,-z") +DECLARE_GENERATED_SPACE_GROUP(15, "C 1 2/c 1", "-x,y,-z+1/2; -x,-y,-z") + +DECLARE_GENERATED_SPACE_GROUP(194, "P 63/m m c", "-y,x-y,z; -x,-y,z+1/2; y,x,-z; -x,-y,-z") + +DECLARE_GENERATED_SPACE_GROUP(221, "P m -3 m", "-x,-y,z; -x,y,-z; z,x,y; y,x,-z; -x,-y,-z") +DECLARE_GENERATED_SPACE_GROUP(225, "F m -3 m", "-x,-y,z; -x,y,-z; z,x,y; y,x,-z; -x,-y,-z") +DECLARE_GENERATED_SPACE_GROUP(229, "I m -3 m", "-x,-y,z; -x,y,-z; z,x,y; y,x,-z; -x,-y,-z") } // namespace Geometry } // namespace Mantid From 0dfe66e71d65b7996021ee4860bf94489bb781cb Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Fri, 10 Oct 2014 16:39:40 +0200 Subject: [PATCH 25/48] Refs #10281. Forgot const pointer export problem. --- .../mantid/geometry/src/Exports/SpaceGroup.cpp | 2 +- .../mantid/geometry/src/Exports/SpaceGroupFactory.cpp | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroup.cpp b/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroup.cpp index 2980298978d9..442a4783d4e0 100644 --- a/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroup.cpp +++ b/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroup.cpp @@ -45,7 +45,7 @@ namespace // void export_SpaceGroup() { - register_ptr_to_python >(); + register_ptr_to_python >(); class_("SpaceGroup", no_init) .def("order", &SpaceGroup::order) diff --git a/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroupFactory.cpp b/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroupFactory.cpp index 2b1e504e9099..de61f14cbef4 100644 --- a/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroupFactory.cpp +++ b/Code/Mantid/Framework/PythonInterface/mantid/geometry/src/Exports/SpaceGroupFactory.cpp @@ -30,6 +30,13 @@ namespace return self.isSubscribed(number); } + SpaceGroup_sptr createSpaceGroup(SpaceGroupFactoryImpl &self, const std::string &symbol) + { + SpaceGroup_const_sptr spaceGroup = self.createSpaceGroup(symbol); + + return boost::const_pointer_cast(spaceGroup); + } + } void export_SpaceGroupFactory() @@ -38,7 +45,7 @@ void export_SpaceGroupFactory() class_("SpaceGroupFactoryImpl", no_init) .def("isSubscribedSymbol", &isSubscribedSymbol) .def("isSubscribedNumber", &isSubscribedNumber) - .def("createSpaceGroup", &SpaceGroupFactoryImpl::createSpaceGroup) + .def("createSpaceGroup", &createSpaceGroup) .def("allSubscribedSpaceGroupSymbols", &allSpaceGroupSymbols) .def("subscribedSpaceGroupSymbols", &spaceGroupSymbolsForNumber) .def("subscribedSpaceGroupNumbers", &SpaceGroupFactoryImpl::subscribedSpaceGroupNumbers) From bede8f882d0810bf8fc06add8164ec414627dc70 Mon Sep 17 00:00:00 2001 From: Ian Bush Date: Wed, 15 Oct 2014 14:35:46 +0100 Subject: [PATCH 26/48] Refs #10370 Added method to MDBox to reserve memory for events. MergeMDFiles uses this method to reserve memory for events in an MDBox object. Removed reserve in loadAndAddFrom method on MDBox (could this have a detrimental effect on LoadMD?). --- .../Framework/API/inc/MantidAPI/IMDNode.h | 1 + .../MDAlgorithms/src/MergeMDFiles.cpp | 19 +++++++++++++------ .../MDEvents/inc/MantidMDEvents/MDBox.h | 1 + .../MDEvents/inc/MantidMDEvents/MDGridBox.h | 2 ++ Code/Mantid/Framework/MDEvents/src/MDBox.cpp | 18 +++++++++++++----- .../Framework/MDEvents/test/MDBoxBaseTest.h | 1 + 6 files changed, 31 insertions(+), 11 deletions(-) diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/IMDNode.h b/Code/Mantid/Framework/API/inc/MantidAPI/IMDNode.h index 5cea41bcf918..9205f66d1869 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/IMDNode.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/IMDNode.h @@ -71,6 +71,7 @@ class IMDNode *@param loadFileData -- if true, the data on HDD and not yet in memory are loaded into memory before deleting fileBacked information, if false, all on HDD contents are discarded, which can break the data integrity (used by destructor) */ virtual void clearFileBacked(bool loadFileData)=0; + virtual void reserveMemoryForLoad(uint64_t)=0; /**Save the box at specific disk position using the class, respoinsible for the file IO. */ virtual void saveAt(API::IBoxControllerIO *const /*saver */, uint64_t /*position*/)const=0; diff --git a/Code/Mantid/Framework/MDAlgorithms/src/MergeMDFiles.cpp b/Code/Mantid/Framework/MDAlgorithms/src/MergeMDFiles.cpp index ce5f98266480..2798ea8d4b2d 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/MergeMDFiles.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/MergeMDFiles.cpp @@ -159,17 +159,24 @@ namespace MDAlgorithms TargetBox->clear(); uint64_t nBoxEvents(0); + std::vector numFileEvents(m_EventLoader.size()); + for (size_t iw=0; iwm_EventLoader.size(); iw++) { size_t ID = TargetBox->getID(); + numFileEvents[iw] = static_cast(m_fileComponentsStructure[iw].getEventIndex()[2*ID+1]); + nBoxEvents += numFileEvents[iw]; + } - uint64_t fileLocation = m_fileComponentsStructure[iw].getEventIndex()[2*ID+0]; - size_t numFileEvents = static_cast(m_fileComponentsStructure[iw].getEventIndex()[2*ID+1]); - if(numFileEvents==0)continue; - //TODO: it is possible to avoid the reallocation of the memory at each load - TargetBox->loadAndAddFrom(m_EventLoader[iw],fileLocation,numFileEvents); + // At this point memory required is known, so it is reserved all in one go + TargetBox->reserveMemoryForLoad(nBoxEvents); - nBoxEvents += numFileEvents; + for (size_t iw=0; iwm_EventLoader.size(); iw++) + { + size_t ID = TargetBox->getID(); + uint64_t fileLocation = m_fileComponentsStructure[iw].getEventIndex()[2*ID+0]; + if(numFileEvents[iw]==0) continue; + TargetBox->loadAndAddFrom(m_EventLoader[iw],fileLocation,numFileEvents[iw]); } return nBoxEvents; diff --git a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDBox.h b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDBox.h index 347e37008629..ea9bdc625d29 100644 --- a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDBox.h +++ b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDBox.h @@ -68,6 +68,7 @@ namespace MDEvents //----------------------------------------------------------------------------------------------- virtual void saveAt(API::IBoxControllerIO *const /* */, uint64_t /*position*/)const; virtual void loadAndAddFrom(API::IBoxControllerIO *const /* */, uint64_t /*position*/, size_t /* Size */); + virtual void reserveMemoryForLoad(uint64_t /* Size */); /**drop events data from memory but keep averages (and file-backed info) */ void clearDataFromMemory(); /** */ diff --git a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h index 0058be49fdf2..4d5f63be210c 100644 --- a/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h +++ b/Code/Mantid/Framework/MDEvents/inc/MantidMDEvents/MDGridBox.h @@ -67,6 +67,8 @@ namespace MDEvents /**Load the box data of specified size from the disk location provided using the class, respoinsible for the file IO. */ virtual void loadAndAddFrom(API::IBoxControllerIO *const /* */, uint64_t /*position*/, size_t /* Size */) {/*Not directly loadable */} + virtual void reserveMemoryForLoad(uint64_t /* Size */) + {/*Not directly loadable */} //------------------------------------------------------------------------------------------------------- /** Uses the cached value of points stored in the grid box diff --git a/Code/Mantid/Framework/MDEvents/src/MDBox.cpp b/Code/Mantid/Framework/MDEvents/src/MDBox.cpp index 0860428cd616..ece7dfc1309f 100644 --- a/Code/Mantid/Framework/MDEvents/src/MDBox.cpp +++ b/Code/Mantid/Framework/MDEvents/src/MDBox.cpp @@ -900,9 +900,20 @@ namespace MDEvents FileSaver->saveBlock(TabledData,position); } + + /** + * Reserve all the memory required for loading in one step. + * + * @param size -- number of events to reserve for + */ + TMDE( + void MDBox)::reserveMemoryForLoad(uint64_t size) + { + this->data.reserve(size); + } + /**Load the box data of specified size from the disk location provided using the class, respoinsible for the file IO and append them to exisiting events - * Clear events vector first if overwriting the exisitng events is necessary. The efficiency would be higher if jentle cleaning occurs (the size of event data vector - is nullified but memory still allocated) + * Clear events vector first if overwriting the exisitng events is necessary. * * @param FileSaver -- the pointer to the class, responsible for file IO * @param filePosition -- the place in the direct access file, where necessary data are located @@ -923,9 +934,6 @@ namespace MDEvents std::vector TableData; FileSaver->loadBlock(TableData,filePosition,nEvents); - // convert loaded events to data; - size_t nCurrentEvents = data.size(); - this->data.reserve(nCurrentEvents+nEvents); // convert data to events appending new events to existing MDE::dataToEvents(TableData,data,false); diff --git a/Code/Mantid/Framework/MDEvents/test/MDBoxBaseTest.h b/Code/Mantid/Framework/MDEvents/test/MDBoxBaseTest.h index 14c4a60f1ac7..44733b33da4b 100644 --- a/Code/Mantid/Framework/MDEvents/test/MDBoxBaseTest.h +++ b/Code/Mantid/Framework/MDEvents/test/MDBoxBaseTest.h @@ -49,6 +49,7 @@ class MDBoxBaseTester : public MDBoxBase void setFileBacked(){}; void saveAt(API::IBoxControllerIO *const /* */, uint64_t /*position*/)const{/*Not saveable */}; void loadAndAddFrom(API::IBoxControllerIO *const /* */, uint64_t /*position*/, size_t /* Size */){}; + void reserveMemoryForLoad(uint64_t /* Size */){}; // regardless of what is actually instantiated, base tester would call itself gridbox bool isBox()const{return false;} From c01e4f831e1f58d4638c5a786e438cb536d3eac2 Mon Sep 17 00:00:00 2001 From: Ian Bush Date: Wed, 15 Oct 2014 14:40:19 +0100 Subject: [PATCH 27/48] Refs #10370 Added unit test for ReserveMemoryForLoad. --- Code/Mantid/Framework/MDEvents/test/MDBoxTest.h | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Code/Mantid/Framework/MDEvents/test/MDBoxTest.h b/Code/Mantid/Framework/MDEvents/test/MDBoxTest.h index 1cf9119bce53..136448053eaa 100644 --- a/Code/Mantid/Framework/MDEvents/test/MDBoxTest.h +++ b/Code/Mantid/Framework/MDEvents/test/MDBoxTest.h @@ -629,6 +629,15 @@ static void destroySuite(MDBoxTest * suite) { delete suite; } TS_ASSERT_THROWS_NOTHING(box.unmask()); TSM_ASSERT("Should have been masked.", !box.getIsMasked()); } + + void test_reserve() + { + BoxController_sptr sc( new BoxController(2)); + MDBox,2> b(sc.get()); + + b.reserveMemoryForLoad(3); + TS_ASSERT_EQUALS( b.getEvents().capacity(), 3); + } }; From 7127f5ac56c4f9dee5764a55b28ab3c0d3c47026 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Wed, 15 Oct 2014 16:05:27 +0100 Subject: [PATCH 28/48] Refs #10301 Change Refl UI scale column type to double --- .../DataHandling/src/LoadReflTBL.cpp | 16 ++++++--- .../DataHandling/src/SaveReflTBL.cpp | 6 ++-- .../DataHandling/test/LoadReflTBLTest.h | 12 +++---- .../DataHandling/test/SaveReflTBLTest.h | 34 +++++++++---------- .../CustomInterfaces/src/QReflTableModel.cpp | 15 ++++---- .../src/ReflBlankMainViewPresenter.cpp | 2 +- .../src/ReflLoadedMainViewPresenter.cpp | 2 +- .../test/ReflLoadedMainViewPresenterTest.h | 10 +++--- 8 files changed, 54 insertions(+), 43 deletions(-) diff --git a/Code/Mantid/Framework/DataHandling/src/LoadReflTBL.cpp b/Code/Mantid/Framework/DataHandling/src/LoadReflTBL.cpp index 9c79afc15a11..025bdbd31917 100644 --- a/Code/Mantid/Framework/DataHandling/src/LoadReflTBL.cpp +++ b/Code/Mantid/Framework/DataHandling/src/LoadReflTBL.cpp @@ -291,7 +291,7 @@ namespace Mantid auto colQmin = ws->addColumn("str","Qmin"); auto colQmax = ws->addColumn("str","Qmax"); auto colDqq = ws->addColumn("str","dq/q"); - auto colScale = ws->addColumn("str","Scale"); + auto colScale = ws->addColumn("double","Scale"); auto colStitch = ws->addColumn("int","StitchGroup"); auto colOptions = ws->addColumn("str","Options"); @@ -317,6 +317,11 @@ namespace Mantid } getCells(line, columns); + const std::string scaleStr = columns.at(16); + double scale = 1.0; + if(!scaleStr.empty()) + Mantid::Kernel::Strings::convert(columns.at(16), scale); + //check if the first run in the row has any data associated with it // 0 = runs, 1 = theta, 2 = trans, 3 = qmin, 4 = qmax if (columns[0] != "" || columns[1] != "" || columns[2] != "" || columns[3] != "" || columns[4] != "") @@ -327,7 +332,7 @@ namespace Mantid row << columns.at(i); } row << columns.at(15); - row << columns.at(16); + row << scale; row << stitchID; } @@ -341,7 +346,7 @@ namespace Mantid row << columns.at(i); } row << columns.at(15); - row << columns.at(16); + row << scale; row << stitchID; } @@ -352,7 +357,10 @@ namespace Mantid TableRow row = ws->appendRow(); for (int i = 10; i < 17; ++i) { - row << columns.at(i); + if(i == 16) + row << scale; + else + row << columns.at(i); } row << stitchID; } diff --git a/Code/Mantid/Framework/DataHandling/src/SaveReflTBL.cpp b/Code/Mantid/Framework/DataHandling/src/SaveReflTBL.cpp index 5b623ef210e4..5642859373ec 100644 --- a/Code/Mantid/Framework/DataHandling/src/SaveReflTBL.cpp +++ b/Code/Mantid/Framework/DataHandling/src/SaveReflTBL.cpp @@ -111,7 +111,8 @@ namespace Mantid //now add dq/q and scale from the first row in the group TableRow row = ws->getRow(rowNos[0]); writeVal(row.cell(5),file); - writeVal(row.cell(6),file, false, true); + std::string scaleStr = boost::lexical_cast(row.cell(6)); + writeVal(scaleStr, file, false, true); } //now do the same for the ungrouped @@ -130,7 +131,8 @@ namespace Mantid } //now add dq/q and scale writeVal(row.cell(5),file); - writeVal(row.cell(6),file, false, true); + std::string scaleStr = boost::lexical_cast(row.cell(6)); + writeVal(scaleStr, file, false, true); } file.close(); } diff --git a/Code/Mantid/Framework/DataHandling/test/LoadReflTBLTest.h b/Code/Mantid/Framework/DataHandling/test/LoadReflTBLTest.h index 46012aaeec33..48d189156ed6 100644 --- a/Code/Mantid/Framework/DataHandling/test/LoadReflTBLTest.h +++ b/Code/Mantid/Framework/DataHandling/test/LoadReflTBLTest.h @@ -65,7 +65,7 @@ class LoadReflTBLTest : public CxxTest::TestSuite TS_ASSERT_DELTA(boost::lexical_cast(row.cell(3)),0.01,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(4)),0.06,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(5)),0.04,0.001); - TS_ASSERT_DELTA(boost::lexical_cast(row.cell(6)),2,0.01); + TS_ASSERT_DELTA(row.cell(6),2,0.01); TS_ASSERT_EQUALS(row.cell(7),1); row = outputWS->getRow(1); @@ -75,7 +75,7 @@ class LoadReflTBLTest : public CxxTest::TestSuite TS_ASSERT_DELTA(boost::lexical_cast(row.cell(3)),0.01,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(4)),0.06,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(5)),0.04,0.001); - TS_ASSERT_DELTA(boost::lexical_cast(row.cell(6)),2,0.01); + TS_ASSERT_DELTA(row.cell(6),2,0.01); TS_ASSERT_EQUALS(row.cell(7),2); row = outputWS->getRow(2); @@ -85,7 +85,7 @@ class LoadReflTBLTest : public CxxTest::TestSuite TS_ASSERT_DELTA(boost::lexical_cast(row.cell(3)),0.035,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(4)),0.3,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(5)),0.04,0.001); - TS_ASSERT_DELTA(boost::lexical_cast(row.cell(6)),2,0.01); + TS_ASSERT_DELTA(row.cell(6),2,0.01); TS_ASSERT_EQUALS(row.cell(7),2); cleanupafterwards(); @@ -128,7 +128,7 @@ class LoadReflTBLTest : public CxxTest::TestSuite TS_ASSERT_DELTA(boost::lexical_cast(row.cell(3)),0.01,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(4)),0.06,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(5)),0.04,0.001); - TS_ASSERT_DELTA(boost::lexical_cast(row.cell(6)),2,0.01); + TS_ASSERT_DELTA(row.cell(6),2,0.01); TS_ASSERT_EQUALS(row.cell(7),1); row = outputWS->getRow(1); @@ -138,7 +138,7 @@ class LoadReflTBLTest : public CxxTest::TestSuite TS_ASSERT_DELTA(boost::lexical_cast(row.cell(3)),0.01,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(4)),0.06,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(5)),0.04,0.001); - TS_ASSERT_DELTA(boost::lexical_cast(row.cell(6)),2,0.01); + TS_ASSERT_DELTA(row.cell(6),2,0.01); TS_ASSERT_EQUALS(row.cell(7),2); row = outputWS->getRow(2); @@ -148,7 +148,7 @@ class LoadReflTBLTest : public CxxTest::TestSuite TS_ASSERT_DELTA(boost::lexical_cast(row.cell(3)),0.035,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(4)),0.3,0.001); TS_ASSERT_DELTA(boost::lexical_cast(row.cell(5)),0.04,0.001); - TS_ASSERT_DELTA(boost::lexical_cast(row.cell(6)),2,0.01); + TS_ASSERT_DELTA(row.cell(6),2,0.01); TS_ASSERT_EQUALS(row.cell(7),2); cleanupafterwards(); diff --git a/Code/Mantid/Framework/DataHandling/test/SaveReflTBLTest.h b/Code/Mantid/Framework/DataHandling/test/SaveReflTBLTest.h index ea927d347441..e9f376d78cfa 100644 --- a/Code/Mantid/Framework/DataHandling/test/SaveReflTBLTest.h +++ b/Code/Mantid/Framework/DataHandling/test/SaveReflTBLTest.h @@ -69,10 +69,10 @@ class SaveReflTBLTest : public CxxTest::TestSuite ITableWorkspace_sptr ws = CreateWorkspace(); TableRow row = ws->appendRow(); - row << "13460" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << "2" << 4; + row << "13460" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 2.0 << 4; row = ws->appendRow(); - row << "13470" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << "2" << 5; + row << "13470" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 2.0 << 5; Mantid::API::IAlgorithm_sptr alg = Mantid::API::AlgorithmManager::Instance().create("SaveReflTBL"); alg->setRethrows(true); @@ -111,7 +111,7 @@ class SaveReflTBLTest : public CxxTest::TestSuite ITableWorkspace_sptr ws = CreateWorkspace(); TableRow row = ws->appendRow(); - row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << "2" << 1; + row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << 2.0 << 1; Mantid::API::IAlgorithm_sptr alg = Mantid::API::AlgorithmManager::Instance().create("SaveReflTBL"); alg->setRethrows(true); @@ -135,7 +135,7 @@ class SaveReflTBLTest : public CxxTest::TestSuite auto colQmin = ws->addColumn("str","Qmin"); auto colQmax = ws->addColumn("str","Qmax"); auto colDqq = ws->addColumn("str","dq/q"); - auto colScale = ws->addColumn("str","Scale"); + auto colScale = ws->addColumn("double","Scale"); colRuns->setPlotType(0); colTheta->setPlotType(0); @@ -146,13 +146,13 @@ class SaveReflTBLTest : public CxxTest::TestSuite colScale->setPlotType(0); TableRow row = ws->appendRow(); - row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << "2"; + row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << 2.0; row = ws->appendRow(); - row << "13462" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << "2"; + row << "13462" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << 2.0; row = ws->appendRow(); - row << "13470" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << "2"; + row << "13470" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << 2.0; Mantid::API::IAlgorithm_sptr alg = Mantid::API::AlgorithmManager::Instance().create("SaveReflTBL"); alg->setRethrows(true); @@ -214,7 +214,7 @@ class SaveReflTBLTest : public CxxTest::TestSuite auto colQmin = ws->addColumn("str","Qmin"); auto colQmax = ws->addColumn("str","Qmax"); auto colDqq = ws->addColumn("str","dq/q"); - auto colScale = ws->addColumn("str","Scale"); + auto colScale = ws->addColumn("double","Scale"); auto colStitch = ws->addColumn("int","StitchGroup"); colRuns->setPlotType(0); @@ -227,32 +227,32 @@ class SaveReflTBLTest : public CxxTest::TestSuite colStitch->setPlotType(0); TableRow row = ws->appendRow(); - row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << "2" << 1; + row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << 2.0 << 1; row = ws->appendRow(); - row << "13462" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << "2" << 1; + row << "13462" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << 2.0 << 1; row = ws->appendRow(); - row << "13470" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << "2" << 1; + row << "13470" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << 2.0 << 1; row = ws->appendRow(); - row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << "2" << 2; + row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << 2.0 << 2; row = ws->appendRow(); - row << "13462" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << "2" << 2; + row << "13462" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << 2.0 << 2; row = ws->appendRow(); - row << "13470" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << "2" << 3; + row << "13470" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << 2.0 << 3; row = ws->appendRow(); - row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << "2" << 0; + row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << 2.0 << 0; //this row's last two cells will show in the tableworkspace, but the first row in stich group 3's will take priority when saving row = ws->appendRow(); - row << "13462" << "2.3" << "13463" << "0.035" << "0.3" << "0.4" << "3" << 3; + row << "13462" << "2.3" << "13463" << "0.035" << "0.3" << "0.4" << 3.0 << 3; row = ws->appendRow(); - row << "13470" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << "2" << 4; + row << "13470" << "2.3" << "13463" << "0.035" << "0.3" << "0.04" << 2.0 << 4; return ws; } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/QReflTableModel.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/QReflTableModel.cpp index 03e6e8548246..afd0cee4c9ca 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/QReflTableModel.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/QReflTableModel.cpp @@ -83,7 +83,7 @@ namespace MantidQt m_dataCache.push_back(QString::fromStdString(tableRow.cell(COL_QMIN))); m_dataCache.push_back(QString::fromStdString(tableRow.cell(COL_QMAX))); m_dataCache.push_back(QString::fromStdString(tableRow.cell(COL_DQQ))); - m_dataCache.push_back(QString::fromStdString(tableRow.cell(COL_SCALE))); + m_dataCache.push_back(QString::number(tableRow.cell(COL_SCALE))); m_dataCache.push_back(QString::number(tableRow.cell(COL_GROUP))); m_dataCache.push_back(QString::fromStdString(tableRow.cell(COL_OPTIONS))); @@ -167,13 +167,14 @@ namespace MantidQt const int colNumber = index.column(); const int rowNumber = index.row(); - if (colNumber == COL_GROUP) + switch(colNumber) { - m_tWS->Int(rowNumber, COL_GROUP) = str.toInt(); - } - else - { - m_tWS->String(rowNumber, colNumber) = str.toStdString(); + case COL_GROUP: + m_tWS->Int(rowNumber, COL_GROUP) = str.toInt(); break; + case COL_SCALE: + m_tWS->Double(rowNumber, COL_SCALE) = str.toDouble(); break; + default: + m_tWS->String(rowNumber, colNumber) = str.toStdString(); break; } invalidateDataCache(rowNumber); diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflBlankMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflBlankMainViewPresenter.cpp index 26d1da245c7d..10e1bf2f5887 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflBlankMainViewPresenter.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflBlankMainViewPresenter.cpp @@ -13,7 +13,7 @@ namespace auto colQmin = ws->addColumn("str","Qmin"); auto colQmax = ws->addColumn("str","Qmax"); auto colDqq = ws->addColumn("str","dq/q"); - auto colScale = ws->addColumn("str","Scale"); + auto colScale = ws->addColumn("double","Scale"); auto colStitch = ws->addColumn("int","StitchGroup"); auto colOptions = ws->addColumn("str","Options"); diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflLoadedMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflLoadedMainViewPresenter.cpp index 5a2795a76c72..e2e798cfdb29 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflLoadedMainViewPresenter.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflLoadedMainViewPresenter.cpp @@ -18,7 +18,7 @@ namespace model->String(0,3); model->String(0,4); model->String(0,5); - model->String(0,6); + model->Double(0,6); model->Int(0,7); model->String(0,8); } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h index d011cf5b5fdc..5a74e69b1697 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h @@ -37,7 +37,7 @@ class ReflLoadedMainViewPresenterTest : public CxxTest::TestSuite auto colQmin = ws->addColumn("str","Qmin"); auto colQmax = ws->addColumn("str","Qmax"); auto colDqq = ws->addColumn("str","dq/q"); - auto colScale = ws->addColumn("str","Scale"); + auto colScale = ws->addColumn("double","Scale"); auto colStitch = ws->addColumn("int","StitchGroup"); auto colOptions = ws->addColumn("str","Options"); @@ -62,13 +62,13 @@ class ReflLoadedMainViewPresenterTest : public CxxTest::TestSuite auto ws = createWorkspace(wsName); TableRow row = ws->appendRow(); - row << "13460" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << "1" << 3 << ""; + row << "13460" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 1.0 << 3 << ""; row = ws->appendRow(); - row << "13462" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << "1" << 3 << ""; + row << "13462" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 1.0 << 3 << ""; row = ws->appendRow(); - row << "13469" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << "1" << 1 << ""; + row << "13469" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 1.0 << 1 << ""; row = ws->appendRow(); - row << "13470" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << "1" << 1 << ""; + row << "13470" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 1.0 << 1 << ""; return ws; } From 4235aedfaea94ac6adc86d0db87d0eedd176fa49 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Wed, 15 Oct 2014 16:14:50 +0100 Subject: [PATCH 29/48] Refs #10301 Fix broken unit tests --- .../test/ReflLoadedMainViewPresenterTest.h | 10 +++++----- .../CustomInterfaces/test/ReflMainViewMockObjects.h | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h index 5a74e69b1697..85b3847dc682 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h @@ -77,7 +77,7 @@ class ReflLoadedMainViewPresenterTest : public CxxTest::TestSuite ITableWorkspace_sptr ws = createWorkspace(); TableRow row = ws->appendRow(); - row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << "2" << "1" << ""; + row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << 2.0 << 1 << ""; return ws; } @@ -487,9 +487,9 @@ class ReflLoadedMainViewPresenterTest : public CxxTest::TestSuite { auto ws = createWorkspace("TestWorkspace"); TableRow row = ws->appendRow(); - row << "dataA" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << "1" << 1; + row << "dataA" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 1.0 << 1; row = ws->appendRow(); - row << "dataB" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << "1" << 1; + row << "dataB" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 1.0 << 1; loadWorkspace("INTER13460", "dataA"); loadWorkspace("INTER13462", "dataB"); @@ -547,9 +547,9 @@ class ReflLoadedMainViewPresenterTest : public CxxTest::TestSuite auto ws = createWorkspace("TestWorkspace"); //Autofill everything we can TableRow row = ws->appendRow(); - row << "13460" << "" << "13463,13464" << "" << "" << "" << "1" << 1; + row << "13460" << "" << "13463,13464" << "" << "" << "" << 1.0 << 1; row = ws->appendRow(); - row << "13462" << "" << "13463,13464" << "" << "" << "" << "1" << 1; + row << "13462" << "" << "13463,13464" << "" << "" << "" << 1.0 << 1; MockView mockView; ReflLoadedMainViewPresenter presenter(ws,&mockView); diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h index fbf4504534cc..312289383f2c 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h @@ -46,13 +46,13 @@ class MockView : public ReflMainView void addDataForTest() { TableRow row = m_model->appendRow(); - row << "13460" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << "1" << 3 << ""; + row << "13460" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 1.0 << 3 << ""; row = m_model->appendRow(); - row << "13462" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << "1" << 3 << ""; + row << "13462" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 1.0 << 3 << ""; row = m_model->appendRow(); - row << "13469" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << "1" << 1 << ""; + row << "13469" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 1.0 << 1 << ""; row = m_model->appendRow(); - row << "13470" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << "1" << 1 << ""; + row << "13470" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 1.0 << 1 << ""; m_model->removeRow(0); } private: From c63a2a2144232cf8e1a9354fc2e8e00e3d9abcd3 Mon Sep 17 00:00:00 2001 From: Ian Bush Date: Thu, 16 Oct 2014 11:57:47 +0100 Subject: [PATCH 30/48] Refs #10370 Reserve memory again in LoadMD. Changes to loadAndAddFrom in MDBox meant memory is no longer reserved in LoadMD, this fixes the issue. Note the performance gain for this, if any, appears marginal. --- Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp b/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp index 5b5d3b02c1d2..e9736c335496 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp @@ -366,7 +366,10 @@ namespace Mantid if(!box)continue; if(BoxEventIndex[2*i+1]>0) // Load in memory NOT using the file as the back-end, + { + boxTree[i]->reserveMemoryForLoad(BoxEventIndex[2*i+1]); boxTree[i]->loadAndAddFrom(loader.get(),BoxEventIndex[2*i],static_cast(BoxEventIndex[2*i+1])); + } } loader->closeFile(); From d3ab94f36cdb98de56c909a9362ca8616cb1ef1d Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Thu, 16 Oct 2014 12:23:52 +0100 Subject: [PATCH 31/48] Refs #10378 Minor Refl UI Improvements * Select entire rows by default * Allow search panel to be collapsed * Reduce the minimum size where possible * Expand options column to fill whitespace --- .../MantidQtCustomInterfaces/ReflMainWidget.ui | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainWidget.ui b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainWidget.ui index 920cdab16dbe..beb3efec0c0b 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainWidget.ui +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainWidget.ui @@ -48,7 +48,7 @@ Qt::Horizontal - false + true @@ -286,7 +286,7 @@ - + 0 0 @@ -329,12 +329,18 @@ QAbstractItemView::ContiguousSelection + + QAbstractItemView::SelectRows + 60 20 + + true + 20 @@ -384,6 +390,12 @@ + + + 0 + 0 + + Select the instrument to assume for fetching runs From 394251df3b0f6d395b63b8a89d38d6a217b4a38a Mon Sep 17 00:00:00 2001 From: Michael Wedel Date: Thu, 16 Oct 2014 17:05:36 +0200 Subject: [PATCH 32/48] Refs #10281. Renamed ProductGroup to ProductOfCyclicGroups. Following Anders' suggestion to name the class after what it actually does. --- Code/Mantid/Framework/Geometry/CMakeLists.txt | 6 +-- .../inc/MantidGeometry/Crystal/CyclicGroup.h | 2 +- .../inc/MantidGeometry/Crystal/Group.h | 2 +- ...ProductGroup.h => ProductOfCyclicGroups.h} | 18 ++++----- ...uctGroup.cpp => ProductOfCyclicGroups.cpp} | 16 ++++---- .../src/Crystal/SpaceGroupFactory.cpp | 6 +-- ...roupTest.h => ProductOfCyclicGroupsTest.h} | 38 +++++++++---------- 7 files changed, 44 insertions(+), 44 deletions(-) rename Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/{ProductGroup.h => ProductOfCyclicGroups.h} (81%) rename Code/Mantid/Framework/Geometry/src/Crystal/{ProductGroup.cpp => ProductOfCyclicGroups.cpp} (66%) rename Code/Mantid/Framework/Geometry/test/{ProductGroupTest.h => ProductOfCyclicGroupsTest.h} (60%) diff --git a/Code/Mantid/Framework/Geometry/CMakeLists.txt b/Code/Mantid/Framework/Geometry/CMakeLists.txt index d75af6e00620..c10c32bdc3ad 100644 --- a/Code/Mantid/Framework/Geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/Geometry/CMakeLists.txt @@ -10,7 +10,7 @@ set ( SRC_FILES src/Crystal/OrientedLattice.cpp src/Crystal/PointGroup.cpp src/Crystal/PointGroupFactory.cpp - src/Crystal/ProductGroup.cpp + src/Crystal/ProductOfCyclicGroups.cpp src/Crystal/ReducedCell.cpp src/Crystal/ReflectionCondition.cpp src/Crystal/ScalarUtils.cpp @@ -121,7 +121,7 @@ set ( INC_FILES inc/MantidGeometry/Crystal/OrientedLattice.h inc/MantidGeometry/Crystal/PointGroup.h inc/MantidGeometry/Crystal/PointGroupFactory.h - inc/MantidGeometry/Crystal/ProductGroup.h + inc/MantidGeometry/Crystal/ProductOfCyclicGroups.h inc/MantidGeometry/Crystal/ReducedCell.h inc/MantidGeometry/Crystal/ReflectionCondition.h inc/MantidGeometry/Crystal/ScalarUtils.h @@ -275,7 +275,7 @@ set ( TEST_FILES PointGroupFactoryTest.h PointGroupTest.h PolygonEdgeTest.h - ProductGroupTest.h + ProductOfCyclicGroupsTest.h QuadrilateralTest.h RectangularDetectorPixelTest.h RectangularDetectorTest.h diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h index 7cece6f8f7b6..68a60fe9fa88 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/CyclicGroup.h @@ -49,7 +49,7 @@ namespace Geometry This is much more convenient than having to construct a Group, where all four symmetry operations would have to be supplied. - Related to this class is ProductGroup, which provides an easy way + Related to this class is ProductOfCyclicGroups, which provides an easy way to express a group that is the product of multiple cyclic groups (such as some point groups). diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h index 72309acfce96..72fffc46898c 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/Group.h @@ -76,7 +76,7 @@ namespace Geometry Most of the time it's not required to use Group directly, there are several sub-classes that implement different behavior (CenteringGroup, CyclicGroup, - ProductGroup) and are easier to handle. For construction there is a simple + ProductOfCyclicGroups) and are easier to handle. For construction there is a simple "factory function", that works for all Group-based classes which provide a string-based constructor: diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductGroup.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductOfCyclicGroups.h similarity index 81% rename from Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductGroup.h rename to Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductOfCyclicGroups.h index 43cb91b5833d..6c244d42af47 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductGroup.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Crystal/ProductOfCyclicGroups.h @@ -9,15 +9,15 @@ namespace Mantid namespace Geometry { -/** ProductGroup : +/** ProductOfCyclicGroups : - ProductGroup expands a bit on the explanations given in + ProductOfCyclicGroups expands a bit on the explanations given in CyclicGroup. As shown for example in [1], some point groups cannot be expressed solely as a cyclic group. Instead it's necessary to multiply two or three cyclic groups to obtain all symmetry operations of that group. - For this purpose, ProductGroup was created. It takes a set of + For this purpose, ProductOfCyclicGroups was created. It takes a set of n symmetry operations, each of which is seen as a generator of a cyclic group C_i. The resulting n groups ("factor groups") are multiplied to form a product group G: @@ -28,7 +28,7 @@ namespace Geometry in code to generate even large groups from a few generators becomes very short using this class: - Group_const_sptr pointGroup422 = GroupFactory::create("-y,x,z; x,-y,-z"); + Group_const_sptr pointGroup422 = GroupFactory::create("-y,x,z; x,-y,-z"); This is for example used in SpaceGroupFactory to create space groups from a small set of generators supplied in the International Tables @@ -60,17 +60,17 @@ namespace Geometry File change history is stored at: Code Documentation is available at: */ -class MANTID_GEOMETRY_DLL ProductGroup : public Group +class MANTID_GEOMETRY_DLL ProductOfCyclicGroups : public Group { public: - ProductGroup(const std::string &generators); - ProductGroup(const std::vector &factorGroups); - virtual ~ProductGroup() { } + ProductOfCyclicGroups(const std::string &generators); + ProductOfCyclicGroups(const std::vector &factorGroups); + virtual ~ProductOfCyclicGroups() { } protected: Group_const_sptr getGeneratedGroup(const std::string &generators) const; std::vector getFactorGroups(const std::vector &symmetryOperations) const; - Group_const_sptr getProductGroup(const std::vector &factorGroups) const; + Group_const_sptr getProductOfCyclicGroups(const std::vector &factorGroups) const; }; diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/ProductGroup.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/ProductOfCyclicGroups.cpp similarity index 66% rename from Code/Mantid/Framework/Geometry/src/Crystal/ProductGroup.cpp rename to Code/Mantid/Framework/Geometry/src/Crystal/ProductOfCyclicGroups.cpp index f941a2fde5a6..0cafb2407b35 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/ProductGroup.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/ProductOfCyclicGroups.cpp @@ -1,4 +1,4 @@ -#include "MantidGeometry/Crystal/ProductGroup.h" +#include "MantidGeometry/Crystal/ProductOfCyclicGroups.h" #include "MantidGeometry/Crystal/SymmetryOperationFactory.h" #include "MantidGeometry/Crystal/CyclicGroup.h" @@ -9,28 +9,28 @@ namespace Geometry { /// String constructor with semicolon-separated symmetry operations -ProductGroup::ProductGroup(const std::string &generators) : +ProductOfCyclicGroups::ProductOfCyclicGroups(const std::string &generators) : Group(*(getGeneratedGroup(generators))) { } /// Constructor which directly takes a list of factor groups to form the product -ProductGroup::ProductGroup(const std::vector &factorGroups) : - Group(*(getProductGroup(factorGroups))) +ProductOfCyclicGroups::ProductOfCyclicGroups(const std::vector &factorGroups) : + Group(*(getProductOfCyclicGroups(factorGroups))) { } /// Generates symmetry operations from the string, creates a CyclicGroup from each operation and multiplies them to form a factor group. -Group_const_sptr ProductGroup::getGeneratedGroup(const std::string &generators) const +Group_const_sptr ProductOfCyclicGroups::getGeneratedGroup(const std::string &generators) const { std::vector operations = SymmetryOperationFactory::Instance().createSymOps(generators); std::vector factorGroups = getFactorGroups(operations); - return getProductGroup(factorGroups); + return getProductOfCyclicGroups(factorGroups); } /// Returns a vector of cyclic groups for the given vector of symmetry operations -std::vector ProductGroup::getFactorGroups(const std::vector &symmetryOperations) const +std::vector ProductOfCyclicGroups::getFactorGroups(const std::vector &symmetryOperations) const { std::vector groups; @@ -42,7 +42,7 @@ std::vector ProductGroup::getFactorGroups(const std::vector &factorGroups) const +Group_const_sptr ProductOfCyclicGroups::getProductOfCyclicGroups(const std::vector &factorGroups) const { Group_const_sptr productGroup = boost::make_shared(*(factorGroups.front())); diff --git a/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp index 7e18079d91df..38f4021bd883 100644 --- a/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp +++ b/Code/Mantid/Framework/Geometry/src/Crystal/SpaceGroupFactory.cpp @@ -1,7 +1,7 @@ #include "MantidGeometry/Crystal/SpaceGroupFactory.h" #include "MantidGeometry/Crystal/SymmetryOperationFactory.h" -#include "MantidGeometry/Crystal/ProductGroup.h" +#include "MantidGeometry/Crystal/ProductOfCyclicGroups.h" #include "MantidGeometry/Crystal/CenteringGroup.h" #include "MantidKernel/LibraryManager.h" @@ -177,7 +177,7 @@ std::string SpaceGroupFactoryImpl::getCenteringString(const std::string &hmSymbo * Returns a group constructed from a generator string and a centering symbol * * Generators have to be provided as a semicolon separated list of symmetry operations - * in x,y,z format, for example "-x,-y,-z; -x,y,z; -y,x,z". A ProductGroup using this + * in x,y,z format, for example "-x,-y,-z; -x,y,z; -y,x,z". A ProductOfCyclicGroups using this * string is constructed. Centering symbol has to be supported by CenteringGroup. The * group is then calculated as the product of these two groups. * @@ -187,7 +187,7 @@ std::string SpaceGroupFactoryImpl::getCenteringString(const std::string &hmSymbo */ Group_const_sptr SpaceGroupFactoryImpl::getGeneratedGroup(const std::string &generators, const std::string ¢eringSymbol) const { - Group_const_sptr baseGroup = GroupFactory::create(generators); + Group_const_sptr baseGroup = GroupFactory::create(generators); Group_const_sptr centeringGroup = GroupFactory::create(centeringSymbol); return baseGroup * centeringGroup; diff --git a/Code/Mantid/Framework/Geometry/test/ProductGroupTest.h b/Code/Mantid/Framework/Geometry/test/ProductOfCyclicGroupsTest.h similarity index 60% rename from Code/Mantid/Framework/Geometry/test/ProductGroupTest.h rename to Code/Mantid/Framework/Geometry/test/ProductOfCyclicGroupsTest.h index a379536eef51..a4159c15524b 100644 --- a/Code/Mantid/Framework/Geometry/test/ProductGroupTest.h +++ b/Code/Mantid/Framework/Geometry/test/ProductOfCyclicGroupsTest.h @@ -3,27 +3,27 @@ #include -#include "MantidGeometry/Crystal/ProductGroup.h" +#include "MantidGeometry/Crystal/ProductOfCyclicGroups.h" #include "MantidGeometry/Crystal/CyclicGroup.h" #include "MantidGeometry/Crystal/SymmetryOperationFactory.h" using namespace Mantid::Geometry; -class ProductGroupTest : public CxxTest::TestSuite +class ProductOfCyclicGroupsTest : 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 ProductGroupTest *createSuite() { return new ProductGroupTest(); } - static void destroySuite( ProductGroupTest *suite ) { delete suite; } + static ProductOfCyclicGroupsTest *createSuite() { return new ProductOfCyclicGroupsTest(); } + static void destroySuite( ProductOfCyclicGroupsTest *suite ) { delete suite; } void testStringConstructor() { - TS_ASSERT_THROWS_NOTHING(ProductGroup group("x,y,z")); + TS_ASSERT_THROWS_NOTHING(ProductOfCyclicGroups group("x,y,z")); - TS_ASSERT_THROWS_ANYTHING(ProductGroup group("x,y,z; doesnt work")); - TS_ASSERT_THROWS_ANYTHING(ProductGroup group("x,y,z| z,x,y")); + TS_ASSERT_THROWS_ANYTHING(ProductOfCyclicGroups group("x,y,z; doesnt work")); + TS_ASSERT_THROWS_ANYTHING(ProductOfCyclicGroups group("x,y,z| z,x,y")); } void testVectorConstructor() @@ -32,17 +32,17 @@ class ProductGroupTest : public CxxTest::TestSuite groups.push_back(GroupFactory::create("-x,-y,-z")); groups.push_back(GroupFactory::create("x,-y,z")); - TS_ASSERT_THROWS_NOTHING(ProductGroup group(groups)); + TS_ASSERT_THROWS_NOTHING(ProductOfCyclicGroups group(groups)); Group_const_sptr null; groups.push_back(null); - TS_ASSERT_THROWS_ANYTHING(ProductGroup group(groups)); + TS_ASSERT_THROWS_ANYTHING(ProductOfCyclicGroups group(groups)); } void testGetGeneratedGroup() { - TestableProductGroup group; + TestableProductOfCyclicGroups group; Group_const_sptr generatedGroup = group.getGeneratedGroup("-x,-y,-z; x,-y,z"); @@ -52,7 +52,7 @@ class ProductGroupTest : public CxxTest::TestSuite void testGetFactorGroups() { - TestableProductGroup group; + TestableProductOfCyclicGroups group; std::vector symmetryOperations = SymmetryOperationFactory::Instance().createSymOps("-x,-y,-z; x,-y,z"); std::vector generatedGroup = group.getFactorGroups(symmetryOperations); @@ -60,27 +60,27 @@ class ProductGroupTest : public CxxTest::TestSuite TS_ASSERT_EQUALS(generatedGroup.size(), 2); } - void testGetProductGroup() + void testGetProductOfCyclicGroups() { - TestableProductGroup group; + TestableProductOfCyclicGroups group; std::vector groups; groups.push_back(GroupFactory::create("-x,-y,-z")); groups.push_back(GroupFactory::create("x,-y,z")); - Group_const_sptr productGroup = group.getProductGroup(groups); + Group_const_sptr productGroup = group.getProductOfCyclicGroups(groups); TS_ASSERT_EQUALS(productGroup->order(), 4); } private: - class TestableProductGroup : public ProductGroup + class TestableProductOfCyclicGroups : public ProductOfCyclicGroups { - friend class ProductGroupTest; + friend class ProductOfCyclicGroupsTest; public: - TestableProductGroup() : - ProductGroup("x,y,z") { } - ~TestableProductGroup() { } + TestableProductOfCyclicGroups() : + ProductOfCyclicGroups("x,y,z") { } + ~TestableProductOfCyclicGroups() { } }; }; From ec902c6e993b6c61a8c550ce22eafb5962ea8da5 Mon Sep 17 00:00:00 2001 From: Ian Bush Date: Fri, 17 Oct 2014 09:55:11 +0100 Subject: [PATCH 33/48] Revert "Refs #10370 Reserve memory again in LoadMD." This reverts commit c63a2a2144232cf8e1a9354fc2e8e00e3d9abcd3. Instead of this we can still allow reserve to be called in loadAndAddFrom method in MDBox. --- Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp b/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp index e9736c335496..5b5d3b02c1d2 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp @@ -366,10 +366,7 @@ namespace Mantid if(!box)continue; if(BoxEventIndex[2*i+1]>0) // Load in memory NOT using the file as the back-end, - { - boxTree[i]->reserveMemoryForLoad(BoxEventIndex[2*i+1]); boxTree[i]->loadAndAddFrom(loader.get(),BoxEventIndex[2*i],static_cast(BoxEventIndex[2*i+1])); - } } loader->closeFile(); From 54ad8c6ae6f51e6e2710a400c2856ff6a5fcba36 Mon Sep 17 00:00:00 2001 From: Ian Bush Date: Fri, 17 Oct 2014 10:30:23 +0100 Subject: [PATCH 34/48] Refs #10370 Added reserve back to loadAndAddfrom in MDBox. This means the reserve will still be performed if required, but not if memory was already reserved. --- Code/Mantid/Framework/MDEvents/src/MDBox.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Code/Mantid/Framework/MDEvents/src/MDBox.cpp b/Code/Mantid/Framework/MDEvents/src/MDBox.cpp index ece7dfc1309f..03e020c1b945 100644 --- a/Code/Mantid/Framework/MDEvents/src/MDBox.cpp +++ b/Code/Mantid/Framework/MDEvents/src/MDBox.cpp @@ -934,6 +934,9 @@ namespace MDEvents std::vector TableData; FileSaver->loadBlock(TableData,filePosition,nEvents); + // convert loaded events to data; + size_t nCurrentEvents = data.size(); + this->data.reserve(nCurrentEvents+nEvents); // if space already reserved, e.g. from MergeMDFiles, this does nothing // convert data to events appending new events to existing MDE::dataToEvents(TableData,data,false); From 68b05b4ca2a370183219eea23dac91e166870357 Mon Sep 17 00:00:00 2001 From: Ian Bush Date: Fri, 17 Oct 2014 11:06:52 +0100 Subject: [PATCH 35/48] Revert "Refs #10370 Added reserve back to loadAndAddfrom in MDBox." This reverts commit 54ad8c6ae6f51e6e2710a400c2856ff6a5fcba36. Decided to leave the reserve as previously, as push back should allow for adequate performance in most cases even if another method relying on loadAndAddFrom in MDBox does not call reserveMemoryForLoad first. --- Code/Mantid/Framework/MDEvents/src/MDBox.cpp | 3 --- 1 file changed, 3 deletions(-) diff --git a/Code/Mantid/Framework/MDEvents/src/MDBox.cpp b/Code/Mantid/Framework/MDEvents/src/MDBox.cpp index 03e020c1b945..ece7dfc1309f 100644 --- a/Code/Mantid/Framework/MDEvents/src/MDBox.cpp +++ b/Code/Mantid/Framework/MDEvents/src/MDBox.cpp @@ -934,9 +934,6 @@ namespace MDEvents std::vector TableData; FileSaver->loadBlock(TableData,filePosition,nEvents); - // convert loaded events to data; - size_t nCurrentEvents = data.size(); - this->data.reserve(nCurrentEvents+nEvents); // if space already reserved, e.g. from MergeMDFiles, this does nothing // convert data to events appending new events to existing MDE::dataToEvents(TableData,data,false); From fde2f5abfc38df00c06d87d95df6f04d99fe2ecb Mon Sep 17 00:00:00 2001 From: Ian Bush Date: Fri, 17 Oct 2014 11:09:24 +0100 Subject: [PATCH 36/48] Revert "Revert "Refs #10370 Reserve memory again in LoadMD."" This reverts commit ec902c6e993b6c61a8c550ce22eafb5962ea8da5. --- Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp b/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp index 5b5d3b02c1d2..e9736c335496 100644 --- a/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp +++ b/Code/Mantid/Framework/MDAlgorithms/src/LoadMD.cpp @@ -366,7 +366,10 @@ namespace Mantid if(!box)continue; if(BoxEventIndex[2*i+1]>0) // Load in memory NOT using the file as the back-end, + { + boxTree[i]->reserveMemoryForLoad(BoxEventIndex[2*i+1]); boxTree[i]->loadAndAddFrom(loader.get(),BoxEventIndex[2*i],static_cast(BoxEventIndex[2*i+1])); + } } loader->closeFile(); From 3ae9c14395f4bb21ecbed924df07c4209254a84d Mon Sep 17 00:00:00 2001 From: Anders Markvardsen Date: Fri, 17 Oct 2014 11:27:47 +0100 Subject: [PATCH 37/48] Fix bad merge conflict. re #10281 --- Code/Mantid/Framework/Geometry/CMakeLists.txt | 28 +++++++++++++++---- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/CMakeLists.txt b/Code/Mantid/Framework/Geometry/CMakeLists.txt index fca601e7ce00..846301d04a49 100644 --- a/Code/Mantid/Framework/Geometry/CMakeLists.txt +++ b/Code/Mantid/Framework/Geometry/CMakeLists.txt @@ -1,15 +1,21 @@ set ( SRC_FILES src/ComponentParser.cpp + src/Crystal/CenteringGroup.cpp src/Crystal/ConventionalCell.cpp src/Crystal/CrystalStructure.cpp + src/Crystal/CyclicGroup.cpp + src/Crystal/Group.cpp src/Crystal/IndexingUtils.cpp src/Crystal/NiggliCell.cpp src/Crystal/OrientedLattice.cpp src/Crystal/PointGroup.cpp src/Crystal/PointGroupFactory.cpp + src/Crystal/ProductOfCyclicGroups.cpp src/Crystal/ReducedCell.cpp src/Crystal/ReflectionCondition.cpp src/Crystal/ScalarUtils.cpp + src/Crystal/SpaceGroup.cpp + src/Crystal/SpaceGroupFactory.cpp src/Crystal/SymmetryOperation.cpp src/Crystal/SymmetryOperationFactory.cpp src/Crystal/SymmetryOperationSymbolParser.cpp @@ -98,23 +104,29 @@ set ( OPENCASCADE_SRC ) set ( SRC_UNITY_IGNORE_FILES src/Instrument/CompAssembly.cpp - src/Instrument/ObjCompAssembly.cpp + src/Instrument/ObjCompAssembly.cpp src/Rendering/OCGeometryHandler.cpp src/Rendering/OCGeometryRenderer.cpp ) - + set ( INC_FILES inc/MantidGeometry/ComponentParser.h + inc/MantidGeometry/Crystal/CenteringGroup.h inc/MantidGeometry/Crystal/ConventionalCell.h inc/MantidGeometry/Crystal/CrystalStructure.h + inc/MantidGeometry/Crystal/CyclicGroup.h + inc/MantidGeometry/Crystal/Group.h inc/MantidGeometry/Crystal/IndexingUtils.h inc/MantidGeometry/Crystal/NiggliCell.h inc/MantidGeometry/Crystal/OrientedLattice.h inc/MantidGeometry/Crystal/PointGroup.h inc/MantidGeometry/Crystal/PointGroupFactory.h + inc/MantidGeometry/Crystal/ProductOfCyclicGroups.h inc/MantidGeometry/Crystal/ReducedCell.h inc/MantidGeometry/Crystal/ReflectionCondition.h inc/MantidGeometry/Crystal/ScalarUtils.h + inc/MantidGeometry/Crystal/SpaceGroup.h + inc/MantidGeometry/Crystal/SpaceGroupFactory.h inc/MantidGeometry/Crystal/SymmetryOperation.h inc/MantidGeometry/Crystal/SymmetryOperationFactory.h inc/MantidGeometry/Crystal/SymmetryOperationSymbolParser.h @@ -206,6 +218,7 @@ set ( TEST_FILES AlgebraTest.h BnIdTest.h BoundingBoxTest.h + CenteringGroupTest.h CompAssemblyTest.h ComponentHelperTest.h ComponentParserTest.h @@ -215,12 +228,14 @@ set ( TEST_FILES ConventionalCellTest.h ConvexPolygonTest.h CrystalStructureTest.h + CyclicGroupTest.h CylinderTest.h DetectorGroupTest.h DetectorTest.h FitParameterTest.h GeneralTest.h GoniometerTest.h + GroupTest.h IDFObjectTest.h IMDDimensionFactoryTest.h IMDDimensionTest.h @@ -242,11 +257,11 @@ set ( TEST_FILES MathSupportTest.h NearestNeighboursFactoryTest.h NearestNeighboursTest.h - NiggliCellTest.h NullImplicitFunctionTest.h ObjCompAssemblyTest.h ObjComponentTest.h ObjectTest.h + NiggliCellTest.h OrientedLatticeTest.h ParCompAssemblyTest.h ParComponentFactoryTest.h @@ -260,6 +275,7 @@ set ( TEST_FILES PointGroupFactoryTest.h PointGroupTest.h PolygonEdgeTest.h + ProductOfCyclicGroupsTest.h QuadrilateralTest.h RectangularDetectorPixelTest.h RectangularDetectorTest.h @@ -276,6 +292,8 @@ set ( TEST_FILES RulesUnionTest.h ScalarUtilsTest.h ShapeFactoryTest.h + SpaceGroupFactoryTest.h + SpaceGroupTest.h SphereTest.h SurfaceFactoryTest.h SurfaceTest.h @@ -332,8 +350,8 @@ enable_precompiled_headers( inc/MantidGeometry/PrecompiledHeader.h SRC_FILES ) # Add the target for this directory add_library ( Geometry ${SRC_FILES} ${INC_FILES} ) # Set the name of the generated library -set_target_properties ( Geometry PROPERTIES OUTPUT_NAME MantidGeometry - COMPILE_DEFINITIONS IN_MANTID_GEOMETRY ) +set_target_properties ( Geometry PROPERTIES OUTPUT_NAME MantidGeometry + COMPILE_DEFINITIONS IN_MANTID_GEOMETRY ) # Add to the 'Framework' group in VS set_property ( TARGET Geometry PROPERTY FOLDER "MantidFramework" ) From 475a6a9982dec495fd93b23a051d648e17167198 Mon Sep 17 00:00:00 2001 From: Dan Nixon Date: Fri, 17 Oct 2014 12:10:59 +0100 Subject: [PATCH 38/48] Add convole to sample logs Also correct a Qt warning Refs #10381 --- .../inc/MantidQtCustomInterfaces/IndirectDataReduction.ui | 4 ++-- Code/Mantid/scripts/Inelastic/IndirectDataAnalysis.py | 1 + 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/IndirectDataReduction.ui b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/IndirectDataReduction.ui index 2a378418c3ac..e4d7ec7c8cb5 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/IndirectDataReduction.ui +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/IndirectDataReduction.ui @@ -1683,7 +1683,7 @@ Later steps in the process (saving, renaming) will not be done. Preview - + @@ -2363,7 +2363,7 @@ Later steps in the process (saving, renaming) will not be done. Preview - + 6 diff --git a/Code/Mantid/scripts/Inelastic/IndirectDataAnalysis.py b/Code/Mantid/scripts/Inelastic/IndirectDataAnalysis.py index 4c5abd745c37..36ebc9ff0a0f 100644 --- a/Code/Mantid/scripts/Inelastic/IndirectDataAnalysis.py +++ b/Code/Mantid/scripts/Inelastic/IndirectDataAnalysis.py @@ -122,6 +122,7 @@ def confitSeq(inputWS, func, startX, endX, ftype, bgd, temperature=None, specMin axis.setUnit("MomentumTransfer") CopyLogs(InputWorkspace=inputWS, OutputWorkspace=wsname) + AddSampleLog(Workspace=wsname, LogName='convolve_members', LogType='String', LogText=str(convolve)) AddSampleLog(Workspace=wsname, LogName="fit_program", LogType="String", LogText='ConvFit') AddSampleLog(Workspace=wsname, LogName='background', LogType='String', LogText=str(bgd)) AddSampleLog(Workspace=wsname, LogName='delta_function', LogType='String', LogText=str(using_delta_func)) From 1dded72502f7701c04a21e45e257726ae1f60549 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Fri, 17 Oct 2014 12:24:09 +0100 Subject: [PATCH 39/48] Refs #10364 Merge Refl presenters into ReflMainViewPresenter --- .../MantidQt/CustomInterfaces/CMakeLists.txt | 15 - .../MantidQtCustomInterfaces/QtReflMainView.h | 7 +- .../ReflBlankMainViewPresenter.h | 54 -- .../ReflLoadedMainViewPresenter.h | 54 -- .../MantidQtCustomInterfaces/ReflMainView.h | 3 + .../ReflMainViewPresenter.h | 18 +- .../ReflNullMainViewPresenter.h | 44 -- .../CustomInterfaces/src/QtReflMainView.cpp | 56 +- .../src/ReflBlankMainViewPresenter.cpp | 85 --- .../src/ReflLoadedMainViewPresenter.cpp | 103 --- .../src/ReflMainViewPresenter.cpp | 128 +++- .../src/ReflNullMainViewPresenter.cpp | 19 - .../test/ReflBlankMainViewPresenterTest.h | 398 ----------- .../test/ReflLoadedMainViewPresenterTest.h | 625 ------------------ 14 files changed, 162 insertions(+), 1447 deletions(-) delete mode 100644 Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflBlankMainViewPresenter.h delete mode 100644 Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflLoadedMainViewPresenter.h delete mode 100644 Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflNullMainViewPresenter.h delete mode 100644 Code/Mantid/MantidQt/CustomInterfaces/src/ReflBlankMainViewPresenter.cpp delete mode 100644 Code/Mantid/MantidQt/CustomInterfaces/src/ReflLoadedMainViewPresenter.cpp delete mode 100644 Code/Mantid/MantidQt/CustomInterfaces/src/ReflNullMainViewPresenter.cpp delete mode 100644 Code/Mantid/MantidQt/CustomInterfaces/test/ReflBlankMainViewPresenterTest.h delete mode 100644 Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h diff --git a/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt b/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt index 40265b835a28..a99dc4acb7af 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt +++ b/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt @@ -54,11 +54,8 @@ set ( SRC_FILES src/QtWorkspaceMementoModel.cpp src/Quasi.cpp src/RawFileMemento.cpp - src/ReflBlankMainViewPresenter.cpp - src/ReflLoadedMainViewPresenter.cpp src/ReflMainView.cpp src/ReflMainViewPresenter.cpp - src/ReflNullMainViewPresenter.cpp src/ResNorm.cpp src/SANSAddFiles.cpp src/SANSDiagnostics.cpp @@ -134,11 +131,8 @@ set ( INC_FILES inc/MantidQtCustomInterfaces/Muon/MuonAnalysisHelper.h inc/MantidQtCustomInterfaces/Muon/MuonAnalysisOptionTab.h inc/MantidQtCustomInterfaces/Muon/MuonAnalysisResultTableTab.h - inc/MantidQtCustomInterfaces/ReflBlankMainViewPresenter.h - inc/MantidQtCustomInterfaces/ReflLoadedMainViewPresenter.h inc/MantidQtCustomInterfaces/ReflMainView.h inc/MantidQtCustomInterfaces/ReflMainViewPresenter.h - inc/MantidQtCustomInterfaces/ReflNullMainViewPresenter.h inc/MantidQtCustomInterfaces/QReflTableModel.h inc/MantidQtCustomInterfaces/QtReflMainView.h inc/MantidQtCustomInterfaces/QtWorkspaceMementoModel.h @@ -261,8 +255,6 @@ set ( TEST_FILES IO_MuonGroupingTest.h MuonAnalysisHelperTest.h RawFileMementoTest.h - ReflBlankMainViewPresenterTest.h - ReflLoadedMainViewPresenterTest.h WorkspaceInADSTest.h WorkspaceMementoTest.h ) @@ -314,10 +306,3 @@ add_subdirectory ( test ) ########################################################################### install ( TARGETS CustomInterfaces ${SYSTEM_PACKAGE_TARGET} DESTINATION ${PLUGINS_DIR}/qtplugins/mantid ) - -set ( TEST_FILES - QReflTableModelTest.h - QtReflMainViewTest.h - ReflBlankMainViewPresenterTest.h - ReflLoadedMainViewPresenterTest.h -) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/QtReflMainView.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/QtReflMainView.h index 87f14d6ceedd..cd6448572107 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/QtReflMainView.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/QtReflMainView.h @@ -65,19 +65,22 @@ namespace MantidQt virtual std::vector getSelectedRowIndexes() const; virtual std::string getSearchInstrument() const; virtual std::string getProcessInstrument() const; + virtual std::string getWorkspaceToOpen() const; private: //initialise the interface virtual void initLayout(); virtual void setInstrumentList(const std::vector& instruments); //the presenter - boost::scoped_ptr m_presenter; + boost::shared_ptr m_presenter; //the interface Ui::reflMainWidget ui; + //the workspace the user selected to open + std::string m_toOpen; private slots: void setModel(QString name); - void setNew(); + void actionNewTable(); void actionSave(); void actionSaveAs(); void actionAddRow(); diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflBlankMainViewPresenter.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflBlankMainViewPresenter.h deleted file mode 100644 index f2870504ff7b..000000000000 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflBlankMainViewPresenter.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef MANTID_CUSTOMINTERFACES_REFLBLANKMAINVIEWPRESENTER_H_ -#define MANTID_CUSTOMINTERFACES_REFLBLANKMAINVIEWPRESENTER_H_ - -#include "MantidKernel/System.h" -#include "MantidQtCustomInterfaces/ReflMainViewPresenter.h" -#include "MantidAPI/ITableWorkspace.h" -#include "MantidQtCustomInterfaces/ReflMainView.h" - -namespace MantidQt -{ - namespace CustomInterfaces - { - - /** ReflBlankMainViewPresenter : Handles presentation logic for the reflectometry interface - when a table workspace has not been loaded or selected. - - Copyright © 2014 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 . - - File change history is stored at: - Code Documentation is available at: - */ - class DLLExport ReflBlankMainViewPresenter: public ReflMainViewPresenter - { - public: - - ReflBlankMainViewPresenter(ReflMainView* view); - virtual ~ReflBlankMainViewPresenter(); - protected: - //press changes to a previously saved-to item in the ADS, or ask for a name if never given one - virtual void save(); - //press changes to a new item in the ADS - virtual void saveAs(); - }; - - - } // namespace CustomInterfaces -} // namespace Mantid - -#endif /* MANTID_CUSTOMINTERFACES_REFLBLANKMAINVIEWPRESENTER_H_ */ diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflLoadedMainViewPresenter.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflLoadedMainViewPresenter.h deleted file mode 100644 index b7572cc5cf75..000000000000 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflLoadedMainViewPresenter.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef MANTID_CUSTOMINTERFACES_REFLLOADEDMAINVIEWPRESENTER_H_ -#define MANTID_CUSTOMINTERFACES_REFLLOADEDMAINVIEWPRESENTER_H_ - -#include "MantidKernel/System.h" -#include "MantidQtCustomInterfaces/ReflMainViewPresenter.h" -#include "MantidAPI/ITableWorkspace.h" -#include "MantidQtCustomInterfaces/ReflMainView.h" - -namespace MantidQt -{ - namespace CustomInterfaces - { - - /** ReflLoadedMainViewPresenter : Handles presentation logic for the reflectometry interface - when a table workspace is loaded as the active table. - - Copyright © 2014 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 . - - File change history is stored at: - Code Documentation is available at: - */ - class DLLExport ReflLoadedMainViewPresenter: public ReflMainViewPresenter - { - public: - ReflLoadedMainViewPresenter(Mantid::API::ITableWorkspace_sptr model, ReflMainView* view); - ReflLoadedMainViewPresenter(std::string model, ReflMainView* view); - virtual ~ReflLoadedMainViewPresenter(); - protected: - //press changes to the same item in the ADS - virtual void save(); - //press changes to a new item in the ADS - virtual void saveAs(); - }; - - - } // namespace CustomInterfaces -} // namespace Mantid - -#endif /* MANTID_CUSTOMINTERFACES_REFLLOADEDMAINVIEWPRESENTER_H_ */ diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h index ea08c1f214f5..b5978452214d 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h @@ -57,6 +57,7 @@ namespace MantidQt virtual std::vector getSelectedRowIndexes() const = 0; virtual std::string getSearchInstrument() const = 0; virtual std::string getProcessInstrument() const = 0; + virtual std::string getWorkspaceToOpen() const = 0; static const int NoFlags = 0; static const int SaveFlag = 1; @@ -65,6 +66,8 @@ namespace MantidQt static const int DeleteRowFlag = 4; static const int ProcessFlag = 5; static const int GroupRowsFlag = 6; + static const int OpenTableFlag = 7; + static const int NewTableFlag = 8; }; } } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainViewPresenter.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainViewPresenter.h index 27320404201e..c820f8a427f5 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainViewPresenter.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainViewPresenter.h @@ -37,20 +37,14 @@ namespace MantidQt class DLLExport ReflMainViewPresenter: public IReflPresenter { public: - ReflMainViewPresenter(Mantid::API::ITableWorkspace_sptr model, ReflMainView* view); ReflMainViewPresenter(ReflMainView* view); - virtual ~ReflMainViewPresenter() = 0; + virtual ~ReflMainViewPresenter(); virtual void notify(int flag); protected: - //The model and backup copy of the original model Mantid::API::ITableWorkspace_sptr m_model; - Mantid::API::ITableWorkspace_sptr m_cache; - std::string m_cache_name; - //the view + std::string m_wsName; ReflMainView* m_view; - //Load the model into the view - virtual void load(); //process selected rows virtual void process(); //load a run into the ADS, or re-use one in the ADS if possible @@ -77,9 +71,11 @@ namespace MantidQt virtual void deleteRow(); //group selected rows together virtual void groupRows(); - //virtual save methods - virtual void save() = 0; - virtual void saveAs() = 0; + //table io methods + virtual void newTable(); + virtual void openTable(); + virtual void saveTable(); + virtual void saveTableAs(); public: static const int COL_RUNS = 0; diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflNullMainViewPresenter.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflNullMainViewPresenter.h deleted file mode 100644 index dbb17a024b75..000000000000 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflNullMainViewPresenter.h +++ /dev/null @@ -1,44 +0,0 @@ -#ifndef MANTID_CUSTOMINTERFACES_REFLNULLMAINVIEWPRESENTER_H -#define MANTID_CUSTOMINTERFACES_REFLNULLMAINVIEWPRESENTER_H - -#include "MantidKernel/System.h" -#include "MantidQtCustomInterfaces/IReflPresenter.h" -namespace MantidQt -{ - namespace CustomInterfaces - { - /** @class ReflNullMainViewPresenter - - ReflNullMainViewPresenter is a Null object that supports all the IReflPresenter interfaces but raises a runtime error if any are called. - - Copyright © 2011-14 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 . - - File change history is stored at: . - Code Documentation is available at: -*/ - class DLLExport ReflNullMainViewPresenter: public IReflPresenter - { - public: - virtual ~ReflNullMainViewPresenter(); - virtual void notify(int flag); - private: - - }; - } -} -#endif diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp index 0a91f9784a98..d0e531b8bce8 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp @@ -1,9 +1,6 @@ #include "MantidQtCustomInterfaces/QtReflMainView.h" #include "MantidQtCustomInterfaces/QReflTableModel.h" -#include "MantidQtCustomInterfaces/ReflNullMainViewPresenter.h" #include "MantidQtCustomInterfaces/ReflMainViewPresenter.h" -#include "MantidQtCustomInterfaces/ReflBlankMainViewPresenter.h" -#include "MantidQtCustomInterfaces/ReflLoadedMainViewPresenter.h" #include "MantidAPI/ITableWorkspace.h" #include "MantidKernel/ConfigService.h" #include @@ -20,7 +17,7 @@ namespace MantidQt //---------------------------------------------------------------------------------------------- /** Constructor */ - QtReflMainView::QtReflMainView(QWidget *parent) : UserSubWindow(parent), m_presenter(new ReflNullMainViewPresenter()) + QtReflMainView::QtReflMainView(QWidget *parent) : UserSubWindow(parent), m_presenter(NULL) { } @@ -66,24 +63,17 @@ namespace MantidQt //Allow rows to be reordered ui.viewTable->verticalHeader()->setMovable(true); - connect(ui.workspaceSelector,SIGNAL(activated(QString)),this,SLOT(setModel(QString))); - connect(ui.actionSaveTable, SIGNAL(triggered()),this, SLOT(actionSave())); - connect(ui.actionSaveTableAs, SIGNAL(triggered()),this, SLOT(actionSaveAs())); - connect(ui.actionNewTable, SIGNAL(triggered()),this, SLOT(setNew())); - connect(ui.actionAddRow, SIGNAL(triggered()),this, SLOT(actionAddRow())); - connect(ui.actionDeleteRow, SIGNAL(triggered()),this, SLOT(actionDeleteRow())); - connect(ui.actionProcess, SIGNAL(triggered()),this, SLOT(actionProcess())); - connect(ui.actionGroupRows, SIGNAL(triggered()),this, SLOT(actionGroupRows())); - setNew(); - } - - /** - This slot loads a blank table and changes to a BlankMainView presenter - */ - void QtReflMainView::setNew() - { - boost::scoped_ptr newPtr(new ReflBlankMainViewPresenter(this)); - m_presenter.swap(newPtr); + connect(ui.workspaceSelector, SIGNAL(activated(QString)), this, SLOT(setModel(QString))); + connect(ui.actionSaveTable, SIGNAL(triggered()), this, SLOT(actionSave())); + connect(ui.actionSaveTableAs, SIGNAL(triggered()), this, SLOT(actionSaveAs())); + connect(ui.actionNewTable, SIGNAL(triggered()), this, SLOT(actionNewTable())); + connect(ui.actionAddRow, SIGNAL(triggered()), this, SLOT(actionAddRow())); + connect(ui.actionDeleteRow, SIGNAL(triggered()), this, SLOT(actionDeleteRow())); + connect(ui.actionProcess, SIGNAL(triggered()), this, SLOT(actionProcess())); + connect(ui.actionGroupRows, SIGNAL(triggered()), this, SLOT(actionGroupRows())); + + //Finally, create a presenter to do the thinking for us + m_presenter = boost::shared_ptr(new ReflMainViewPresenter(this)); } /** @@ -92,9 +82,8 @@ namespace MantidQt */ void QtReflMainView::setModel(QString name) { - boost::scoped_ptr newPtr(new ReflLoadedMainViewPresenter(name.toStdString(), this)); - m_presenter.swap(newPtr); - m_presenter->notify(NoFlags); + m_toOpen = name.toStdString(); + m_presenter->notify(OpenTableFlag); } /** @@ -155,6 +144,14 @@ namespace MantidQt m_presenter->notify(GroupRowsFlag); } + /** + This slot notifies the presenter that the "new table" button as been pressed + */ + void QtReflMainView::actionNewTable() + { + m_presenter->notify(NewTableFlag); + } + /** Show an information dialog @param prompt : The prompt to appear on the dialog @@ -287,5 +284,14 @@ namespace MantidQt return rowIndexes; } + /** + Get the name of the workspace that the user wishes to open as a table + @returns The name of the workspace to open + */ + std::string QtReflMainView::getWorkspaceToOpen() const + { + return m_toOpen; + } + } // namespace CustomInterfaces } // namespace Mantid diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflBlankMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflBlankMainViewPresenter.cpp deleted file mode 100644 index 10e1bf2f5887..000000000000 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflBlankMainViewPresenter.cpp +++ /dev/null @@ -1,85 +0,0 @@ -#include "MantidQtCustomInterfaces/ReflBlankMainViewPresenter.h" -#include "MantidAPI/WorkspaceFactory.h" -#include "MantidAPI/TableRow.h" -using namespace Mantid::API; -namespace -{ - ITableWorkspace_sptr createWorkspace() - { - ITableWorkspace_sptr ws = WorkspaceFactory::Instance().createTable(); - auto colRuns = ws->addColumn("str","Run(s)"); - auto colTheta = ws->addColumn("str","ThetaIn"); - auto colTrans = ws->addColumn("str","TransRun(s)"); - auto colQmin = ws->addColumn("str","Qmin"); - auto colQmax = ws->addColumn("str","Qmax"); - auto colDqq = ws->addColumn("str","dq/q"); - auto colScale = ws->addColumn("double","Scale"); - auto colStitch = ws->addColumn("int","StitchGroup"); - auto colOptions = ws->addColumn("str","Options"); - - colRuns->setPlotType(0); - colTheta->setPlotType(0); - colTrans->setPlotType(0); - colQmin->setPlotType(0); - colQmax->setPlotType(0); - colDqq->setPlotType(0); - colScale->setPlotType(0); - colStitch->setPlotType(0); - colOptions->setPlotType(0); - - TableRow row = ws->appendRow(); - return ws; - } -} - -namespace MantidQt -{ - namespace CustomInterfaces - { - - - //---------------------------------------------------------------------------------------------- - /** Constructor - */ - ReflBlankMainViewPresenter::ReflBlankMainViewPresenter(ReflMainView* view): ReflMainViewPresenter(view) - { - m_model = createWorkspace(); - load(); - } - - //---------------------------------------------------------------------------------------------- - /** Destructor - */ - ReflBlankMainViewPresenter::~ReflBlankMainViewPresenter() - { - } - - /** - Press changes to a previously saved-to item in the ADS, or ask for a name if never given one - */ - void ReflBlankMainViewPresenter::save() - { - if (m_cache_name != "") - { - AnalysisDataService::Instance().addOrReplace(m_cache_name, boost::shared_ptr(m_model->clone())); - } - else - { - saveAs(); - } - } - - /** - Press changes to a new item in the ADS - */ - void ReflBlankMainViewPresenter::saveAs() - { - std::string userString = m_view->askUserString("Save As", "Enter a workspace name:", "Workspace"); - if(!userString.empty()) - { - m_cache_name = userString; - save(); - } - } - } // namespace CustomInterfaces -} // namespace Mantid diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflLoadedMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflLoadedMainViewPresenter.cpp deleted file mode 100644 index e2e798cfdb29..000000000000 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflLoadedMainViewPresenter.cpp +++ /dev/null @@ -1,103 +0,0 @@ -#include "MantidQtCustomInterfaces/ReflLoadedMainViewPresenter.h" -#include "MantidAPI/ITableWorkspace.h" -#include "MantidAPI/WorkspaceFactory.h" -#include "MantidQtCustomInterfaces/ReflMainView.h" -using namespace Mantid::API; -namespace -{ - void hasValidModel(ITableWorkspace_sptr model) - { - if(model->columnCount() != 9) - throw std::runtime_error("Selected table has the incorrect number of columns (9) to be used as a reflectometry table."); - - try - { - model->String(0,0); - model->String(0,1); - model->String(0,2); - model->String(0,3); - model->String(0,4); - model->String(0,5); - model->Double(0,6); - model->Int(0,7); - model->String(0,8); - } - catch(const std::runtime_error&) - { - throw std::runtime_error("Selected table does not meet the specifications to become a model for this interface."); - } - } -} -namespace MantidQt -{ - namespace CustomInterfaces - { - - - //---------------------------------------------------------------------------------------------- - /** Constructor - */ - - ReflLoadedMainViewPresenter::ReflLoadedMainViewPresenter(ITableWorkspace_sptr model, ReflMainView* view): - ReflMainViewPresenter(boost::shared_ptr(model->clone()), view) - { - if (model->name() != "") - { - m_cache_name = model->name(); - } - else - { - throw std::runtime_error("Supplied model workspace must have a name"); - } - m_cache = model; - hasValidModel(m_model); - load(); - } - - ReflLoadedMainViewPresenter::ReflLoadedMainViewPresenter(std::string model, ReflMainView* view): - ReflMainViewPresenter(boost::shared_ptr(AnalysisDataService::Instance().retrieveWS(model)->clone()), view) - { - m_cache = AnalysisDataService::Instance().retrieveWS(model); - if (m_cache->name() != "") - { - m_cache_name = m_cache->name(); - } - else - { - throw std::runtime_error("Supplied model workspace must have a name"); - } - hasValidModel(m_model); - load(); - } - - //---------------------------------------------------------------------------------------------- - /** Destructor - */ - ReflLoadedMainViewPresenter::~ReflLoadedMainViewPresenter() - { - - } - - /** - Press changes to the same item in the ADS - */ - void ReflLoadedMainViewPresenter::save() - { - AnalysisDataService::Instance().addOrReplace(m_cache_name,boost::shared_ptr(m_model->clone())); - } - - /** - Press changes to a new item in the ADS - */ - void ReflLoadedMainViewPresenter::saveAs() - { - std::string userString = m_view->askUserString("Save As", "Enter a workspace name:", "Workspace"); - if(!userString.empty()) - { - m_cache_name = userString; - save(); - } - } - - } // namespace CustomInterfaces -} // namespace Mantid diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp index f11d46078ffb..8fbbf8d41c72 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp @@ -1,6 +1,7 @@ #include "MantidQtCustomInterfaces/ReflMainViewPresenter.h" #include "MantidAPI/AlgorithmManager.h" #include "MantidAPI/ITableWorkspace.h" +#include "MantidAPI/TableRow.h" #include "MantidGeometry/Instrument/ParameterMap.h" #include "MantidKernel/Strings.h" #include "MantidKernel/TimeSeriesProperty.h" @@ -13,16 +14,66 @@ using namespace Mantid::API; using namespace Mantid::Geometry; using namespace Mantid::Kernel; -namespace MantidQt +namespace { - namespace CustomInterfaces + void checkValidModel(ITableWorkspace_sptr model) { - ReflMainViewPresenter::ReflMainViewPresenter(ReflMainView* view): m_view(view) + if(model->columnCount() != 9) + throw std::runtime_error("Selected table has the incorrect number of columns (9) to be used as a reflectometry table."); + + try { + model->String(0,0); + model->String(0,1); + model->String(0,2); + model->String(0,3); + model->String(0,4); + model->String(0,5); + model->Double(0,6); + model->Int(0,7); + model->String(0,8); } + catch(const std::runtime_error&) + { + throw std::runtime_error("Selected table does not meet the specifications to become a model for this interface."); + } + } - ReflMainViewPresenter::ReflMainViewPresenter(ITableWorkspace_sptr model, ReflMainView* view): m_model(model), m_view(view) + ITableWorkspace_sptr createWorkspace() + { + ITableWorkspace_sptr ws = WorkspaceFactory::Instance().createTable(); + auto colRuns = ws->addColumn("str","Run(s)"); + auto colTheta = ws->addColumn("str","ThetaIn"); + auto colTrans = ws->addColumn("str","TransRun(s)"); + auto colQmin = ws->addColumn("str","Qmin"); + auto colQmax = ws->addColumn("str","Qmax"); + auto colDqq = ws->addColumn("str","dq/q"); + auto colScale = ws->addColumn("double","Scale"); + auto colStitch = ws->addColumn("int","StitchGroup"); + auto colOptions = ws->addColumn("str","Options"); + + colRuns->setPlotType(0); + colTheta->setPlotType(0); + colTrans->setPlotType(0); + colQmin->setPlotType(0); + colQmax->setPlotType(0); + colDqq->setPlotType(0); + colScale->setPlotType(0); + colStitch->setPlotType(0); + colOptions->setPlotType(0); + + ws->appendRow(); + return ws; + } +} + +namespace MantidQt +{ + namespace CustomInterfaces + { + ReflMainViewPresenter::ReflMainViewPresenter(ReflMainView* view): m_model(NULL), m_view(view) { + newTable(); } ReflMainViewPresenter::~ReflMainViewPresenter() @@ -603,12 +654,14 @@ namespace MantidQt { switch(flag) { - case ReflMainView::SaveAsFlag: saveAs(); break; - case ReflMainView::SaveFlag: save(); break; - case ReflMainView::AddRowFlag: addRow(); break; - case ReflMainView::DeleteRowFlag: deleteRow(); break; - case ReflMainView::ProcessFlag: process(); break; - case ReflMainView::GroupRowsFlag: groupRows(); break; + case ReflMainView::SaveAsFlag: saveTableAs(); break; + case ReflMainView::SaveFlag: saveTable(); break; + case ReflMainView::AddRowFlag: addRow(); break; + case ReflMainView::DeleteRowFlag: deleteRow(); break; + case ReflMainView::ProcessFlag: process(); break; + case ReflMainView::GroupRowsFlag: groupRows(); break; + case ReflMainView::OpenTableFlag: openTable(); break; + case ReflMainView::NewTableFlag: newTable(); break; case ReflMainView::NoFlags: return; } @@ -616,10 +669,61 @@ namespace MantidQt } /** - Load the model into the table + Press changes to the same item in the ADS + */ + void ReflMainViewPresenter::saveTable() + { + if(!m_wsName.empty()) + AnalysisDataService::Instance().addOrReplace(m_wsName,boost::shared_ptr(m_model->clone())); + else + saveTableAs(); + } + + /** + Press changes to a new item in the ADS */ - void ReflMainViewPresenter::load() + void ReflMainViewPresenter::saveTableAs() { + const std::string userString = m_view->askUserString("Save As", "Enter a workspace name:", "Workspace"); + if(!userString.empty()) + { + m_wsName = userString; + saveTable(); + } + } + + /** + Start a new, untitled table + */ + void ReflMainViewPresenter::newTable() + { + m_model = createWorkspace(); + m_wsName.clear(); + m_view->showTable(m_model); + } + + /** + Open a table from the ADS + */ + void ReflMainViewPresenter::openTable() + { + auto& ads = AnalysisDataService::Instance(); + const std::string toOpen = m_view->getWorkspaceToOpen(); + + if(toOpen.empty()) + return; + + if(!ads.isValid(toOpen).empty()) + { + m_view->giveUserCritical("Could not open workspace: " + toOpen, "Error"); + return; + } + + ITableWorkspace_sptr newModel = AnalysisDataService::Instance().retrieveWS(toOpen); + checkValidModel(newModel); + + m_model = newModel; + m_wsName = toOpen; m_view->showTable(m_model); } } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflNullMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflNullMainViewPresenter.cpp deleted file mode 100644 index 2af3afc4169d..000000000000 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflNullMainViewPresenter.cpp +++ /dev/null @@ -1,19 +0,0 @@ -#include "MantidQtCustomInterfaces/ReflNullMainViewPresenter.h" -#include "MantidAPI/ITableWorkspace.h" -#include "MantidQtCustomInterfaces/ReflMainView.h" - -namespace MantidQt -{ - namespace CustomInterfaces - { - void ReflNullMainViewPresenter::notify(int flag) - { - (void)flag; - throw std::runtime_error("Cannot notify a null presenter"); - } - - ReflNullMainViewPresenter::~ReflNullMainViewPresenter() - { - } - } -} diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflBlankMainViewPresenterTest.h b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflBlankMainViewPresenterTest.h deleted file mode 100644 index ae210a0dad6b..000000000000 --- a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflBlankMainViewPresenterTest.h +++ /dev/null @@ -1,398 +0,0 @@ -#ifndef MANTID_CUSTOMINTERFACES_REFLBLANKMAINVIEWPRESENTERTEST_H_ -#define MANTID_CUSTOMINTERFACES_REFLBLANKMAINVIEWPRESENTERTEST_H_ - -#include -#include -#include -#include -#include "MantidDataObjects/TableWorkspace.h" -#include "MantidAPI/ITableWorkspace.h" -#include "MantidQtCustomInterfaces/ReflMainView.h" -#include "MantidAPI/FrameworkManager.h" -#include "MantidAPI/TableRow.h" -#include "MantidQtCustomInterfaces/ReflBlankMainViewPresenter.h" - -#include "ReflMainViewMockObjects.h" - -using namespace MantidQt::CustomInterfaces; -using namespace Mantid::API; -using namespace testing; - -class ReflBlankMainViewPresenterTest : 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 ReflBlankMainViewPresenterTest *createSuite() { return new ReflBlankMainViewPresenterTest(); } - static void destroySuite( ReflBlankMainViewPresenterTest *suite ) { delete suite; } - - void testEditSave() - { - MockView mockView; - ReflBlankMainViewPresenter presenter(&mockView); - - //Set up some data - mockView.addDataForTest(); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits "save" and and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("Workspace")); - presenter.notify(SaveFlag); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the workspace was saved correctly - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("Workspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 4); - TS_ASSERT_EQUALS(ws->String(0, RunCol), "13460"); - TS_ASSERT_EQUALS(ws->Int(0, GroupCol), 3); - - //Tidy up - AnalysisDataService::Instance().remove("Workspace"); - } - - void testSaveAs() - { - MockView mockView; - ReflBlankMainViewPresenter presenter(&mockView); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits "save as" but cancels when choosing a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("")); - presenter.notify(SaveAsFlag); - - //The user hits "save as" and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("Workspace")); - presenter.notify(SaveAsFlag); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check the workspace was saved - TS_ASSERT(AnalysisDataService::Instance().doesExist("Workspace")); - - //Tidy up - AnalysisDataService::Instance().remove("Workspace"); - } - - void testSaveProcess() - { - MockView mockView; - ReflBlankMainViewPresenter presenter(&mockView); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits "save as" but cancels when choosing a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("")); - presenter.notify(SaveAsFlag); - - //The user hits "save as" and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("Workspace")); - presenter.notify(SaveAsFlag); - - //The user hits "save" and is not asked for a name - EXPECT_CALL(mockView, askUserString(_,_,_)).Times(0); - presenter.notify(SaveFlag); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check the workspace was saved - TS_ASSERT(AnalysisDataService::Instance().doesExist("Workspace")); - - //Tidy up - AnalysisDataService::Instance().remove("Workspace"); - } - - void testAddRow() - { - MockView mockView; - ReflBlankMainViewPresenter presenter(&mockView); - - //Set up some data - mockView.addDataForTest(); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits "add row" twice with no rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(2).WillRepeatedly(Return(std::vector())); - presenter.notify(AddRowFlag); - presenter.notify(AddRowFlag); - - //The user hits "save" and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("Workspace")); - presenter.notify(SaveFlag); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the workspace was saved correctly - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("Workspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 6); - TS_ASSERT_EQUALS(ws->String(0, RunCol), "13460"); - TS_ASSERT_EQUALS(ws->String(4, RunCol), ""); - TS_ASSERT_EQUALS(ws->String(5, RunCol), ""); - TS_ASSERT_EQUALS(ws->Int(0, GroupCol), 3); - TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 1); - TS_ASSERT_EQUALS(ws->Int(4, GroupCol), 0); - TS_ASSERT_EQUALS(ws->Int(5, GroupCol), 2); - - //Tidy up - AnalysisDataService::Instance().remove("Workspace"); - } - - void testAddRowSpecify() - { - MockView mockView; - ReflBlankMainViewPresenter presenter(&mockView); - std::vector rowlist; - rowlist.push_back(1); - - //Set up some data - mockView.addDataForTest(); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits "add row twice" with the second row selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(2).WillRepeatedly(Return(rowlist)); - presenter.notify(AddRowFlag); - presenter.notify(AddRowFlag); - - //The user hits "save" and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("Workspace")); - presenter.notify(SaveFlag); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the workspace was saved correctly - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("Workspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 6); - TS_ASSERT_EQUALS(ws->String(0, RunCol), "13460"); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); - TS_ASSERT_EQUALS(ws->String(2, RunCol), ""); - TS_ASSERT_EQUALS(ws->String(3, RunCol), ""); - TS_ASSERT_EQUALS(ws->String(4, RunCol), "13469"); - TS_ASSERT_EQUALS(ws->String(5, RunCol), "13470"); - TS_ASSERT_EQUALS(ws->Int(0, GroupCol), 3); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); - TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 2); - TS_ASSERT_EQUALS(ws->Int(3, GroupCol), 0); - TS_ASSERT_EQUALS(ws->Int(4, GroupCol), 1); - TS_ASSERT_EQUALS(ws->Int(5, GroupCol), 1); - - //Tidy up - AnalysisDataService::Instance().remove("Workspace"); - } - - void testAddRowSpecifyPlural() - { - MockView mockView; - ReflBlankMainViewPresenter presenter(&mockView); - std::vector rowlist; - rowlist.push_back(1); - rowlist.push_back(2); - rowlist.push_back(3); - - //Set up some data - mockView.addDataForTest(); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits "add row" once with the second, third, and fourth rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); - presenter.notify(AddRowFlag); - - //The user hits "save" and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("Workspace")); - presenter.notify(SaveFlag); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the workspace was saved correctly - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("Workspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 5); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); - TS_ASSERT_EQUALS(ws->String(2, RunCol), "13469"); - TS_ASSERT_EQUALS(ws->String(3, RunCol), "13470"); - TS_ASSERT_EQUALS(ws->String(4, RunCol), ""); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); - TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 1); - TS_ASSERT_EQUALS(ws->Int(3, GroupCol), 1); - TS_ASSERT_EQUALS(ws->Int(4, GroupCol), 0); - - //Tidy up - AnalysisDataService::Instance().remove("Workspace"); - } - - void testDeleteRowNone() - { - MockView mockView; - ReflBlankMainViewPresenter presenter(&mockView); - - //Set up some data - mockView.addDataForTest(); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The usert hits "delete row" with no rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(std::vector())); - presenter.notify(DeleteRowFlag); - - //The user hits "save" and and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("Workspace")); - presenter.notify(SaveFlag); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the workspace was saved correctly - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("Workspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 4); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); - - //Tidy up - AnalysisDataService::Instance().remove("Workspace"); - } - - void testDeleteRowSingle() - { - MockView mockView; - ReflBlankMainViewPresenter presenter(&mockView); - std::vector rowlist; - rowlist.push_back(1); - - //Set up some data - mockView.addDataForTest(); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits "delete" with the second row selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); - presenter.notify(DeleteRowFlag); - - //The user hits "save" and and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("Workspace")); - presenter.notify(SaveFlag); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the workspace was saved correctly - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("Workspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 3); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13469"); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 1); - - //Tidy up - AnalysisDataService::Instance().remove("Workspace"); - } - - void testDeleteRowPlural() - { - MockView mockView; - ReflBlankMainViewPresenter presenter(&mockView); - std::vector rowlist; - rowlist.push_back(0); - rowlist.push_back(1); - rowlist.push_back(2); - - //Set up some data - mockView.addDataForTest(); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits "delete" with the first three rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); - presenter.notify(DeleteRowFlag); - - //The user hits "save" and and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("Workspace")); - presenter.notify(SaveFlag); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the workspace was saved correctly - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("Workspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 1); - TS_ASSERT_EQUALS(ws->String(0, RunCol), "13470"); - TS_ASSERT_EQUALS(ws->Int(0, GroupCol), 1); - - //Tidy up - AnalysisDataService::Instance().remove("Workspace"); - } - - void testGroupRows() - { - MockView mockView; - ReflBlankMainViewPresenter presenter(&mockView); - std::vector rowlist; - rowlist.push_back(1); - rowlist.push_back(2); - - //Set up some data - mockView.addDataForTest(); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits "group rows" with the middle two rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); - presenter.notify(GroupRowsFlag); - - //The user hits "save" and and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillRepeatedly(Return("Workspace")); - presenter.notify(SaveFlag); - - //Check that the workspace was saved correctly - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("Workspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 4); - TS_ASSERT_EQUALS(ws->Int(0, GroupCol), 3); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 0); - TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 0); - TS_ASSERT_EQUALS(ws->Int(3, GroupCol), 1); - - //Let's do it again, but with different rows - rowlist.clear(); - rowlist.push_back(0); - rowlist.push_back(1); - - //The user hits "group rows" with the first two rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); - presenter.notify(GroupRowsFlag); - - //The user hits save and is not asked for a workspace name - presenter.notify(SaveFlag); - - //Check that the workspace was save correctly - ws = AnalysisDataService::Instance().retrieveWS("Workspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 4); - TS_ASSERT_EQUALS(ws->Int(0, GroupCol), 2); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 2); - TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 0); - TS_ASSERT_EQUALS(ws->Int(3, GroupCol), 1); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Tidy up - AnalysisDataService::Instance().remove("Workspace"); - } -}; - -#endif /* MANTID_CUSTOMINTERFACES_REFLBLANKMAINVIEWPRESENTERTEST_H_ */ diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h deleted file mode 100644 index 85b3847dc682..000000000000 --- a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflLoadedMainViewPresenterTest.h +++ /dev/null @@ -1,625 +0,0 @@ -#ifndef MANTID_CUSTOMINTERFACES_REFLLOADEDMAINVIEWPRESENTERTEST_H_ -#define MANTID_CUSTOMINTERFACES_REFLLOADEDMAINVIEWPRESENTERTEST_H_ - -#include -#include -#include -#include -#include "MantidDataObjects/TableWorkspace.h" -#include "MantidAPI/ITableWorkspace.h" -#include "MantidQtCustomInterfaces/ReflMainView.h" -#include "MantidAPI/AlgorithmManager.h" -#include "MantidAPI/FrameworkManager.h" -#include "MantidAPI/TableRow.h" -#include "MantidQtCustomInterfaces/ReflLoadedMainViewPresenter.h" - -#include "ReflMainViewMockObjects.h" - -using namespace MantidQt::CustomInterfaces; -using namespace Mantid::API; -using namespace testing; - -//===================================================================================== -// Functional tests -//===================================================================================== -class ReflLoadedMainViewPresenterTest : public CxxTest::TestSuite -{ - -private: - - ITableWorkspace_sptr createWorkspace(const std::string& wsName = "") - { - ITableWorkspace_sptr ws = WorkspaceFactory::Instance().createTable(); - - auto colRuns = ws->addColumn("str","Run(s)"); - auto colTheta = ws->addColumn("str","ThetaIn"); - auto colTrans = ws->addColumn("str","TransRun(s)"); - auto colQmin = ws->addColumn("str","Qmin"); - auto colQmax = ws->addColumn("str","Qmax"); - auto colDqq = ws->addColumn("str","dq/q"); - auto colScale = ws->addColumn("double","Scale"); - auto colStitch = ws->addColumn("int","StitchGroup"); - auto colOptions = ws->addColumn("str","Options"); - - colRuns->setPlotType(0); - colTheta->setPlotType(0); - colTrans->setPlotType(0); - colQmin->setPlotType(0); - colQmax->setPlotType(0); - colDqq->setPlotType(0); - colScale->setPlotType(0); - colStitch->setPlotType(0); - colOptions->setPlotType(0); - - if(wsName.length() > 0) - AnalysisDataService::Instance().addOrReplace(wsName, ws); - - return ws; - } - - ITableWorkspace_sptr createPrefilledWorkspace(const std::string& wsName = "") - { - auto ws = createWorkspace(wsName); - - TableRow row = ws->appendRow(); - row << "13460" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 1.0 << 3 << ""; - row = ws->appendRow(); - row << "13462" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 1.0 << 3 << ""; - row = ws->appendRow(); - row << "13469" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 1.0 << 1 << ""; - row = ws->appendRow(); - row << "13470" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 1.0 << 1 << ""; - return ws; - } - - ITableWorkspace_sptr createBadTypedWorkspace() - { - ITableWorkspace_sptr ws = createWorkspace(); - - TableRow row = ws->appendRow(); - row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << 2.0 << 1 << ""; - - return ws; - } - - ITableWorkspace_sptr createBadLengthWorkspace(bool longer) - { - ITableWorkspace_sptr ws = createWorkspace(); - - if(longer) - ws->addColumn("str","extracolumn"); - else - ws->removeColumn("Options"); - - return ws; - } - - Workspace_sptr loadWorkspace(const std::string& filename, const std::string& wsName) - { - IAlgorithm_sptr algLoad = AlgorithmManager::Instance().create("Load"); - algLoad->initialize(); - algLoad->setProperty("Filename", filename); - algLoad->setProperty("OutputWorkspace", wsName); - algLoad->execute(); - return algLoad->getProperty("OutputWorkspace"); - } - -public: - // This pair of boilerplate methods prevent the suite being created statically - // This means the constructor isn't called when running other tests - static ReflLoadedMainViewPresenterTest *createSuite() { return new ReflLoadedMainViewPresenterTest(); } - static void destroySuite( ReflLoadedMainViewPresenterTest *suite ) { delete suite; } - - ReflLoadedMainViewPresenterTest() - { - FrameworkManager::Instance(); - } - - void testSave() - { - MockView mockView; - ReflLoadedMainViewPresenter presenter(createPrefilledWorkspace("TestWorkspace"),&mockView); - presenter.notify(SaveFlag); - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - AnalysisDataService::Instance().remove("TestWorkspace"); - } - - void testSaveAs() - { - MockView mockView; - ReflLoadedMainViewPresenter presenter(createPrefilledWorkspace("TestWorkspace"),&mockView); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits "save as" but cancels when choosing a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillOnce(Return("")); - presenter.notify(SaveAsFlag); - - //The user hits "save as" and and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillOnce(Return("Workspace")); - presenter.notify(SaveAsFlag); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the workspace was saved - TS_ASSERT(AnalysisDataService::Instance().doesExist("Workspace")); - - //Tidy up - AnalysisDataService::Instance().remove("TestWorkspace"); - AnalysisDataService::Instance().remove("Workspace"); - } - - void testSaveProcess() - { - MockView mockView; - ReflLoadedMainViewPresenter presenter(createPrefilledWorkspace("TestWorkspace"),&mockView); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits "save as" but cancels when choosing a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillOnce(Return("")); - presenter.notify(SaveAsFlag); - - //The user hits "save as" and and enters "Workspace" for a name - EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillOnce(Return("Workspace")); - presenter.notify(SaveAsFlag); - - //The user hits "save" and is not asked to enter a workspace name - EXPECT_CALL(mockView, askUserString(_,_,_)).Times(0); - presenter.notify(SaveFlag); - - //Check calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the workspace was saved - TS_ASSERT(AnalysisDataService::Instance().doesExist("Workspace")); - - //Tidy up - AnalysisDataService::Instance().remove("TestWorkspace"); - AnalysisDataService::Instance().remove("Workspace"); - } - - void testAddRow() - { - MockView mockView; - ReflLoadedMainViewPresenter presenter(createPrefilledWorkspace("TestWorkspace"),&mockView); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //Check the initial state of the table - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 4); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); - TS_ASSERT_THROWS(ws->Int(4, GroupCol), std::runtime_error); - TS_ASSERT_THROWS(ws->Int(5, GroupCol), std::runtime_error); - TS_ASSERT_THROWS(ws->Int(6, GroupCol), std::runtime_error); - - //The user hits "add row" twice with no rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(2).WillRepeatedly(Return(std::vector())); - presenter.notify(AddRowFlag); - presenter.notify(AddRowFlag); - - //The user hits "save" - presenter.notify(SaveFlag); - - //Check the calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the table has been modified correctly - ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 6); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); - TS_ASSERT_EQUALS(ws->String(4, RunCol), ""); - TS_ASSERT_EQUALS(ws->String(5, RunCol), ""); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); - TS_ASSERT_EQUALS(ws->Int(4, GroupCol), 0); - TS_ASSERT_EQUALS(ws->Int(5, GroupCol), 2); - TS_ASSERT_THROWS(ws->Int(6, GroupCol), std::runtime_error); - - //Tidy up - AnalysisDataService::Instance().remove("TestWorkspace"); - } - - void testAddRowSpecify() - { - MockView mockView; - ReflLoadedMainViewPresenter presenter(createPrefilledWorkspace("TestWorkspace"),&mockView); - std::vector rowlist; - rowlist.push_back(1); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //Check the initial state of the table - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(),4); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); - TS_ASSERT_EQUALS(ws->String(2, RunCol), "13469"); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); - TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 1); - TS_ASSERT_THROWS(ws->Int(4, GroupCol), std::runtime_error); - TS_ASSERT_THROWS(ws->Int(5, GroupCol), std::runtime_error); - TS_ASSERT_THROWS(ws->Int(6, GroupCol), std::runtime_error); - - //The user hits "add row" twice, with the second row selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(2).WillRepeatedly(Return(rowlist)); - presenter.notify(AddRowFlag); - presenter.notify(AddRowFlag); - - //The user hits "save" - presenter.notify(SaveFlag); - - //Check the calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the table has been modified correctly - ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 6); - TS_ASSERT_EQUALS(ws->String(2, RunCol), ""); - TS_ASSERT_EQUALS(ws->String(3, RunCol), ""); - TS_ASSERT_EQUALS(ws->String(4, RunCol), "13469"); - TS_ASSERT_EQUALS(ws->String(5, RunCol), "13470"); - TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 2); - TS_ASSERT_EQUALS(ws->Int(3, GroupCol), 0); - TS_ASSERT_EQUALS(ws->Int(4, GroupCol), 1); - TS_ASSERT_EQUALS(ws->Int(5, GroupCol), 1); - TS_ASSERT_THROWS(ws->Int(6, GroupCol), std::runtime_error); - - //Tidy up - AnalysisDataService::Instance().remove("TestWorkspace"); - } - - void testAddRowSpecifyPlural() - { - MockView mockView; - ReflLoadedMainViewPresenter presenter(createPrefilledWorkspace("TestWorkspace"),&mockView); - std::vector rowlist; - rowlist.push_back(1); - rowlist.push_back(2); - rowlist.push_back(3); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //Check the initial state of the table - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 4); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); - TS_ASSERT_EQUALS(ws->String(2, RunCol), "13469"); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); - TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 1); - TS_ASSERT_THROWS(ws->Int(4, GroupCol), std::runtime_error); - TS_ASSERT_THROWS(ws->Int(5, GroupCol), std::runtime_error); - TS_ASSERT_THROWS(ws->Int(6, GroupCol), std::runtime_error); - TS_ASSERT_THROWS(ws->Int(7, GroupCol), std::runtime_error); - - //The user hits "add row" once, with the second, third, and fourth row selected. - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); - presenter.notify(AddRowFlag); - - //The user hits "save" - presenter.notify(SaveFlag); - - //Check the calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the table was modified correctly - ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 5); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); - TS_ASSERT_EQUALS(ws->String(2, RunCol), "13469"); - TS_ASSERT_EQUALS(ws->String(3, RunCol), "13470"); - TS_ASSERT_EQUALS(ws->String(4, RunCol), ""); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); - TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 1); - TS_ASSERT_EQUALS(ws->Int(3, GroupCol), 1); - TS_ASSERT_EQUALS(ws->Int(4, GroupCol), 0); - TS_ASSERT_THROWS(ws->Int(5, GroupCol), std::runtime_error); - - //Tidy up - AnalysisDataService::Instance().remove("TestWorkspace"); - } - - void testDeleteRowNone() - { - MockView mockView; - ReflLoadedMainViewPresenter presenter(createPrefilledWorkspace("TestWorkspace"),&mockView); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //Check the initial state of the table - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 4); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); - - //The user hits "delete row" with no rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(std::vector())); - presenter.notify(DeleteRowFlag); - - //The user hits save - presenter.notify(SaveFlag); - - //Check the calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check that the table was not modified - ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 4); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); - - //Tidy up - AnalysisDataService::Instance().remove("TestWorkspace"); - } - - void testDeleteRowSingle() - { - MockView mockView; - ReflLoadedMainViewPresenter presenter(createPrefilledWorkspace("TestWorkspace"),&mockView); - std::vector rowlist; - rowlist.push_back(1); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //Check the initial state of the table - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 4); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); - - //The user hits "delete row" with the second row selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); - presenter.notify(DeleteRowFlag); - - //The user hits "save" - presenter.notify(SaveFlag); - - //Check the calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 3); - TS_ASSERT_EQUALS(ws->String(1, RunCol), "13469"); - TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 1); - TS_ASSERT_THROWS(ws->Int(3, GroupCol), std::runtime_error); - - //Tidy up - AnalysisDataService::Instance().remove("TestWorkspace"); - } - - void testDeleteRowPlural() - { - MockView mockView; - ReflLoadedMainViewPresenter presenter(createPrefilledWorkspace("TestWorkspace"),&mockView); - std::vector rowlist; - rowlist.push_back(0); - rowlist.push_back(1); - rowlist.push_back(2); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //Check the initial state of the table - ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 4); - TS_ASSERT_EQUALS(ws->String(0, RunCol), "13460"); - TS_ASSERT_EQUALS(ws->Int(0, GroupCol), 3); - - //The user hits "delete row" with the first three rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); - presenter.notify(DeleteRowFlag); - - //The user hits save - presenter.notify(SaveFlag); - - //Check the calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check the rows were deleted as expected - ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->rowCount(), 1); - TS_ASSERT_EQUALS(ws->String(0, RunCol), "13470"); - TS_ASSERT_EQUALS(ws->Int(0, GroupCol), 1); - TS_ASSERT_THROWS(ws->Int(1, GroupCol), std::runtime_error); - TS_ASSERT_THROWS(ws->Int(2, GroupCol), std::runtime_error); - TS_ASSERT_THROWS(ws->Int(3, GroupCol), std::runtime_error); - - //Tidy up - AnalysisDataService::Instance().remove("TestWorkspace"); - } - - void testProcess() - { - MockView mockView; - ReflLoadedMainViewPresenter presenter(createPrefilledWorkspace("TestWorkspace"),&mockView); - std::vector rowlist; - rowlist.push_back(0); - rowlist.push_back(1); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits the "process" button with the first two rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); - EXPECT_CALL(mockView, getProcessInstrument()).WillRepeatedly(Return("INTER")); - EXPECT_CALL(mockView, setProgressRange(_,_)); - EXPECT_CALL(mockView, setProgress(_)).Times(4); - presenter.notify(ProcessFlag); - - //Check the calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check output workspaces were created as expected - TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_13460")); - TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsLam_13460")); - TS_ASSERT(AnalysisDataService::Instance().doesExist("TOF_13460")); - TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_13462")); - TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsLam_13462")); - TS_ASSERT(AnalysisDataService::Instance().doesExist("TOF_13462")); - TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_13460_13462")); - TS_ASSERT(AnalysisDataService::Instance().doesExist("TRANS_13463_13464")); - - //Tidy up - AnalysisDataService::Instance().remove("TestWorkspace"); - AnalysisDataService::Instance().remove("IvsQ_13460"); - AnalysisDataService::Instance().remove("IvsLam_13460"); - AnalysisDataService::Instance().remove("TOF_13460"); - AnalysisDataService::Instance().remove("IvsQ_13462"); - AnalysisDataService::Instance().remove("IvsLam_13462"); - AnalysisDataService::Instance().remove("TOF_13462"); - AnalysisDataService::Instance().remove("IvsQ_13460_13462"); - AnalysisDataService::Instance().remove("TRANS_13463_13464"); - } - - /* - * Test processing workspaces with non-standard names, with - * and without run_number information in the sample log. - */ - void testProcessCustomNames() - { - auto ws = createWorkspace("TestWorkspace"); - TableRow row = ws->appendRow(); - row << "dataA" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 1.0 << 1; - row = ws->appendRow(); - row << "dataB" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 1.0 << 1; - - loadWorkspace("INTER13460", "dataA"); - loadWorkspace("INTER13462", "dataB"); - - //Remove the `run_number` entry from dataA's log so its run number cannot be determined that way - IAlgorithm_sptr algDelLog = AlgorithmManager::Instance().create("DeleteLog"); - algDelLog->initialize(); - algDelLog->setProperty("Workspace", "dataA"); - algDelLog->setProperty("Name", "run_number"); - algDelLog->execute(); - - MockView mockView; - ReflLoadedMainViewPresenter presenter(ws,&mockView); - std::vector rowlist; - rowlist.push_back(0); - rowlist.push_back(1); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits the "process" button with the first two rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); - EXPECT_CALL(mockView, getProcessInstrument()).WillRepeatedly(Return("INTER")); - EXPECT_CALL(mockView, setProgressRange(_,_)); - EXPECT_CALL(mockView, setProgress(_)).Times(4); - presenter.notify(ProcessFlag); - - //Check the calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check output workspaces were created as expected - TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_dataA")); - TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_13462")); - TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_dataA_13462")); - TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsLam_dataA")); - TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsLam_13462")); - - //Tidy up - AnalysisDataService::Instance().remove("TestWorkspace"); - AnalysisDataService::Instance().remove("dataA"); - AnalysisDataService::Instance().remove("dataB"); - AnalysisDataService::Instance().remove("IvsQ_dataA"); - AnalysisDataService::Instance().remove("IvsLam_dataA"); - AnalysisDataService::Instance().remove("IvsQ_13462"); - AnalysisDataService::Instance().remove("IvsLam_13462"); - AnalysisDataService::Instance().remove("IvsQ_dataA_13462"); - AnalysisDataService::Instance().remove("TRANS_13463_13464"); - } - - /* - * Test autofilling workspace values. - */ - void testAutofill() - { - auto ws = createWorkspace("TestWorkspace"); - //Autofill everything we can - TableRow row = ws->appendRow(); - row << "13460" << "" << "13463,13464" << "" << "" << "" << 1.0 << 1; - row = ws->appendRow(); - row << "13462" << "" << "13463,13464" << "" << "" << "" << 1.0 << 1; - - MockView mockView; - ReflLoadedMainViewPresenter presenter(ws,&mockView); - std::vector rowlist; - rowlist.push_back(0); - rowlist.push_back(1); - - //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); - - //The user hits the "process" button with the first two rows selected - EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); - EXPECT_CALL(mockView, getProcessInstrument()).WillRepeatedly(Return("INTER")); - EXPECT_CALL(mockView, setProgressRange(_,_)); - EXPECT_CALL(mockView, setProgress(_)).Times(4); - presenter.notify(ProcessFlag); - - //The user hits the "save" button - presenter.notify(SaveFlag); - - //Check the calls were made as expected - TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); - - //Check the table was updated as expected - ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); - TS_ASSERT_EQUALS(ws->String(0, ThetaCol), "0.7"); - TS_ASSERT_EQUALS(ws->String(0, DQQCol), "0.0340301"); - TS_ASSERT_EQUALS(ws->String(0, QMinCol), "0.009"); - TS_ASSERT_EQUALS(ws->String(0, QMaxCol), "0.154"); - - TS_ASSERT_EQUALS(ws->String(1, ThetaCol), "2.3"); - TS_ASSERT_EQUALS(ws->String(1, DQQCol), "0.0340505"); - TS_ASSERT_EQUALS(ws->String(1, QMinCol), "0.03"); - TS_ASSERT_EQUALS(ws->String(1, QMaxCol), "0.504"); - - //Tidy up - AnalysisDataService::Instance().remove("TestWorkspace"); - AnalysisDataService::Instance().remove("TRANS_13463_13464"); - AnalysisDataService::Instance().remove("TOF_13460"); - AnalysisDataService::Instance().remove("TOF_13463"); - AnalysisDataService::Instance().remove("TOF_13464"); - AnalysisDataService::Instance().remove("IvsQ_13460"); - AnalysisDataService::Instance().remove("IvsLam_13460"); - } - - void testBadWorkspaceName() - { - MockView mockView; - TS_ASSERT_THROWS(ReflLoadedMainViewPresenter presenter(createPrefilledWorkspace(),&mockView), std::runtime_error&); - } - - void testBadWorkspaceType() - { - MockView mockView; - TS_ASSERT_THROWS(ReflLoadedMainViewPresenter presenter(createBadTypedWorkspace(),&mockView), std::runtime_error&); - } - - void testBadWorkspaceShort() - { - MockView mockView; - TS_ASSERT_THROWS(ReflLoadedMainViewPresenter presenter(createBadLengthWorkspace(false),&mockView), std::runtime_error&); - } - - void testBadWorkspaceLong() - { - MockView mockView; - TS_ASSERT_THROWS(ReflLoadedMainViewPresenter presenter(createBadLengthWorkspace(true),&mockView), std::runtime_error&); - } - -}; - - -#endif /* MANTID_CUSTOMINTERFACES_REFLLOADEDMAINVIEWPRESENTERTEST_H_ */ From 53c51fc79b07f7b809ee7ef8911a446b2a3e1f56 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Fri, 17 Oct 2014 13:23:36 +0100 Subject: [PATCH 40/48] Refs #10364 Add unit tests for ReflMainViewPresenter These unit tests are by no means complete, and are due for a complete overhaul in the near future. They do however cover almost all functionality, so they are sufficient for the moment. --- .../MantidQt/CustomInterfaces/CMakeLists.txt | 1 + .../test/ReflMainViewMockObjects.h | 3 + .../test/ReflMainViewPresenterTest.h | 652 ++++++++++++++++++ 3 files changed, 656 insertions(+) create mode 100644 Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h diff --git a/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt b/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt index a99dc4acb7af..4dd68258c301 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt +++ b/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt @@ -255,6 +255,7 @@ set ( TEST_FILES IO_MuonGroupingTest.h MuonAnalysisHelperTest.h RawFileMementoTest.h + ReflMainViewPresenterTest.h WorkspaceInADSTest.h WorkspaceMementoTest.h ) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h index 312289383f2c..5c3bc391bd9f 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h @@ -9,6 +9,8 @@ using namespace MantidQt::CustomInterfaces; using namespace Mantid::API; //Clean flag aliases for use within tests. +const int NewTableFlag = ReflMainView::NewTableFlag; +const int OpenTableFlag = ReflMainView::OpenTableFlag; const int SaveAsFlag = ReflMainView::SaveAsFlag; const int SaveFlag = ReflMainView::SaveFlag; const int ProcessFlag = ReflMainView::ProcessFlag; @@ -42,6 +44,7 @@ class MockView : public ReflMainView MOCK_CONST_METHOD0(getSelectedRowIndexes, std::vector()); MOCK_CONST_METHOD0(getSearchInstrument, std::string()); MOCK_CONST_METHOD0(getProcessInstrument, std::string()); + MOCK_CONST_METHOD0(getWorkspaceToOpen, std::string()); virtual ~MockView(){} void addDataForTest() { diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h new file mode 100644 index 000000000000..a63f4de8f5a8 --- /dev/null +++ b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h @@ -0,0 +1,652 @@ +#ifndef MANTID_CUSTOMINTERFACES_REFLMAINVIEWPRESENTERTEST_H +#define MANTID_CUSTOMINTERFACES_REFLMAINVIEWPRESENTERTEST_H + +#include +#include +#include +#include +#include "MantidDataObjects/TableWorkspace.h" +#include "MantidAPI/ITableWorkspace.h" +#include "MantidQtCustomInterfaces/ReflMainView.h" +#include "MantidAPI/AlgorithmManager.h" +#include "MantidAPI/FrameworkManager.h" +#include "MantidAPI/TableRow.h" +#include "MantidQtCustomInterfaces/ReflMainViewPresenter.h" + +#include "ReflMainViewMockObjects.h" + +using namespace MantidQt::CustomInterfaces; +using namespace Mantid::API; +using namespace testing; + +//===================================================================================== +// Functional tests +//===================================================================================== +class ReflMainViewPresenterTest : public CxxTest::TestSuite +{ + +private: + + ITableWorkspace_sptr createWorkspace(const std::string& wsName = "") + { + ITableWorkspace_sptr ws = WorkspaceFactory::Instance().createTable(); + + auto colRuns = ws->addColumn("str","Run(s)"); + auto colTheta = ws->addColumn("str","ThetaIn"); + auto colTrans = ws->addColumn("str","TransRun(s)"); + auto colQmin = ws->addColumn("str","Qmin"); + auto colQmax = ws->addColumn("str","Qmax"); + auto colDqq = ws->addColumn("str","dq/q"); + auto colScale = ws->addColumn("double","Scale"); + auto colStitch = ws->addColumn("int","StitchGroup"); + auto colOptions = ws->addColumn("str","Options"); + + colRuns->setPlotType(0); + colTheta->setPlotType(0); + colTrans->setPlotType(0); + colQmin->setPlotType(0); + colQmax->setPlotType(0); + colDqq->setPlotType(0); + colScale->setPlotType(0); + colStitch->setPlotType(0); + colOptions->setPlotType(0); + + if(wsName.length() > 0) + AnalysisDataService::Instance().addOrReplace(wsName, ws); + + return ws; + } + + ITableWorkspace_sptr createPrefilledWorkspace(const std::string& wsName = "") + { + auto ws = createWorkspace(wsName); + + TableRow row = ws->appendRow(); + row << "13460" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 1.0 << 3 << ""; + row = ws->appendRow(); + row << "13462" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 1.0 << 3 << ""; + row = ws->appendRow(); + row << "13469" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 1.0 << 1 << ""; + row = ws->appendRow(); + row << "13470" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 1.0 << 1 << ""; + return ws; + } + + ITableWorkspace_sptr createBadTypedWorkspace() + { + ITableWorkspace_sptr ws = createWorkspace(); + + TableRow row = ws->appendRow(); + row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << 2.0 << 1 << ""; + + return ws; + } + + ITableWorkspace_sptr createBadLengthWorkspace(bool longer) + { + ITableWorkspace_sptr ws = createWorkspace(); + + if(longer) + ws->addColumn("str","extracolumn"); + else + ws->removeColumn("Options"); + + return ws; + } + + Workspace_sptr loadWorkspace(const std::string& filename, const std::string& wsName) + { + IAlgorithm_sptr algLoad = AlgorithmManager::Instance().create("Load"); + algLoad->initialize(); + algLoad->setProperty("Filename", filename); + algLoad->setProperty("OutputWorkspace", wsName); + algLoad->execute(); + return algLoad->getProperty("OutputWorkspace"); + } + +public: + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static ReflMainViewPresenterTest *createSuite() { return new ReflMainViewPresenterTest(); } + static void destroySuite( ReflMainViewPresenterTest *suite ) { delete suite; } + + ReflMainViewPresenterTest() + { + FrameworkManager::Instance(); + } + + void testSave() + { + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + + createPrefilledWorkspace("TestWorkspace"); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + presenter.notify(SaveFlag); + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + AnalysisDataService::Instance().remove("TestWorkspace"); + } + + void testSaveAs() + { + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + + createPrefilledWorkspace("TestWorkspace"); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + //We should not receive any errors + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + + //The user hits "save as" but cancels when choosing a name + EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillOnce(Return("")); + presenter.notify(SaveAsFlag); + + //The user hits "save as" and and enters "Workspace" for a name + EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillOnce(Return("Workspace")); + presenter.notify(SaveAsFlag); + + //Check calls were made as expected + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + + //Check that the workspace was saved + TS_ASSERT(AnalysisDataService::Instance().doesExist("Workspace")); + + //Tidy up + AnalysisDataService::Instance().remove("TestWorkspace"); + AnalysisDataService::Instance().remove("Workspace"); + } + + void testSaveProcess() + { + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + + createPrefilledWorkspace("TestWorkspace"); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + //We should not receive any errors + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + + //The user hits "save as" but cancels when choosing a name + EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillOnce(Return("")); + presenter.notify(SaveAsFlag); + + //The user hits "save as" and and enters "Workspace" for a name + EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillOnce(Return("Workspace")); + presenter.notify(SaveAsFlag); + + //The user hits "save" and is not asked to enter a workspace name + EXPECT_CALL(mockView, askUserString(_,_,_)).Times(0); + presenter.notify(SaveFlag); + + //Check calls were made as expected + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + + //Check that the workspace was saved + TS_ASSERT(AnalysisDataService::Instance().doesExist("Workspace")); + + //Tidy up + AnalysisDataService::Instance().remove("TestWorkspace"); + AnalysisDataService::Instance().remove("Workspace"); + } + + void testAddRow() + { + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + + createPrefilledWorkspace("TestWorkspace"); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + //We should not receive any errors + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + + //Check the initial state of the table + ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(), 4); + TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); + TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); + TS_ASSERT_THROWS(ws->Int(4, GroupCol), std::runtime_error); + TS_ASSERT_THROWS(ws->Int(5, GroupCol), std::runtime_error); + TS_ASSERT_THROWS(ws->Int(6, GroupCol), std::runtime_error); + + //The user hits "add row" twice with no rows selected + EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(2).WillRepeatedly(Return(std::vector())); + presenter.notify(AddRowFlag); + presenter.notify(AddRowFlag); + + //The user hits "save" + presenter.notify(SaveFlag); + + //Check the calls were made as expected + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + + //Check that the table has been modified correctly + ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(), 6); + TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); + TS_ASSERT_EQUALS(ws->String(4, RunCol), ""); + TS_ASSERT_EQUALS(ws->String(5, RunCol), ""); + TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); + TS_ASSERT_EQUALS(ws->Int(4, GroupCol), 0); + TS_ASSERT_EQUALS(ws->Int(5, GroupCol), 2); + TS_ASSERT_THROWS(ws->Int(6, GroupCol), std::runtime_error); + + //Tidy up + AnalysisDataService::Instance().remove("TestWorkspace"); + } + + void testAddRowSpecify() + { + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + + createPrefilledWorkspace("TestWorkspace"); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + std::vector rowlist; + rowlist.push_back(1); + + //We should not receive any errors + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + + //Check the initial state of the table + ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(),4); + TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); + TS_ASSERT_EQUALS(ws->String(2, RunCol), "13469"); + TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); + TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 1); + TS_ASSERT_THROWS(ws->Int(4, GroupCol), std::runtime_error); + TS_ASSERT_THROWS(ws->Int(5, GroupCol), std::runtime_error); + TS_ASSERT_THROWS(ws->Int(6, GroupCol), std::runtime_error); + + //The user hits "add row" twice, with the second row selected + EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(2).WillRepeatedly(Return(rowlist)); + presenter.notify(AddRowFlag); + presenter.notify(AddRowFlag); + + //The user hits "save" + presenter.notify(SaveFlag); + + //Check the calls were made as expected + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + + //Check that the table has been modified correctly + ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(), 6); + TS_ASSERT_EQUALS(ws->String(2, RunCol), ""); + TS_ASSERT_EQUALS(ws->String(3, RunCol), ""); + TS_ASSERT_EQUALS(ws->String(4, RunCol), "13469"); + TS_ASSERT_EQUALS(ws->String(5, RunCol), "13470"); + TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 2); + TS_ASSERT_EQUALS(ws->Int(3, GroupCol), 0); + TS_ASSERT_EQUALS(ws->Int(4, GroupCol), 1); + TS_ASSERT_EQUALS(ws->Int(5, GroupCol), 1); + TS_ASSERT_THROWS(ws->Int(6, GroupCol), std::runtime_error); + + //Tidy up + AnalysisDataService::Instance().remove("TestWorkspace"); + } + + void testAddRowSpecifyPlural() + { + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + + createPrefilledWorkspace("TestWorkspace"); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + std::vector rowlist; + rowlist.push_back(1); + rowlist.push_back(2); + rowlist.push_back(3); + + //We should not receive any errors + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + + //Check the initial state of the table + ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(), 4); + TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); + TS_ASSERT_EQUALS(ws->String(2, RunCol), "13469"); + TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); + TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 1); + TS_ASSERT_THROWS(ws->Int(4, GroupCol), std::runtime_error); + TS_ASSERT_THROWS(ws->Int(5, GroupCol), std::runtime_error); + TS_ASSERT_THROWS(ws->Int(6, GroupCol), std::runtime_error); + TS_ASSERT_THROWS(ws->Int(7, GroupCol), std::runtime_error); + + //The user hits "add row" once, with the second, third, and fourth row selected. + EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); + presenter.notify(AddRowFlag); + + //The user hits "save" + presenter.notify(SaveFlag); + + //Check the calls were made as expected + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + + //Check that the table was modified correctly + ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(), 5); + TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); + TS_ASSERT_EQUALS(ws->String(2, RunCol), "13469"); + TS_ASSERT_EQUALS(ws->String(3, RunCol), "13470"); + TS_ASSERT_EQUALS(ws->String(4, RunCol), ""); + TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); + TS_ASSERT_EQUALS(ws->Int(2, GroupCol), 1); + TS_ASSERT_EQUALS(ws->Int(3, GroupCol), 1); + TS_ASSERT_EQUALS(ws->Int(4, GroupCol), 0); + TS_ASSERT_THROWS(ws->Int(5, GroupCol), std::runtime_error); + + //Tidy up + AnalysisDataService::Instance().remove("TestWorkspace"); + } + + void testDeleteRowNone() + { + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + + createPrefilledWorkspace("TestWorkspace"); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + //We should not receive any errors + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + + //Check the initial state of the table + ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(), 4); + TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); + TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); + + //The user hits "delete row" with no rows selected + EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(std::vector())); + presenter.notify(DeleteRowFlag); + + //The user hits save + presenter.notify(SaveFlag); + + //Check the calls were made as expected + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + + //Check that the table was not modified + ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(), 4); + TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); + TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); + + //Tidy up + AnalysisDataService::Instance().remove("TestWorkspace"); + } + + void testDeleteRowSingle() + { + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + + createPrefilledWorkspace("TestWorkspace"); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + std::vector rowlist; + rowlist.push_back(1); + + //We should not receive any errors + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + + //Check the initial state of the table + ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(), 4); + TS_ASSERT_EQUALS(ws->String(1, RunCol), "13462"); + TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 3); + + //The user hits "delete row" with the second row selected + EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); + presenter.notify(DeleteRowFlag); + + //The user hits "save" + presenter.notify(SaveFlag); + + //Check the calls were made as expected + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + + ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(), 3); + TS_ASSERT_EQUALS(ws->String(1, RunCol), "13469"); + TS_ASSERT_EQUALS(ws->Int(1, GroupCol), 1); + TS_ASSERT_THROWS(ws->Int(3, GroupCol), std::runtime_error); + + //Tidy up + AnalysisDataService::Instance().remove("TestWorkspace"); + } + + void testDeleteRowPlural() + { + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + + createPrefilledWorkspace("TestWorkspace"); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + std::vector rowlist; + rowlist.push_back(0); + rowlist.push_back(1); + rowlist.push_back(2); + + //We should not receive any errors + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + + //Check the initial state of the table + ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(), 4); + TS_ASSERT_EQUALS(ws->String(0, RunCol), "13460"); + TS_ASSERT_EQUALS(ws->Int(0, GroupCol), 3); + + //The user hits "delete row" with the first three rows selected + EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); + presenter.notify(DeleteRowFlag); + + //The user hits save + presenter.notify(SaveFlag); + + //Check the calls were made as expected + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + + //Check the rows were deleted as expected + ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->rowCount(), 1); + TS_ASSERT_EQUALS(ws->String(0, RunCol), "13470"); + TS_ASSERT_EQUALS(ws->Int(0, GroupCol), 1); + TS_ASSERT_THROWS(ws->Int(1, GroupCol), std::runtime_error); + TS_ASSERT_THROWS(ws->Int(2, GroupCol), std::runtime_error); + TS_ASSERT_THROWS(ws->Int(3, GroupCol), std::runtime_error); + + //Tidy up + AnalysisDataService::Instance().remove("TestWorkspace"); + } + + void testProcess() + { + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + + createPrefilledWorkspace("TestWorkspace"); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + std::vector rowlist; + rowlist.push_back(0); + rowlist.push_back(1); + + //We should not receive any errors + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + + //The user hits the "process" button with the first two rows selected + EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); + EXPECT_CALL(mockView, getProcessInstrument()).WillRepeatedly(Return("INTER")); + EXPECT_CALL(mockView, setProgressRange(_,_)); + EXPECT_CALL(mockView, setProgress(_)).Times(4); + presenter.notify(ProcessFlag); + + //Check the calls were made as expected + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + + //Check output workspaces were created as expected + TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_13460")); + TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsLam_13460")); + TS_ASSERT(AnalysisDataService::Instance().doesExist("TOF_13460")); + TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_13462")); + TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsLam_13462")); + TS_ASSERT(AnalysisDataService::Instance().doesExist("TOF_13462")); + TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_13460_13462")); + TS_ASSERT(AnalysisDataService::Instance().doesExist("TRANS_13463_13464")); + + //Tidy up + AnalysisDataService::Instance().remove("TestWorkspace"); + AnalysisDataService::Instance().remove("IvsQ_13460"); + AnalysisDataService::Instance().remove("IvsLam_13460"); + AnalysisDataService::Instance().remove("TOF_13460"); + AnalysisDataService::Instance().remove("IvsQ_13462"); + AnalysisDataService::Instance().remove("IvsLam_13462"); + AnalysisDataService::Instance().remove("TOF_13462"); + AnalysisDataService::Instance().remove("IvsQ_13460_13462"); + AnalysisDataService::Instance().remove("TRANS_13463_13464"); + } + + /* + * Test processing workspaces with non-standard names, with + * and without run_number information in the sample log. + */ + void testProcessCustomNames() + { + auto ws = createWorkspace("TestWorkspace"); + TableRow row = ws->appendRow(); + row << "dataA" << "0.7" << "13463,13464" << "0.01" << "0.06" << "0.04" << 1.0 << 1; + row = ws->appendRow(); + row << "dataB" << "2.3" << "13463,13464" << "0.035" << "0.3" << "0.04" << 1.0 << 1; + + loadWorkspace("INTER13460", "dataA"); + loadWorkspace("INTER13462", "dataB"); + + //Remove the `run_number` entry from dataA's log so its run number cannot be determined that way + IAlgorithm_sptr algDelLog = AlgorithmManager::Instance().create("DeleteLog"); + algDelLog->initialize(); + algDelLog->setProperty("Workspace", "dataA"); + algDelLog->setProperty("Name", "run_number"); + algDelLog->execute(); + + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + std::vector rowlist; + rowlist.push_back(0); + rowlist.push_back(1); + + //We should not receive any errors + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + + //The user hits the "process" button with the first two rows selected + EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); + EXPECT_CALL(mockView, getProcessInstrument()).WillRepeatedly(Return("INTER")); + EXPECT_CALL(mockView, setProgressRange(_,_)); + EXPECT_CALL(mockView, setProgress(_)).Times(4); + presenter.notify(ProcessFlag); + + //Check the calls were made as expected + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + + //Check output workspaces were created as expected + TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_dataA")); + TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_13462")); + TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsQ_dataA_13462")); + TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsLam_dataA")); + TS_ASSERT(AnalysisDataService::Instance().doesExist("IvsLam_13462")); + + //Tidy up + AnalysisDataService::Instance().remove("TestWorkspace"); + AnalysisDataService::Instance().remove("dataA"); + AnalysisDataService::Instance().remove("dataB"); + AnalysisDataService::Instance().remove("IvsQ_dataA"); + AnalysisDataService::Instance().remove("IvsLam_dataA"); + AnalysisDataService::Instance().remove("IvsQ_13462"); + AnalysisDataService::Instance().remove("IvsLam_13462"); + AnalysisDataService::Instance().remove("IvsQ_dataA_13462"); + AnalysisDataService::Instance().remove("TRANS_13463_13464"); + } + + /* + * Test autofilling workspace values. + */ + void testAutofill() + { + auto ws = createWorkspace("TestWorkspace"); + //Autofill everything we can + TableRow row = ws->appendRow(); + row << "13460" << "" << "13463,13464" << "" << "" << "" << 1.0 << 1; + row = ws->appendRow(); + row << "13462" << "" << "13463,13464" << "" << "" << "" << 1.0 << 1; + + MockView mockView; + ReflMainViewPresenter presenter(&mockView); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + std::vector rowlist; + rowlist.push_back(0); + rowlist.push_back(1); + + //We should not receive any errors + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + + //The user hits the "process" button with the first two rows selected + EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); + EXPECT_CALL(mockView, getProcessInstrument()).WillRepeatedly(Return("INTER")); + EXPECT_CALL(mockView, setProgressRange(_,_)); + EXPECT_CALL(mockView, setProgress(_)).Times(4); + presenter.notify(ProcessFlag); + + //The user hits the "save" button + presenter.notify(SaveFlag); + + //Check the calls were made as expected + TS_ASSERT(Mock::VerifyAndClearExpectations(&mockView)); + + //Check the table was updated as expected + ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); + TS_ASSERT_EQUALS(ws->String(0, ThetaCol), "0.7"); + TS_ASSERT_EQUALS(ws->String(0, DQQCol), "0.0340301"); + TS_ASSERT_EQUALS(ws->String(0, QMinCol), "0.009"); + TS_ASSERT_EQUALS(ws->String(0, QMaxCol), "0.154"); + + TS_ASSERT_EQUALS(ws->String(1, ThetaCol), "2.3"); + TS_ASSERT_EQUALS(ws->String(1, DQQCol), "0.0340505"); + TS_ASSERT_EQUALS(ws->String(1, QMinCol), "0.03"); + TS_ASSERT_EQUALS(ws->String(1, QMaxCol), "0.504"); + + //Tidy up + AnalysisDataService::Instance().remove("TestWorkspace"); + AnalysisDataService::Instance().remove("TRANS_13463_13464"); + AnalysisDataService::Instance().remove("TOF_13460"); + AnalysisDataService::Instance().remove("TOF_13463"); + AnalysisDataService::Instance().remove("TOF_13464"); + AnalysisDataService::Instance().remove("IvsQ_13460"); + AnalysisDataService::Instance().remove("IvsLam_13460"); + } + +}; + +#endif /* MANTID_CUSTOMINTERFACES_REFLMAINVIEWPRESENTERTEST_H */ From 75675252bb72c830d8966c7973d74ab668b5a698 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Fri, 17 Oct 2014 13:28:39 +0100 Subject: [PATCH 41/48] Refs #10364 Remove ReflMainView.cpp --- .../MantidQt/CustomInterfaces/CMakeLists.txt | 1 - .../inc/MantidQtCustomInterfaces/ReflMainView.h | 4 ++-- .../CustomInterfaces/src/ReflMainView.cpp | 17 ----------------- 3 files changed, 2 insertions(+), 20 deletions(-) delete mode 100644 Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainView.cpp diff --git a/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt b/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt index 4dd68258c301..645399d4964a 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt +++ b/Code/Mantid/MantidQt/CustomInterfaces/CMakeLists.txt @@ -54,7 +54,6 @@ set ( SRC_FILES src/QtWorkspaceMementoModel.cpp src/Quasi.cpp src/RawFileMemento.cpp - src/ReflMainView.cpp src/ReflMainViewPresenter.cpp src/ResNorm.cpp src/SANSAddFiles.cpp diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h index b5978452214d..7602fab8090f 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h @@ -36,8 +36,8 @@ namespace MantidQt class DLLExport ReflMainView { public: - ReflMainView(); - virtual ~ReflMainView() = 0; + ReflMainView() {}; + virtual ~ReflMainView() {}; //Connect the model virtual void showTable(Mantid::API::ITableWorkspace_sptr model) = 0; diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainView.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainView.cpp deleted file mode 100644 index 88019bdf982a..000000000000 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainView.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "MantidQtCustomInterfaces/ReflMainView.h" -#include "MantidAPI/ITableWorkspace.h" - -namespace MantidQt -{ - namespace CustomInterfaces - { - - ReflMainView::ReflMainView() - { - } - - ReflMainView::~ReflMainView() - { - } - } -} From 0d4eca48ac9dc5846c5e3d8116fbbf528851c640 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Fri, 17 Oct 2014 13:46:10 +0100 Subject: [PATCH 42/48] Refs #10364 Move instrument list logic out of ReflMainView The presenter is now responsible for giving the view a list of valid instruments and selecting a default one. --- .../MantidQtCustomInterfaces/QtReflMainView.h | 4 +++- .../MantidQtCustomInterfaces/ReflMainView.h | 3 +++ .../CustomInterfaces/src/QtReflMainView.cpp | 22 +++++-------------- .../src/ReflMainViewPresenter.cpp | 14 ++++++++++++ .../test/ReflMainViewMockObjects.h | 2 ++ .../test/ReflMainViewPresenterTest.h | 12 ++++++++++ 6 files changed, 39 insertions(+), 18 deletions(-) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/QtReflMainView.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/QtReflMainView.h index cd6448572107..2dcee3bf647c 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/QtReflMainView.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/QtReflMainView.h @@ -61,6 +61,9 @@ namespace MantidQt virtual void setProgressRange(int min, int max); virtual void setProgress(int progress); + //Settor methods + virtual void setInstrumentList(const std::vector& instruments, const std::string& defaultInstrument); + //Accessor methods virtual std::vector getSelectedRowIndexes() const; virtual std::string getSearchInstrument() const; @@ -70,7 +73,6 @@ namespace MantidQt private: //initialise the interface virtual void initLayout(); - virtual void setInstrumentList(const std::vector& instruments); //the presenter boost::shared_ptr m_presenter; //the interface diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h index 7602fab8090f..7507cf17e664 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainView.h @@ -53,6 +53,9 @@ namespace MantidQt virtual void setProgressRange(int min, int max) = 0; virtual void setProgress(int progress) = 0; + //Settor methods + virtual void setInstrumentList(const std::vector& instruments, const std::string& defaultInstrument) = 0; + //Accessor methods virtual std::vector getSelectedRowIndexes() const = 0; virtual std::string getSearchInstrument() const = 0; diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp index d0e531b8bce8..67c853de5cfc 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp @@ -44,22 +44,6 @@ namespace MantidQt ui.progressBar->setRange(0, 100); ui.progressBar->setValue(0); - //Set up the instrument selectors - std::vector instruments; - instruments.push_back("INTER"); - instruments.push_back("SURF"); - instruments.push_back("CRISP"); - instruments.push_back("POLREF"); - setInstrumentList(instruments); - - const std::string defaultInst = Mantid::Kernel::ConfigService::Instance().getString("default.instrument"); - if(std::find(instruments.begin(), instruments.end(), defaultInst) != instruments.end()) - { - int index = ui.comboSearchInstrument->findData(QString::fromStdString(defaultInst), Qt::DisplayRole); - ui.comboSearchInstrument->setCurrentIndex(index); - ui.comboProcessInstrument->setCurrentIndex(index); - } - //Allow rows to be reordered ui.viewTable->verticalHeader()->setMovable(true); @@ -237,7 +221,7 @@ namespace MantidQt Set the list of available instruments to search and process for @param instruments : The list of instruments available */ - void QtReflMainView::setInstrumentList(const std::vector& instruments) + void QtReflMainView::setInstrumentList(const std::vector& instruments, const std::string& defaultInstrument) { ui.comboSearchInstrument->clear(); ui.comboProcessInstrument->clear(); @@ -248,6 +232,10 @@ namespace MantidQt ui.comboSearchInstrument->addItem(instrument); ui.comboProcessInstrument->addItem(instrument); } + + int index = ui.comboSearchInstrument->findData(QString::fromStdString(defaultInstrument), Qt::DisplayRole); + ui.comboSearchInstrument->setCurrentIndex(index); + ui.comboProcessInstrument->setCurrentIndex(index); } /** diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp index 8fbbf8d41c72..6218fcde8e57 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp @@ -73,6 +73,20 @@ namespace MantidQt { ReflMainViewPresenter::ReflMainViewPresenter(ReflMainView* view): m_model(NULL), m_view(view) { + //Set up the instrument selectors + std::vector instruments; + instruments.push_back("INTER"); + instruments.push_back("SURF"); + instruments.push_back("CRISP"); + instruments.push_back("POLREF"); + + //If the user's configured default instrument is in this list, set it as the default, otherwise use INTER + const std::string defaultInst = Mantid::Kernel::ConfigService::Instance().getString("default.instrument"); + if(std::find(instruments.begin(), instruments.end(), defaultInst) != instruments.end()) + m_view->setInstrumentList(instruments, defaultInst); + else + m_view->setInstrumentList(instruments, "INTER"); + newTable(); } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h index 5c3bc391bd9f..616829c8e952 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewMockObjects.h @@ -41,6 +41,8 @@ class MockView : public ReflMainView MOCK_METHOD2(giveUserWarning, void(std::string, std::string)); MOCK_METHOD2(setProgressRange, void(int, int)); MOCK_METHOD1(setProgress, void(int)); + MOCK_METHOD2(setInstrumentList, void(const std::vector& instruments, const std::string& defaultInstrument)); + MOCK_METHOD1(setInstrument, void(const std::string&)); MOCK_CONST_METHOD0(getSelectedRowIndexes, std::vector()); MOCK_CONST_METHOD0(getSearchInstrument, std::string()); MOCK_CONST_METHOD0(getProcessInstrument, std::string()); diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h index a63f4de8f5a8..148ea5511ada 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h @@ -118,6 +118,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite void testSave() { MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); createPrefilledWorkspace("TestWorkspace"); @@ -132,6 +133,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite void testSaveAs() { MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); createPrefilledWorkspace("TestWorkspace"); @@ -163,6 +165,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite void testSaveProcess() { MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); createPrefilledWorkspace("TestWorkspace"); @@ -198,6 +201,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite void testAddRow() { MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); createPrefilledWorkspace("TestWorkspace"); @@ -245,6 +249,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite void testAddRowSpecify() { MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); createPrefilledWorkspace("TestWorkspace"); @@ -299,6 +304,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite void testAddRowSpecifyPlural() { MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); createPrefilledWorkspace("TestWorkspace"); @@ -355,6 +361,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite void testDeleteRowNone() { MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); createPrefilledWorkspace("TestWorkspace"); @@ -393,6 +400,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite void testDeleteRowSingle() { MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); createPrefilledWorkspace("TestWorkspace"); @@ -434,6 +442,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite void testDeleteRowPlural() { MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); createPrefilledWorkspace("TestWorkspace"); @@ -480,6 +489,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite void testProcess() { MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); createPrefilledWorkspace("TestWorkspace"); @@ -548,6 +558,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite algDelLog->execute(); MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); presenter.notify(OpenTableFlag); @@ -601,6 +612,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite row << "13462" << "" << "13463,13464" << "" << "" << "" << 1.0 << 1; MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); ReflMainViewPresenter presenter(&mockView); EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); presenter.notify(OpenTableFlag); From 051d75611562c5a909f9dacf1a2f014f0ef9664f Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Fri, 17 Oct 2014 14:21:36 +0100 Subject: [PATCH 43/48] Refs #10364 Test loading invalid workspaces The old tests were dropped during the merge. Let's re-add them. --- .../src/ReflMainViewPresenter.cpp | 10 +- .../test/ReflMainViewPresenterTest.h | 111 ++++++++++++------ 2 files changed, 85 insertions(+), 36 deletions(-) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp index 6218fcde8e57..4f661b2952f2 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp @@ -734,7 +734,15 @@ namespace MantidQt } ITableWorkspace_sptr newModel = AnalysisDataService::Instance().retrieveWS(toOpen); - checkValidModel(newModel); + try + { + checkValidModel(newModel); + } + catch(std::runtime_error& e) + { + m_view->giveUserCritical("Invalid workspace to open:\n" + std::string(e.what()), "Error"); + return; + } m_model = newModel; m_wsName = toOpen; diff --git a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h index 148ea5511ada..54f816cd0c3c 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/test/ReflMainViewPresenterTest.h @@ -27,7 +27,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite private: - ITableWorkspace_sptr createWorkspace(const std::string& wsName = "") + ITableWorkspace_sptr createWorkspace(const std::string& wsName) { ITableWorkspace_sptr ws = WorkspaceFactory::Instance().createTable(); @@ -57,7 +57,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite return ws; } - ITableWorkspace_sptr createPrefilledWorkspace(const std::string& wsName = "") + ITableWorkspace_sptr createPrefilledWorkspace(const std::string& wsName) { auto ws = createWorkspace(wsName); @@ -72,28 +72,6 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite return ws; } - ITableWorkspace_sptr createBadTypedWorkspace() - { - ITableWorkspace_sptr ws = createWorkspace(); - - TableRow row = ws->appendRow(); - row << "13460" << "0.7" << "13463" << "0.01" << "0.06" << "0.04" << 2.0 << 1 << ""; - - return ws; - } - - ITableWorkspace_sptr createBadLengthWorkspace(bool longer) - { - ITableWorkspace_sptr ws = createWorkspace(); - - if(longer) - ws->addColumn("str","extracolumn"); - else - ws->removeColumn("Options"); - - return ws; - } - Workspace_sptr loadWorkspace(const std::string& filename, const std::string& wsName) { IAlgorithm_sptr algLoad = AlgorithmManager::Instance().create("Load"); @@ -141,7 +119,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite presenter.notify(OpenTableFlag); //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); //The user hits "save as" but cancels when choosing a name EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillOnce(Return("")); @@ -173,7 +151,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite presenter.notify(OpenTableFlag); //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); //The user hits "save as" but cancels when choosing a name EXPECT_CALL(mockView, askUserString(_,_,"Workspace")).Times(1).WillOnce(Return("")); @@ -209,7 +187,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite presenter.notify(OpenTableFlag); //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); //Check the initial state of the table ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); @@ -260,7 +238,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite rowlist.push_back(1); //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); //Check the initial state of the table ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); @@ -317,7 +295,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite rowlist.push_back(3); //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); //Check the initial state of the table ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); @@ -369,7 +347,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite presenter.notify(OpenTableFlag); //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); //Check the initial state of the table ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); @@ -411,7 +389,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite rowlist.push_back(1); //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); //Check the initial state of the table ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); @@ -455,7 +433,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite rowlist.push_back(2); //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); //Check the initial state of the table ITableWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS("TestWorkspace"); @@ -501,7 +479,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite rowlist.push_back(1); //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); //The user hits the "process" button with the first two rows selected EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); @@ -568,7 +546,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite rowlist.push_back(1); //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); //The user hits the "process" button with the first two rows selected EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); @@ -622,7 +600,7 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite rowlist.push_back(1); //We should not receive any errors - EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(0); //The user hits the "process" button with the first two rows selected EXPECT_CALL(mockView, getSelectedRowIndexes()).Times(1).WillRepeatedly(Return(rowlist)); @@ -659,6 +637,69 @@ class ReflMainViewPresenterTest : public CxxTest::TestSuite AnalysisDataService::Instance().remove("IvsLam_13460"); } + void testBadWorkspaceType() + { + ITableWorkspace_sptr ws = WorkspaceFactory::Instance().createTable(); + + //Wrong types + ws->addColumn("str","Run(s)"); + ws->addColumn("str","ThetaIn"); + ws->addColumn("str","TransRun(s)"); + ws->addColumn("str","Qmin"); + ws->addColumn("str","Qmax"); + ws->addColumn("str","dq/q"); + ws->addColumn("str","Scale"); + ws->addColumn("str","StitchGroup"); + ws->addColumn("str","Options"); + + AnalysisDataService::Instance().addOrReplace("TestWorkspace", ws); + + MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); + ReflMainViewPresenter presenter(&mockView); + + //We should receive an error + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(1); + + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(1).WillRepeatedly(Return("TestWorkspace")); + presenter.notify(OpenTableFlag); + + AnalysisDataService::Instance().remove("TestWorkspace"); + } + + void testBadWorkspaceLength() + { + MockView mockView; + EXPECT_CALL(mockView, setInstrumentList(_,_)).Times(1); + ReflMainViewPresenter presenter(&mockView); + + //Because we to open twice, get an error twice + EXPECT_CALL(mockView, giveUserCritical(_,_)).Times(2); + EXPECT_CALL(mockView, getWorkspaceToOpen()).Times(2).WillRepeatedly(Return("TestWorkspace")); + + ITableWorkspace_sptr ws = WorkspaceFactory::Instance().createTable(); + ws->addColumn("str","Run(s)"); + ws->addColumn("str","ThetaIn"); + ws->addColumn("str","TransRun(s)"); + ws->addColumn("str","Qmin"); + ws->addColumn("str","Qmax"); + ws->addColumn("str","dq/q"); + ws->addColumn("double","Scale"); + ws->addColumn("int","StitchGroup"); + AnalysisDataService::Instance().addOrReplace("TestWorkspace", ws); + + //Try to open with too few columns + presenter.notify(OpenTableFlag); + + ws->addColumn("str","OptionsA"); + ws->addColumn("str","OptionsB"); + AnalysisDataService::Instance().addOrReplace("TestWorkspace", ws); + + //Try to open with too many columns + presenter.notify(OpenTableFlag); + + AnalysisDataService::Instance().remove("TestWorkspace"); + } }; #endif /* MANTID_CUSTOMINTERFACES_REFLMAINVIEWPRESENTERTEST_H */ From 10f9827938225da4e0d29bf179dd09c8c62977a0 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Fri, 17 Oct 2014 14:30:53 +0100 Subject: [PATCH 44/48] Refs #10364 Cheer up doxygen --- Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp index 67c853de5cfc..bcd3a0c71a05 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp @@ -220,6 +220,7 @@ namespace MantidQt /** Set the list of available instruments to search and process for @param instruments : The list of instruments available + @param defaultInstrument : The instrument to have selected by default */ void QtReflMainView::setInstrumentList(const std::vector& instruments, const std::string& defaultInstrument) { From 36052a719f31b78900c2725e74851cfa8b4f1e29 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Fri, 17 Oct 2014 14:37:40 +0100 Subject: [PATCH 45/48] Refs #10364 Fix build on Win7 and RHEL6 --- Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp | 2 +- .../MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp index bcd3a0c71a05..9d16ea5a4f21 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/QtReflMainView.cpp @@ -17,7 +17,7 @@ namespace MantidQt //---------------------------------------------------------------------------------------------- /** Constructor */ - QtReflMainView::QtReflMainView(QWidget *parent) : UserSubWindow(parent), m_presenter(NULL) + QtReflMainView::QtReflMainView(QWidget *parent) : UserSubWindow(parent) { } diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp index 4f661b2952f2..6d2bcfc0a0b4 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp @@ -71,7 +71,7 @@ namespace MantidQt { namespace CustomInterfaces { - ReflMainViewPresenter::ReflMainViewPresenter(ReflMainView* view): m_model(NULL), m_view(view) + ReflMainViewPresenter::ReflMainViewPresenter(ReflMainView* view): m_view(view) { //Set up the instrument selectors std::vector instruments; From e101163cda165667ca7eca2a93beb2e48aee23f8 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Mon, 20 Oct 2014 09:57:56 +0100 Subject: [PATCH 46/48] Refs #10389 Scale column defaults to 1 --- .../CustomInterfaces/src/ReflMainViewPresenter.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp index 6d2bcfc0a0b4..97e0d4b71083 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp @@ -62,7 +62,6 @@ namespace colStitch->setPlotType(0); colOptions->setPlotType(0); - ws->appendRow(); return ws; } } @@ -624,6 +623,9 @@ namespace MantidQt row = m_model->insertRow(*rows.rbegin() + 1); } + //Set the default scale to 1.0 + m_model->Double(row, COL_SCALE) = 1.0; + //Set the group id of the new row m_model->Int(row, COL_GROUP) = groupId; @@ -714,6 +716,9 @@ namespace MantidQt m_model = createWorkspace(); m_wsName.clear(); m_view->showTable(m_model); + + //Start with one blank row + addRow(); } /** From ae81db24ed33fc0eb917a85607d1563a1f2644ad Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Mon, 20 Oct 2014 09:58:57 +0100 Subject: [PATCH 47/48] Refs #10389 Scale IvsQ workspaces using scale column --- .../CustomInterfaces/src/ReflMainViewPresenter.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp index 97e0d4b71083..f0c9d825b638 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp @@ -426,6 +426,20 @@ namespace MantidQt if(!algReflOne->isExecuted()) throw std::runtime_error("Failed to run ReflectometryReductionOneAuto."); + const double scale = m_model->Double(rowNo, COL_SCALE); + if(scale != 1.0) + { + IAlgorithm_sptr algScale = AlgorithmManager::Instance().create("Scale"); + algScale->initialize(); + algScale->setProperty("InputWorkspace", "IvsQ_" + runNo); + algScale->setProperty("OutputWorkspace", "IvsQ_" + runNo); + algScale->setProperty("Factor", 1.0 / scale); + algScale->execute(); + + if(!algScale->isExecuted()) + throw std::runtime_error("Failed to run Scale algorithm"); + } + //Processing has completed. Put Qmin and Qmax into the table if needed, for stitching. if(m_model->String(rowNo, COL_QMIN).empty() || m_model->String(rowNo, COL_QMAX).empty()) { From c84971080f82bf59fb9db69c12c61ce4ab0d6bf7 Mon Sep 17 00:00:00 2001 From: Harry Jeffery Date: Mon, 20 Oct 2014 10:03:35 +0100 Subject: [PATCH 48/48] Refs #10389 Generalise row insertion Sidestep the unit test issue by providing an internal row insertion method that does not disturb the view. This method may also be useful in future if we want to provide insertBefore/insertAfter actions. --- .../ReflMainViewPresenter.h | 2 + .../src/ReflMainViewPresenter.cpp | 40 +++++++++---------- 2 files changed, 20 insertions(+), 22 deletions(-) diff --git a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainViewPresenter.h b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainViewPresenter.h index c820f8a427f5..e0f2c5b30283 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainViewPresenter.h +++ b/Code/Mantid/MantidQt/CustomInterfaces/inc/MantidQtCustomInterfaces/ReflMainViewPresenter.h @@ -65,6 +65,8 @@ namespace MantidQt void processRow(size_t rowNo); //Stitch some rows void stitchRows(std::vector rows); + //insert a row in the model before the given index + virtual void insertRow(size_t before); //add row(s) to the model virtual void addRow(); //delete row(s) from the model diff --git a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp index f0c9d825b638..c92af568545d 100644 --- a/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp +++ b/Code/Mantid/MantidQt/CustomInterfaces/src/ReflMainViewPresenter.cpp @@ -615,38 +615,34 @@ namespace MantidQt } /** - Add row(s) to the model + Inserts a new row in the specified location + @param before The index to insert the new row before */ - void ReflMainViewPresenter::addRow() + void ReflMainViewPresenter::insertRow(size_t before) { - std::vector rows = m_view->getSelectedRowIndexes(); - std::sort(rows.begin(), rows.end()); - const int groupId = getUnusedGroup(); - size_t row = 0; - - if(rows.size() == 0) - { - //No rows selected, just append a new row - row = m_model->rowCount(); - m_model->appendRow(); - } - else - { - //One or more rows selected, insert after the last row - row = m_model->insertRow(*rows.rbegin() + 1); - } - + size_t row = m_model->insertRow(before); //Set the default scale to 1.0 m_model->Double(row, COL_SCALE) = 1.0; - //Set the group id of the new row m_model->Int(row, COL_GROUP) = groupId; - //Make sure the view updates m_view->showTable(m_model); } + /** + Add row(s) to the model + */ + void ReflMainViewPresenter::addRow() + { + std::vector rows = m_view->getSelectedRowIndexes(); + std::sort(rows.begin(), rows.end()); + if(rows.size() == 0) + insertRow(m_model->rowCount()); + else + insertRow(*rows.rbegin() + 1); + } + /** Delete row(s) from the model */ @@ -732,7 +728,7 @@ namespace MantidQt m_view->showTable(m_model); //Start with one blank row - addRow(); + insertRow(0); } /**