diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/IPeak.h b/Code/Mantid/Framework/API/inc/MantidAPI/IPeak.h index 442f9cca369e..0ee7a8d085ff 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/IPeak.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/IPeak.h @@ -7,6 +7,7 @@ #include "MantidKernel/Matrix.h" #include "MantidKernel/V3D.h" #include "MantidKernel/PhysicalConstants.h" +#include namespace Mantid { namespace API { @@ -50,9 +51,9 @@ class MANTID_API_DLL IPeak { virtual bool findDetector() = 0; virtual void setQSampleFrame(Mantid::Kernel::V3D QSampleFrame, - double detectorDistance = 1.0) = 0; + boost::optional detectorDistance) = 0; virtual void setQLabFrame(Mantid::Kernel::V3D QLabFrame, - double detectorDistance = 1.0) = 0; + boost::optional detectorDistance) = 0; virtual void setWavelength(double wavelength) = 0; virtual double getWavelength() const = 0; diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/IPeaksWorkspace.h b/Code/Mantid/Framework/API/inc/MantidAPI/IPeaksWorkspace.h index 7282be8542cc..15bd48bf8b94 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/IPeaksWorkspace.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/IPeaksWorkspace.h @@ -7,6 +7,7 @@ #include "MantidAPI/ITableWorkspace.h" #include "MantidAPI/ExperimentInfo.h" #include "MantidKernel/SpecialCoordinateSystem.h" +#include namespace Mantid { @@ -98,12 +99,20 @@ class MANTID_API_DLL IPeaksWorkspace : public ITableWorkspace, //--------------------------------------------------------------------------------------------- /** Create an instance of a Peak - * @param QLabFrame :: Q of the center of the peak, in reciprocal space - * @param detectorDistance :: distance between the sample and the detector. + * @param QLabFrame :: Q of the center of the peak in the lab frame, in reciprocal space + * @param detectorDistance :: Optional distance between the sample and the detector. Calculated if not provided. * @return a pointer to a new Peak object. */ virtual IPeak *createPeak(Mantid::Kernel::V3D QLabFrame, - double detectorDistance = 1.0) const = 0; + boost::optional detectorDistance) const = 0; + + + /** + * Create an instance of a peak using a V3D + * @param HKL V3D + * @return a pointer to a new Peak object. + */ + virtual IPeak *createPeakHKL(Mantid::Kernel::V3D HKL) const = 0; //--------------------------------------------------------------------------------------------- /** Determine if the workspace has been integrated using a peaks integration diff --git a/Code/Mantid/Framework/API/test/MockObjects.h b/Code/Mantid/Framework/API/test/MockObjects.h index a87670dc88ec..591c063b6e13 100644 --- a/Code/Mantid/Framework/API/test/MockObjects.h +++ b/Code/Mantid/Framework/API/test/MockObjects.h @@ -103,9 +103,9 @@ namespace MOCK_METHOD0(findDetector, bool()); MOCK_METHOD2(setQSampleFrame, - void(Mantid::Kernel::V3D QSampleFrame, double detectorDistance)); + void(Mantid::Kernel::V3D QSampleFrame, boost::optional detectorDistance)); MOCK_METHOD2(setQLabFrame, - void(Mantid::Kernel::V3D QLabFrame, double detectorDistance)); + void(Mantid::Kernel::V3D QLabFrame, boost::optional detectorDistance)); MOCK_METHOD1(setWavelength, void(double wavelength)); MOCK_CONST_METHOD0(getWavelength, diff --git a/Code/Mantid/Framework/Algorithms/src/CreateSampleWorkspace.cpp b/Code/Mantid/Framework/Algorithms/src/CreateSampleWorkspace.cpp index f04d8ac8bb3f..99496421e98c 100644 --- a/Code/Mantid/Framework/Algorithms/src/CreateSampleWorkspace.cpp +++ b/Code/Mantid/Framework/Algorithms/src/CreateSampleWorkspace.cpp @@ -396,7 +396,7 @@ Instrument_sptr CreateSampleWorkspace::createTestInstrumentRectangular( int num_banks, int pixels, double pixelSpacing) { boost::shared_ptr testInst(new Instrument("basic_rect")); testInst->setReferenceFrame( - boost::shared_ptr(new ReferenceFrame(Y, X, Left, ""))); + boost::shared_ptr(new ReferenceFrame(Y, Z, Left, ""))); const double cylRadius(pixelSpacing / 2); const double cylHeight(0.0002); diff --git a/Code/Mantid/Framework/Crystal/CMakeLists.txt b/Code/Mantid/Framework/Crystal/CMakeLists.txt index 5498af2dfec0..24d7be132d25 100644 --- a/Code/Mantid/Framework/Crystal/CMakeLists.txt +++ b/Code/Mantid/Framework/Crystal/CMakeLists.txt @@ -1,5 +1,6 @@ set ( SRC_FILES + src/AddPeakHKL.cpp src/AnvredCorrection.cpp src/CalculatePeaksHKL.cpp src/CalculateUMatrix.cpp @@ -69,6 +70,7 @@ set ( SRC_FILES set ( SRC_UNITY_IGNORE_FILES ) set ( INC_FILES + inc/MantidCrystal/AddPeakHKL.h inc/MantidCrystal/AnvredCorrection.h inc/MantidCrystal/BackgroundStrategy.h inc/MantidCrystal/CalculatePeaksHKL.h @@ -140,6 +142,7 @@ set ( INC_FILES ) set ( TEST_FILES + AddPeakHKLTest.h AnvredCorrectionTest.h CalculatePeaksHKLTest.h CalculateUMatrixTest.h diff --git a/Code/Mantid/Framework/Crystal/inc/MantidCrystal/AddPeakHKL.h b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/AddPeakHKL.h new file mode 100644 index 000000000000..055eadd3b7af --- /dev/null +++ b/Code/Mantid/Framework/Crystal/inc/MantidCrystal/AddPeakHKL.h @@ -0,0 +1,56 @@ +#ifndef MANTID_CRYSTAL_ADDPEAKHKL_H_ +#define MANTID_CRYSTAL_ADDPEAKHKL_H_ + +#include "MantidKernel/System.h" +#include "MantidAPI/Algorithm.h" + +namespace Mantid +{ +namespace Crystal +{ + + /** AddPeakHKL : Algorithm to add a peaks to a PeaksWorkspace in the HKL frame + + Copyright © 2015 ISIS Rutherford Appleton Laboratory, NScD Oak Ridge National Laboratory & European Spallation Source + + 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 AddPeakHKL : public API::Algorithm + { + public: + AddPeakHKL(); + virtual ~AddPeakHKL(); + + virtual const std::string name() const; + virtual int version() const; + virtual const std::string category() const; + virtual const std::string summary() const; + + private: + void init(); + void exec(); + + + }; + + +} // namespace Crystal +} // namespace Mantid + +#endif /* MANTID_CRYSTAL_ADDPEAKHKL_H_ */ diff --git a/Code/Mantid/Framework/Crystal/src/AddPeakHKL.cpp b/Code/Mantid/Framework/Crystal/src/AddPeakHKL.cpp new file mode 100644 index 000000000000..78c563d096e9 --- /dev/null +++ b/Code/Mantid/Framework/Crystal/src/AddPeakHKL.cpp @@ -0,0 +1,74 @@ +#include "MantidCrystal/AddPeakHKL.h" +#include "MantidAPI/IPeaksWorkspace.h" +#include "MantidAPI/IPeak.h" +#include "MantidKernel/ArrayProperty.h" +#include "MantidKernel/ArrayLengthValidator.h" +#include "MantidKernel/V3D.h" + +namespace Mantid +{ +namespace Crystal +{ + + using namespace Mantid::Kernel; + using namespace Mantid::API; + + // Register the algorithm into the AlgorithmFactory + DECLARE_ALGORITHM(AddPeakHKL) + + + + //---------------------------------------------------------------------------------------------- + /** Constructor + */ + AddPeakHKL::AddPeakHKL() + { + } + + //---------------------------------------------------------------------------------------------- + /** Destructor + */ + AddPeakHKL::~AddPeakHKL() + { + } + + + //---------------------------------------------------------------------------------------------- + + /// Algorithms name for identification. @see Algorithm::name + const std::string AddPeakHKL::name() const { return "AddPeakHKL"; } + + /// Algorithm's version for identification. @see Algorithm::version + int AddPeakHKL::version() const { return 1;}; + + /// Algorithm's category for identification. @see Algorithm::category + const std::string AddPeakHKL::category() const { return "Crystal";} + + /// Algorithm's summary for use in the GUI and help. @see Algorithm::summary + const std::string AddPeakHKL::summary() const { return "Add a peak in the hkl frame";}; + + //---------------------------------------------------------------------------------------------- + /** Initialize the algorithm's properties. + */ + void AddPeakHKL::init() + { + declareProperty(new WorkspaceProperty("Workspace","",Direction::InOut), "An input workspace."); + declareProperty(new ArrayProperty("HKL", boost::make_shared > (3)), "HKL point to add"); + } + + //---------------------------------------------------------------------------------------------- + /** Execute the algorithm. + */ + void AddPeakHKL::exec() + { + IPeaksWorkspace_sptr peakWS = this->getProperty("Workspace"); + const std::vector hklValue = this->getProperty("HKL"); + IPeak * peak = peakWS->createPeakHKL(V3D(hklValue[0], hklValue[1], hklValue[2])); + peakWS->addPeak(*peak); + delete peak; + } + + + +} // namespace Crystal +} // namespace Mantid diff --git a/Code/Mantid/Framework/Crystal/src/PredictPeaks.cpp b/Code/Mantid/Framework/Crystal/src/PredictPeaks.cpp index 541403979d91..ba77553c1c38 100644 --- a/Code/Mantid/Framework/Crystal/src/PredictPeaks.cpp +++ b/Code/Mantid/Framework/Crystal/src/PredictPeaks.cpp @@ -145,7 +145,7 @@ void PredictPeaks::doHKL(const double h, const double k, const double l, PARALLEL_CRITICAL(PredictPeaks_numInRange) { numInRange++; } // Create the peak using the Q in the lab framewith all its info: - Peak p(inst, q); + Peak p(inst, q, boost::optional()); if (p.findDetector()) { // Only add peaks that hit the detector p.setGoniometerMatrix(gonio); diff --git a/Code/Mantid/Framework/Crystal/test/AddPeakHKLTest.h b/Code/Mantid/Framework/Crystal/test/AddPeakHKLTest.h new file mode 100644 index 000000000000..0c842e3da845 --- /dev/null +++ b/Code/Mantid/Framework/Crystal/test/AddPeakHKLTest.h @@ -0,0 +1,112 @@ +#ifndef MANTID_CRYSTAL_ADDPEAKHKLTEST_H_ +#define MANTID_CRYSTAL_ADDPEAKHKLTEST_H_ + +#include +#include "MantidCrystal/AddPeakHKL.h" +#include "MantidKernel/V3D.h" +#include "MantidAPI/AnalysisDataService.h" +#include "MantidDataObjects/PeaksWorkspace.h" +#include "MantidGeometry/Crystal/OrientedLattice.h" +#include "MantidTestHelpers/ComponentCreationHelper.h" + +using Mantid::Crystal::AddPeakHKL; +using namespace Mantid::Kernel; +using namespace Mantid::API; +using namespace Mantid::DataObjects; + +class AddPeakHKLTest : 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 AddPeakHKLTest *createSuite() { return new AddPeakHKLTest(); } + static void destroySuite( AddPeakHKLTest *suite ) { delete suite; } + + + void test_Init() + { + AddPeakHKL alg; + TS_ASSERT_THROWS_NOTHING( alg.initialize() ) + TS_ASSERT( alg.isInitialized() ) + } + + void test_hkl_validation() + { + AddPeakHKL alg; + alg.initialize(); + std::vector hkl_bad(4); // Too big! + TS_ASSERT_THROWS( alg.setProperty("HKL", hkl_bad), std::invalid_argument& ); + + std::vector hkl_good(3, 0); // Right size. + TS_ASSERT_THROWS_NOTHING( alg.setProperty("HKL", hkl_good) ); + } + + + void test_exec() + { + // Create simple fictional instrument + const V3D source(0,0,0); + const V3D sample(15, 0, 0); + const V3D detectorPos(20, 5, 0); + const V3D beam1 = sample - source; + const V3D beam2 = detectorPos - sample; + auto minimalInstrument = ComponentCreationHelper::createMinimalInstrument( source, sample, detectorPos ); + + // Derive distances and angles + const double l1 = beam1.norm(); + const double l2 = beam2.norm(); + const V3D qLabDir = (beam1/l1) - (beam2/l2); + + const double microSecsInSec = 1e6; + + // Derive QLab for diffraction + const double wavenumber_in_angstrom_times_tof_in_microsec = + (Mantid::PhysicalConstants::NeutronMass * (l1 + l2) * 1e-10 * microSecsInSec) / + Mantid::PhysicalConstants::h_bar; + V3D qLab = qLabDir * wavenumber_in_angstrom_times_tof_in_microsec; + + Mantid::Geometry::OrientedLattice orientedLattice(1, 1, 1, 90, 90, 90); // U is identity, real and reciprocal lattice vectors are identical. + Mantid::Geometry::Goniometer goniometer; // identity + V3D hkl = qLab / (2 * M_PI); // Given our settings above, this is the simplified relationship between qLab and hkl. + + // Now create a peaks workspace around the simple fictional instrument + PeaksWorkspace_sptr ws = boost::make_shared(); + ws->setInstrument(minimalInstrument); + ws->mutableSample().setOrientedLattice(&orientedLattice); + ws->mutableRun().setGoniometer(goniometer, false); + + AddPeakHKL alg; + alg.setChild(true); + alg.initialize(); + std::vector hklVec; + hklVec.push_back(hkl.X()); + hklVec.push_back(hkl.Y()); + hklVec.push_back(hkl.Z()); + alg.setProperty("HKL", hklVec); + alg.setProperty("Workspace", ws); + alg.execute(); + IPeaksWorkspace_sptr ws_out = alg.getProperty("Workspace"); + + // Get the peak just added. + const IPeak& peak =ws_out->getPeak(0); + + /* + Now we check we have made a self - consistent peak + */ + TSM_ASSERT_EQUALS("New peak should have HKL we demanded.", hkl, peak.getHKL()); + TSM_ASSERT_EQUALS("New peak should have QLab we expected.", qLab, peak.getQLabFrame()); + TSM_ASSERT_EQUALS("QSample and QLab should be identical given the identity goniometer settings.", peak.getQLabFrame(), peak.getQSampleFrame()); + auto detector = peak.getDetector(); + TSM_ASSERT("No detector", detector); + TSM_ASSERT_EQUALS("This detector id does not match what we expect from the instrument definition", 1, detector->getID()); + TSM_ASSERT_EQUALS("Thie detector position is wrong", detectorPos, detector->getPos()); + TSM_ASSERT_EQUALS("Goniometer has not been set properly", goniometer.getR(), peak.getGoniometerMatrix()); + + } + + + +}; + + +#endif /* MANTID_CRYSTAL_ADDPEAKHKLTEST_H_ */ diff --git a/Code/Mantid/Framework/Crystal/test/IndexSXPeaksTest.h b/Code/Mantid/Framework/Crystal/test/IndexSXPeaksTest.h index b8c4ca4a2166..a69316a8dc7d 100644 --- a/Code/Mantid/Framework/Crystal/test/IndexSXPeaksTest.h +++ b/Code/Mantid/Framework/Crystal/test/IndexSXPeaksTest.h @@ -120,7 +120,7 @@ class IndexSXPeaksTest : public CxxTest::TestSuite { IPeak& peak = m_masterPeaks->getPeak(i); Mantid::Kernel::V3D v(1, 0, 0); - peak.setQSampleFrame(v); // Overwrite all Q samples to be co-linear. + peak.setQSampleFrame(v, boost::optional()); // Overwrite all Q samples to be co-linear. } TS_ASSERT_THROWS(doTest(6, "1, 2, 3, 4, 5, 6", 14.131, 19.247, 8.606, 90.0, 105.071, 90.0), std::runtime_error); diff --git a/Code/Mantid/Framework/Crystal/test/PeaksOnSurfaceTest.h b/Code/Mantid/Framework/Crystal/test/PeaksOnSurfaceTest.h index 7e2fa007ee98..239a663dfe7e 100644 --- a/Code/Mantid/Framework/Crystal/test/PeaksOnSurfaceTest.h +++ b/Code/Mantid/Framework/Crystal/test/PeaksOnSurfaceTest.h @@ -33,7 +33,7 @@ class PeaksOnSurfaceTest : public CxxTest::TestSuite Mantid::Kernel::V3D position; if(coordFrame == "Q (lab frame)") { - peak.setQLabFrame(peakPosition); + peak.setQLabFrame(peakPosition,1/*set the detector distance explicitly*/); } else { @@ -374,4 +374,4 @@ class PeaksOnSurfaceTestPerformance : public CxxTest::TestSuite } }; -#endif /* MANTID_CRYSTAL_PEAKSONSURFACETEST_H_ */ \ No newline at end of file +#endif /* MANTID_CRYSTAL_PEAKSONSURFACETEST_H_ */ diff --git a/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/Peak.h b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/Peak.h index 877e8adad604..c5c33fbc2a52 100644 --- a/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/Peak.h +++ b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/Peak.h @@ -3,12 +3,15 @@ #include "MantidAPI/IPeak.h" #include "MantidGeometry/Instrument.h" +#include "MantidKernel/Logger.h" #include "MantidKernel/Matrix.h" #include "MantidKernel/V3D.h" #include "MantidKernel/PhysicalConstants.h" #include "MantidKernel/System.h" #include "MantidGeometry/Crystal/PeakShape.h" #include +#include + namespace Mantid { namespace DataObjects { @@ -25,10 +28,10 @@ class DLLExport Peak : public API::IPeak { Peak(); Peak(Geometry::Instrument_const_sptr m_inst, Mantid::Kernel::V3D QLabFrame, - double detectorDistance = 1.0); + boost::optional detectorDistance = boost::optional()); Peak(Geometry::Instrument_const_sptr m_inst, Mantid::Kernel::V3D QSampleFrame, Mantid::Kernel::Matrix goniometer, - double detectorDistance = 1.0); + boost::optional detectorDistance = boost::optional()); Peak(Geometry::Instrument_const_sptr m_inst, int m_DetectorID, double m_Wavelength); Peak(Geometry::Instrument_const_sptr m_inst, int m_DetectorID, @@ -83,9 +86,9 @@ class DLLExport Peak : public API::IPeak { Mantid::Kernel::V3D getDetectorPositionNoCheck() const; void setQSampleFrame(Mantid::Kernel::V3D QSampleFrame, - double detectorDistance = 1.0); + boost::optional detectorDistance = boost::optional()); void setQLabFrame(Mantid::Kernel::V3D QLabFrame, - double detectorDistance = 1.0); + boost::optional detectorDistance = boost::optional()); void setWavelength(double wavelength); double getWavelength() const; @@ -134,7 +137,7 @@ class DLLExport Peak : public API::IPeak { private: - + bool findDetector(const Mantid::Kernel::V3D &beam); /// Shared pointer to the instrument (for calculating some values ) Geometry::Instrument_const_sptr m_inst; @@ -208,6 +211,9 @@ class DLLExport Peak : public API::IPeak { /// Peak shape Mantid::Geometry::PeakShape_const_sptr m_peakShape; + + /// Static logger + static Mantid::Kernel::Logger g_log; }; } // namespace Mantid diff --git a/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/PeaksWorkspace.h b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/PeaksWorkspace.h index 42cc58043f1b..4c4efe464c89 100644 --- a/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/PeaksWorkspace.h +++ b/Code/Mantid/Framework/DataObjects/inc/MantidDataObjects/PeaksWorkspace.h @@ -21,6 +21,7 @@ #include #include #include +#include // IsamplePosition should be IsampleOrientation namespace Mantid { @@ -108,9 +109,13 @@ class DLLExport PeaksWorkspace : public Mantid::API::IPeaksWorkspace { const Peak &getPeak(int peakNum) const; API::IPeak *createPeak(Kernel::V3D QFrame, - double detectorDistance = 1.0) const; + boost::optional detectorDistance = boost::optional()) const; std::vector> peakInfo(Kernel::V3D qFrame, bool labCoords) const; + + + Peak *createPeakHKL(Kernel::V3D HKL) const; + int peakInfoNumber(Kernel::V3D qFrame, bool labCoords) const; std::vector &getPeaks(); diff --git a/Code/Mantid/Framework/DataObjects/src/Peak.cpp b/Code/Mantid/Framework/DataObjects/src/Peak.cpp index 5e6101ff5e9b..71e958d83617 100644 --- a/Code/Mantid/Framework/DataObjects/src/Peak.cpp +++ b/Code/Mantid/Framework/DataObjects/src/Peak.cpp @@ -1,6 +1,7 @@ #include "MantidDataObjects/Peak.h" #include "MantidDataObjects/NoShape.h" #include "MantidGeometry/Instrument/RectangularDetector.h" +#include "MantidGeometry/Instrument/ReferenceFrame.h" #include "MantidGeometry/Objects/InstrumentRayTracer.h" #include "MantidKernel/Strings.h" #include "MantidKernel/System.h" @@ -29,11 +30,11 @@ Peak::Peak() * * @param m_inst :: Shared pointer to the instrument for this peak detection * @param QLabFrame :: Q of the center of the peak, in reciprocal space - * @param detectorDistance :: distance between the sample and the detector. + * @param detectorDistance :: Optional distance between the sample and the detector. Calculated if not explicitly provided. * Used to give a valid TOF. Default 1.0 meters. */ Peak::Peak(Geometry::Instrument_const_sptr m_inst, - Mantid::Kernel::V3D QLabFrame, double detectorDistance) + Mantid::Kernel::V3D QLabFrame, boost::optional detectorDistance) : m_H(0), m_K(0), m_L(0), m_Intensity(0), m_SigmaIntensity(0), m_BinCount(0), m_GoniometerMatrix(3, 3, true), m_InverseGoniometerMatrix(3, 3, true), m_RunNumber(0), m_MonitorCount(0), @@ -51,12 +52,12 @@ Peak::Peak(Geometry::Instrument_const_sptr m_inst, * @param QSampleFrame :: Q of the center of the peak, in reciprocal space, in *the sample frame (goniometer rotation accounted for). * @param goniometer :: a 3x3 rotation matrix - * @param detectorDistance :: distance between the sample and the detector. + * @param detectorDistance :: Optional distance between the sample and the detector. Calculated if not explicitly provided. * Used to give a valid TOF. Default 1.0 meters. */ Peak::Peak(Geometry::Instrument_const_sptr m_inst, Mantid::Kernel::V3D QSampleFrame, - Mantid::Kernel::Matrix goniometer, double detectorDistance) + Mantid::Kernel::Matrix goniometer, boost::optional detectorDistance) : m_H(0), m_K(0), m_L(0), m_Intensity(0), m_SigmaIntensity(0), m_BinCount(0), m_GoniometerMatrix(goniometer), m_InverseGoniometerMatrix(goniometer), m_RunNumber(0), m_MonitorCount(0), @@ -463,10 +464,10 @@ Mantid::Kernel::V3D Peak::getQSampleFrame() const { * This is in inelastic convention: momentum transfer of the LATTICE! * Also, q does NOT have a 2pi factor = it is equal to 1/wavelength. * @param detectorDistance :: distance between the sample and the detector. - * Used to give a valid TOF. Default 1.0 meters. + * Used to give a valid TOF. You do NOT need to explicitly set this. */ void Peak::setQSampleFrame(Mantid::Kernel::V3D QSampleFrame, - double detectorDistance) { + boost::optional detectorDistance) { V3D Qlab = m_GoniometerMatrix * QSampleFrame; this->setQLabFrame(Qlab, detectorDistance); } @@ -483,11 +484,11 @@ void Peak::setQSampleFrame(Mantid::Kernel::V3D QSampleFrame, * This is in inelastic convention: momentum transfer of the LATTICE! * Also, q does have a 2pi factor = it is equal to 2pi/wavelength (in *Angstroms). - * @param detectorDistance :: distance between the sample and the detector. - * Used to give a valid TOF. Default 1.0 meters. + * @param detectorDistance :: distance between the sample and the detector. If this is provided. Then we do not + * ray trace to find the intersecing detector. */ void Peak::setQLabFrame(Mantid::Kernel::V3D QLabFrame, - double detectorDistance) { + boost::optional detectorDistance) { // Clear out the detector = we can't know them m_DetectorID = -1; m_det = IDetector_sptr(); @@ -495,25 +496,32 @@ void Peak::setQLabFrame(Mantid::Kernel::V3D QLabFrame, m_Col = -1; m_BankName = "None"; + // The q-vector direction of the peak is = goniometer * ub * hkl_vector V3D q = QLabFrame; - /* The incident neutron wavevector is in the +Z direction, ki = 1/wl (in z - * direction). + /* The incident neutron wavevector is along the beam direction, ki = 1/wl (usually z, but referenceframe is definitive). * In the inelastic convention, q = ki - kf. - * The final neutron wavector kf = -qx in x; -qy in y; and (-qz+1/wl) in z. + * The final neutron wavector kf = -qx in x; -qy in y; and (-q.beam_dir+1/wl) in beam direction. * AND: norm(kf) = norm(ki) = 2*pi/wavelength - * THEREFORE: 1/wl = norm(q)^2 / (2*qz) + * THEREFORE: 1/wl = norm(q)^2 / (2*q.beam_dir) */ double norm_q = q.norm(); + if(!this->m_inst) + { + throw std::invalid_argument("Setting QLab without an instrument would lead to an inconsistent state for the Peak"); + } + boost::shared_ptr refFrame = this->m_inst->getReferenceFrame(); + const V3D refBeamDir = refFrame->vecPointingAlongBeam(); + const double qBeam = q.scalar_prod(refBeamDir); if (norm_q == 0.0) throw std::invalid_argument("Peak::setQLabFrame(): Q cannot be 0,0,0."); - if (q.Z() == 0.0) + if ( qBeam == 0.0) throw std::invalid_argument( - "Peak::setQLabFrame(): Q cannot be 0 in the Z (beam) direction."); + "Peak::setQLabFrame(): Q cannot be 0 in the beam direction."); - double one_over_wl = (norm_q * norm_q) / (2.0 * q.Z()); + double one_over_wl = (norm_q * norm_q) / (2.0 * qBeam); double wl = (2.0 * M_PI) / one_over_wl; if (wl < 0.0) { std::ostringstream mess; @@ -522,16 +530,30 @@ void Peak::setQLabFrame(Mantid::Kernel::V3D QLabFrame, throw std::invalid_argument(mess.str()); } - // This is the scattered direction, kf = (-qx, -qy, 1/wl-qz) - V3D beam = q * -1.0; - beam.setZ(one_over_wl - q.Z()); - beam.normalize(); // Save the wavelength this->setWavelength(wl); + V3D detectorDir = q * -1.0; + detectorDir[refFrame->pointingAlongBeam()] = one_over_wl - qBeam; + detectorDir.normalize(); + // Use the given detector distance to find the detector position. - detPos = samplePos + beam * detectorDistance; + if(detectorDistance.is_initialized()) + { + detPos = samplePos + detectorDir * detectorDistance.get(); + // We do not-update the detector as by manually setting the distance the client seems to know better. + } + else + { + // Find the detector + const bool found = findDetector(detectorDir); + if (!found) + { + // This is important, so we ought to log when this fails to happen. + g_log.debug("Could not find detector after setting qLab via setQLab with QLab : " + q.toString()); + } + } } /** After creating a peak using the Q in the lab frame, @@ -544,12 +566,22 @@ void Peak::setQLabFrame(Mantid::Kernel::V3D QLabFrame, * @return true if the detector ID was found. */ bool Peak::findDetector() { - bool found = false; + // Scattered beam direction V3D oldDetPos = detPos; V3D beam = detPos - samplePos; beam.normalize(); + return findDetector(beam); +} + +/** + * @brief Peak::findDetector : Find the detector along the beam location. sets the detector, and detector position if found + * @param beam : detector direction from the sample as V3D + * @return True if a detector has been found + */ +bool Peak::findDetector(const Mantid::Kernel::V3D &beam) { + bool found = false; // Create a ray tracer InstrumentRayTracer tracker(m_inst); tracker.traceFromSample(beam); @@ -896,5 +928,7 @@ Mantid::Kernel::V3D Peak::getDetectorPosition() const { return getDetector()->getPos(); } +Mantid::Kernel::Logger Peak::g_log("PeakLogger"); + } // namespace Mantid } // namespace DataObjects diff --git a/Code/Mantid/Framework/DataObjects/src/PeaksWorkspace.cpp b/Code/Mantid/Framework/DataObjects/src/PeaksWorkspace.cpp index 5258f57751e4..83aabf18e3fd 100644 --- a/Code/Mantid/Framework/DataObjects/src/PeaksWorkspace.cpp +++ b/Code/Mantid/Framework/DataObjects/src/PeaksWorkspace.cpp @@ -199,11 +199,11 @@ const Peak &PeaksWorkspace::getPeak(const int peakNum) const { //--------------------------------------------------------------------------------------------- /** Creates an instance of a Peak BUT DOES NOT ADD IT TO THE WORKSPACE * @param QLabFrame :: Q of the center of the peak, in reciprocal space - * @param detectorDistance :: distance between the sample and the detector. + * @param detectorDistance :: optional distance between the sample and the detector. You do NOT need to explicitly provide this distance. * @return a pointer to a new Peak object. */ API::IPeak *PeaksWorkspace::createPeak(Kernel::V3D QLabFrame, - double detectorDistance) const { + boost::optional detectorDistance) const { return new Peak(this->getInstrument(), QLabFrame, detectorDistance); } @@ -299,6 +299,7 @@ PeaksWorkspace::peakInfo(Kernel::V3D qFrame, bool labCoords) const { } try { + API::IPeak *peak = createPeak(Qlab); if (sample().hasOrientedLattice()) { @@ -387,6 +388,40 @@ PeaksWorkspace::peakInfo(Kernel::V3D qFrame, bool labCoords) const { return Result; } +/** + * Create a Peak from a HKL value provided by the client. + * + * + * @param HKL : reciprocal lattice vector coefficients + * @return Fully formed peak. + */ +Peak *PeaksWorkspace::createPeakHKL(V3D HKL) const +{ + /* + The following allows us to add peaks where we have a single UB to work from. + */ + + Geometry::OrientedLattice lattice = this->sample().getOrientedLattice(); + Geometry::Goniometer goniometer = this->run().getGoniometer(); + + // Calculate qLab from q HKL. As per Busing and Levy 1967, q_lab_frame = 2pi * Goniometer * UB * HKL + V3D qLabFrame = goniometer.getR() * lattice.getUB() * HKL * 2 * M_PI; + + // create a peak using the qLab frame + auto peak = new Peak(this->getInstrument(), qLabFrame); // This should calculate the detector positions too. + + // We need to set HKL separately to keep things consistent. + peak->setHKL(HKL[0], HKL[1], HKL[2]); + + // Set the goniometer + peak->setGoniometerMatrix(goniometer.getR()); + + // Take the run number from this + peak->setRunNumber(this->getRunNumber()); + + return peak; +} + /** * Returns selected information for a "peak" at QLabFrame. * diff --git a/Code/Mantid/Framework/DataObjects/test/PeakTest.h b/Code/Mantid/Framework/DataObjects/test/PeakTest.h index e7bd1b9929f4..317d54c3e840 100644 --- a/Code/Mantid/Framework/DataObjects/test/PeakTest.h +++ b/Code/Mantid/Framework/DataObjects/test/PeakTest.h @@ -5,6 +5,11 @@ #include "MockObjects.h" #include "MantidKernel/Timer.h" #include "MantidKernel/System.h" +#include "MantidKernel/UnitFactory.h" +#include "MantidKernel/Unit.h" +#include "MantidKernel/V3D.h" +#include "MantidKernel/PhysicalConstants.h" +#include "MantidGeometry/Instrument/ReferenceFrame.h" #include #include #include @@ -18,12 +23,25 @@ using namespace Mantid::Kernel; class PeakTest : public CxxTest::TestSuite { +private: + /// Common instrument + Instrument_sptr inst; + Instrument_sptr m_minimalInstrument; public: - /// Common instrument - Instrument_sptr inst; - void setUp() + + + // This pair of boilerplate methods prevent the suite being created statically + // This means the constructor isn't called when running other tests + static PeakTest *createSuite() { + return new PeakTest(); + } + static void destroySuite(PeakTest *suite) { delete suite; } + + + // Constructor + PeakTest() : inst(ComponentCreationHelper::createTestInstrumentRectangular(5, 100)) { - inst = ComponentCreationHelper::createTestInstrumentRectangular(5, 100); + } void test_constructor() @@ -253,14 +271,16 @@ class PeakTest : public CxxTest::TestSuite void test_setQLabFrame_ThrowsIfQIsNull() { Peak p1(inst, 10000, 2.0); - TS_ASSERT_THROWS_ANYTHING(Peak p2(inst, V3D(0,0,0), 1.0)); - TS_ASSERT_THROWS_ANYTHING(Peak p2(inst, V3D(1,2,0), 1.0)); + const boost::optional distance = 1.0; + TS_ASSERT_THROWS_ANYTHING(Peak p2(inst, V3D(0,0,0), distance)); + TS_ASSERT_THROWS_ANYTHING(Peak p2(inst, V3D(1,2,0), distance)); } /** Compare two peaks, but not the detector IDs etc. */ void comparePeaks(Peak & p1, Peak & p2) { + // TODO. Peak should implement bool operator==(const Peak&) and that should be tested, rather than having external functionality here. TS_ASSERT_EQUALS( p1.getQLabFrame(), p2.getQLabFrame() ); TS_ASSERT_EQUALS( p1.getQSampleFrame(), p2.getQSampleFrame() ); TS_ASSERT_EQUALS( p1.getDetPos(), p2.getDetPos() ); @@ -282,7 +302,7 @@ class PeakTest : public CxxTest::TestSuite V3D detPos1 = p1.getDetPos(); // Construct using just Q - Peak p2(inst, Qlab1, detPos1.norm()); + Peak p2(inst, Qlab1, boost::optional(detPos1.norm())); comparePeaks(p1, p2); TS_ASSERT_EQUALS( p2.getBankName(), "None"); TS_ASSERT_EQUALS( p2.getRow(), -1); @@ -290,6 +310,40 @@ class PeakTest : public CxxTest::TestSuite TS_ASSERT_EQUALS( p2.getDetectorID(), -1); } + void test_setQLabFrame2() + { + // Create fictional instrument + const V3D source(0,0,0); + const V3D sample(15, 0, 0); + const V3D detectorPos(20, 5, 0); + const V3D beam1 = sample - source; + const V3D beam2 = detectorPos - sample; + auto minimalInstrument = ComponentCreationHelper::createMinimalInstrument( source, sample, detectorPos ); + + // Derive distances and angles + const double l1 = beam1.norm(); + const double l2 = beam2.norm(); + const V3D qLabDir = (beam1/l1) - (beam2/l2); + + const double microSecsInSec = 1e6; + + // Derive QLab for diffraction + const double wavenumber_in_angstrom_times_tof_in_microsec = + (Mantid::PhysicalConstants::NeutronMass * (l1 + l2) * 1e-10 * microSecsInSec) / + Mantid::PhysicalConstants::h_bar; + + V3D qLab = qLabDir * wavenumber_in_angstrom_times_tof_in_microsec; + + Peak peak; // Everything will be default + peak.setInstrument(minimalInstrument); // Can't do anything without the instrument + peak.setQLabFrame(qLab); + auto detector = peak.getDetector(); + + TSM_ASSERT("No detector", detector); + TS_ASSERT_EQUALS(1, detector->getID()); + TS_ASSERT_EQUALS(detectorPos, detector->getPos()); + } + /** Create peaks using Q in sample frame + a goniometer rotation matrix*/ void test_setQSampleFrame() { @@ -323,7 +377,7 @@ class PeakTest : public CxxTest::TestSuite V3D detPos1 = p1.getDetPos(); // Construct using just Q - Peak p2(inst, Qlab1, detPos1.norm()); + Peak p2(inst, Qlab1, boost::optional(detPos1.norm())); TS_ASSERT( p2.findDetector() ); comparePeaks(p1, p2); TS_ASSERT_EQUALS( p2.getBankName(), "bank1"); @@ -350,7 +404,7 @@ class PeakTest : public CxxTest::TestSuite const double wavelength = 2; Peak p(inst, detectorId, wavelength); TSM_ASSERT_THROWS_NOTHING("Nothing wrong here, detector is valid", p.getDetectorPosition()); - p.setQLabFrame(V3D(1,1,1), 1); // This sets the detector pointer to null and detector id to -1; + p.setQLabFrame(V3D(1,1,1), 1.0); // This sets the detector pointer to null and detector id to -1; TSM_ASSERT_THROWS("Detector is not valid", p.getDetectorPosition(), Mantid::Kernel::Exception::NullPointerException&); } diff --git a/Code/Mantid/Framework/DataObjects/test/PeaksWorkspaceTest.h b/Code/Mantid/Framework/DataObjects/test/PeaksWorkspaceTest.h index 20d627c7d99e..45f37ed189d0 100644 --- a/Code/Mantid/Framework/DataObjects/test/PeaksWorkspaceTest.h +++ b/Code/Mantid/Framework/DataObjects/test/PeaksWorkspaceTest.h @@ -14,6 +14,8 @@ #include "MantidKernel/V3D.h" #include "MantidKernel/Strings.h" #include "MantidKernel/PhysicalConstants.h" +#include "MantidGeometry/Crystal/OrientedLattice.h" +#include "MantidGeometry/Instrument/Goniometer.h" #include "MantidTestHelpers/NexusTestHelper.h" #include "MantidTestHelpers/ComponentCreationHelper.h" #include "MantidAPI/AlgorithmManager.h" @@ -360,6 +362,58 @@ class PeaksWorkspaceTest : public CxxTest::TestSuite TS_ASSERT_EQUALS(coordSystem, pw->getSpecialCoordinateSystem()); } + void test_createPeakHKL() + { + // Create simple fictional instrument + const V3D source(0,0,0); + const V3D sample(15, 0, 0); + const V3D detectorPos(20, 5, 0); + const V3D beam1 = sample - source; + const V3D beam2 = detectorPos - sample; + auto minimalInstrument = ComponentCreationHelper::createMinimalInstrument( source, sample, detectorPos ); + + // Derive distances and angles + const double l1 = beam1.norm(); + const double l2 = beam2.norm(); + const V3D qLabDir = (beam1/l1) - (beam2/l2); + + const double microSecsInSec = 1e6; + + // Derive QLab for diffraction + const double wavenumber_in_angstrom_times_tof_in_microsec = + (Mantid::PhysicalConstants::NeutronMass * (l1 + l2) * 1e-10 * microSecsInSec) / + Mantid::PhysicalConstants::h_bar; + V3D qLab = qLabDir * wavenumber_in_angstrom_times_tof_in_microsec; + + Mantid::Geometry::OrientedLattice orientedLattice(1, 1, 1, 90, 90, 90); // U is identity, real and reciprocal lattice vectors are identical. + Mantid::Geometry::Goniometer goniometer; // identity + V3D hkl = qLab / (2 * M_PI); // Given our settings above, this is the simplified relationship between qLab and hkl. + + // Now create a peaks workspace around the simple fictional instrument + PeaksWorkspace ws; + ws.setInstrument(minimalInstrument); + ws.mutableSample().setOrientedLattice(&orientedLattice); + ws.mutableRun().setGoniometer(goniometer, false); + + // Create the peak + Peak* peak = ws.createPeakHKL(hkl); + + /* + Now we check we have made a self - consistent peak + */ + TSM_ASSERT_EQUALS("New peak should have HKL we demanded.", hkl, peak->getHKL()); + TSM_ASSERT_EQUALS("New peak should have QLab we expected.", qLab, peak->getQLabFrame()); + TSM_ASSERT_EQUALS("QSample and QLab should be identical given the identity goniometer settings.", peak->getQLabFrame(), peak->getQSampleFrame()); + auto detector = peak->getDetector(); + TSM_ASSERT("No detector", detector); + TSM_ASSERT_EQUALS("This detector id does not match what we expect from the instrument definition", 1, detector->getID()); + TSM_ASSERT_EQUALS("Thie detector position is wrong", detectorPos, detector->getPos()); + TSM_ASSERT_EQUALS("Goniometer has not been set properly", goniometer.getR(), peak->getGoniometerMatrix()); + + // Clean up. + delete peak; + } + private: PeaksWorkspace_sptr createSaveTestPeaksWorkspace() diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/ReferenceFrame.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/ReferenceFrame.h index fb4c388bfe30..338e060f4ce3 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/ReferenceFrame.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Instrument/ReferenceFrame.h @@ -8,7 +8,7 @@ namespace Mantid { namespace Geometry { /// Type to describe pointing along options -enum PointingAlong { X, Y, Z }; +enum PointingAlong { X=0, Y=1, Z=2 }; /// Type to distingusih between l and r handedness enum Handedness { Left, Right }; diff --git a/Code/Mantid/Framework/MDAlgorithms/test/CentroidPeaksMD2Test.h b/Code/Mantid/Framework/MDAlgorithms/test/CentroidPeaksMD2Test.h index 31584d4caf27..3adbed5c426d 100644 --- a/Code/Mantid/Framework/MDAlgorithms/test/CentroidPeaksMD2Test.h +++ b/Code/Mantid/Framework/MDAlgorithms/test/CentroidPeaksMD2Test.h @@ -98,9 +98,9 @@ class CentroidPeaksMD2Test : public CxxTest::TestSuite Peak pIn(inst, 1, 1.0, startPos ); if (CoordinatesToUse == "Q (lab frame)") - pIn.setQLabFrame(startPos); + pIn.setQLabFrame(startPos, 1 /*sample to detector distance*/); else if (CoordinatesToUse == "Q (sample frame)") - pIn.setQSampleFrame(startPos); + pIn.setQSampleFrame(startPos, 1 /*sample to detector distance*/); else if (CoordinatesToUse == "HKL") pIn.setHKL(startPos); peakWS->addPeak( pIn ); diff --git a/Code/Mantid/Framework/MDAlgorithms/test/CentroidPeaksMDTest.h b/Code/Mantid/Framework/MDAlgorithms/test/CentroidPeaksMDTest.h index 4e7338b07bdd..445df41cf2d4 100644 --- a/Code/Mantid/Framework/MDAlgorithms/test/CentroidPeaksMDTest.h +++ b/Code/Mantid/Framework/MDAlgorithms/test/CentroidPeaksMDTest.h @@ -98,9 +98,9 @@ class CentroidPeaksMDTest : public CxxTest::TestSuite Peak pIn(inst, 1, 1.0, startPos ); if (CoordinatesToUse == "Q (lab frame)") - pIn.setQLabFrame(startPos); + pIn.setQLabFrame(startPos, 1 /*sample to detector distance*/); else if (CoordinatesToUse == "Q (sample frame)") - pIn.setQSampleFrame(startPos); + pIn.setQSampleFrame(startPos, 1 /*sample to detector distance*/); else if (CoordinatesToUse == "HKL") pIn.setHKL(startPos); peakWS->addPeak( pIn ); diff --git a/Code/Mantid/Framework/MDEvents/src/MDWSTransform.cpp b/Code/Mantid/Framework/MDEvents/src/MDWSTransform.cpp index 15dbab13aef5..d4c24c31db6a 100644 --- a/Code/Mantid/Framework/MDEvents/src/MDWSTransform.cpp +++ b/Code/Mantid/Framework/MDEvents/src/MDWSTransform.cpp @@ -9,9 +9,9 @@ namespace { // logger for the algorithm workspaces Kernel::Logger g_Log("MDWSTransform"); } - using namespace CnvrtToMD; + /** method to build the Q-coordinates transformation. * * @param TargWSDescription -- the class which describes target MD workspace. In diff --git a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IPeak.cpp b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IPeak.cpp index c5d3eec3e73f..e12912bc5a0a 100644 --- a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IPeak.cpp +++ b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IPeak.cpp @@ -6,10 +6,29 @@ using Mantid::API::IPeak; using namespace boost::python; namespace { -Mantid::Geometry::PeakShape_sptr getPeakShape(IPeak& peak) { - // Use clone to make a copy of the PeakShape. - return Mantid::Geometry::PeakShape_sptr(peak.getPeakShape().clone()); +Mantid::Geometry::PeakShape_sptr getPeakShape(IPeak &peak) { + // Use clone to make a copy of the PeakShape. + return Mantid::Geometry::PeakShape_sptr(peak.getPeakShape().clone()); } +void setQLabFrame1(IPeak &peak, Mantid::Kernel::V3D qLabFrame) { + // Set the q lab frame. No explicit detector distance. + return peak.setQLabFrame(qLabFrame, boost::optional()); +} +void setQLabFrame2(IPeak &peak, Mantid::Kernel::V3D qLabFrame, double distance) { + // Set the q lab frame. Detector distance specified. + return peak.setQLabFrame(qLabFrame, distance); +} + +void setQSampleFrame1(IPeak &peak, Mantid::Kernel::V3D qSampleFrame) { + // Set the qsample frame. No explicit detector distance. + return peak.setQSampleFrame(qSampleFrame, boost::optional()); +} + +void setQSampleFrame2(IPeak &peak, Mantid::Kernel::V3D qSampleFrame, double distance) { + // Set the qsample frame. Detector distance specified. + return peak.setQSampleFrame(qSampleFrame, distance); +} + } void export_IPeak() @@ -38,8 +57,10 @@ void export_IPeak() "Using the instrument set in the peak, perform ray tracing to find the exact detector.") .def("getQSampleFrame", &IPeak::getQSampleFrame, "Return the Q change (of the lattice, k_i - k_f) for this peak." "The Q is in the Sample frame: the goniometer rotation WAS taken out. ") - .def("setQLabFrame", &IPeak::setQLabFrame, "Set the peak using the peak's position in reciprocal space, in the lab frame.") - .def("setQSampleFrame", &IPeak::setQSampleFrame, "Set the peak using the peak's position in reciprocal space, in the sample frame.") + .def("setQLabFrame", setQLabFrame1, "Set the peak using the peak's position in reciprocal space, in the lab frame.") + .def("setQLabFrame", setQLabFrame2, "Set the peak using the peak's position in reciprocal space, in the lab frame. Detector distance explicitly supplied.") // two argument overload + .def("setQSampleFrame", setQSampleFrame1, "Set the peak using the peak's position in reciprocal space, in the sample frame.") + .def("setQSampleFrame", setQSampleFrame2, "Set the peak using the peak's position in reciprocal space, in the sample frame. Detector distance explicitly supplied.") .def("setWavelength", &IPeak::setWavelength, "Set the incident wavelength of the neutron. Calculates the energy from this assuming elastic scattering.") .def("getWavelength", &IPeak::getWavelength, "Return the incident wavelength") .def("getScattering", &IPeak::getScattering, "Calculate the scattering angle of the peak") diff --git a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IPeaksWorkspace.cpp b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IPeaksWorkspace.cpp index 80f30016b472..965bf50050c3 100644 --- a/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IPeaksWorkspace.cpp +++ b/Code/Mantid/Framework/PythonInterface/mantid/api/src/Exports/IPeaksWorkspace.cpp @@ -1,13 +1,38 @@ #include "MantidAPI/IPeaksWorkspace.h" #include "MantidAPI/IPeak.h" #include "MantidPythonInterface/kernel/Registry/DataItemInterface.h" +#include "MantidPythonInterface/kernel/Converters/PyObjectToV3D.h" #include #include +#include +#include using namespace Mantid::API; using Mantid::PythonInterface::Registry::DataItemInterface; using namespace boost::python; +namespace { + +/// Create a peak via it's HKL value from a list or numpy array +IPeak* createPeakHKL(IPeaksWorkspace & self, const object& data) +{ + return self.createPeakHKL(Mantid::PythonInterface::Converters::PyObjectToV3D(data)()); +} + +/// Create a peak via it's QLab value from a list or numpy array +IPeak* createPeakQLab(IPeaksWorkspace & self, const object& data) +{ + return self.createPeak(Mantid::PythonInterface::Converters::PyObjectToV3D(data)(), boost::optional()); +} + +/// Create a peak via it's QLab value from a list or numpy array +IPeak* createPeakQLabWithDistance(IPeaksWorkspace & self, const object& data, double detectorDistance) +{ + return self.createPeak(Mantid::PythonInterface::Converters::PyObjectToV3D(data)(), detectorDistance); +} + +} + void export_IPeaksWorkspace() { // IPeaksWorkspace class @@ -16,7 +41,9 @@ void export_IPeaksWorkspace() .def("addPeak", &IPeaksWorkspace::addPeak, "Add a peak to the workspace") .def("removePeak", &IPeaksWorkspace::removePeak, "Remove a peak from the workspace") .def("getPeak", &IPeaksWorkspace::getPeakPtr, return_internal_reference<>(), "Returns a peak at the given index" ) - .def("createPeak", &IPeaksWorkspace::createPeak, return_internal_reference<>(), "Create a Peak and return it") + .def("createPeak", createPeakQLab, return_value_policy(), "Create a Peak and return it from its coordinates in the QLab frame") + .def("createPeak", createPeakQLabWithDistance, return_value_policy(), "Create a Peak and return it from its coordinates in the QLab frame, detector-sample distance explicitly provided") + .def("createPeakHKL", createPeakHKL, return_value_policy(), "Create a Peak and return it from its coordinates in the HKL frame") .def("hasIntegratedPeaks", &IPeaksWorkspace::hasIntegratedPeaks, "Determine if the peaks have been integrated") .def("getRun", &IPeaksWorkspace::mutableRun, return_internal_reference<>(), "Return the Run object for this workspace") diff --git a/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/IPeaksWorkspaceTest.py b/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/IPeaksWorkspaceTest.py index 9726f2b928cd..be86773d8241 100644 --- a/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/IPeaksWorkspaceTest.py +++ b/Code/Mantid/Framework/PythonInterface/test/python/mantid/api/IPeaksWorkspaceTest.py @@ -1,7 +1,8 @@ import unittest from testhelpers import run_algorithm, WorkspaceCreationHelper from mantid.kernel import V3D -from mantid.api import IPeaksWorkspace +from mantid.geometry import OrientedLattice +from mantid.api import IPeaksWorkspace, IPeak class IPeaksWorkspaceTest(unittest.TestCase): """ @@ -38,6 +39,7 @@ def test_interface(self): # Create a new peak at some Q in the lab frame qlab = V3D(1,2,3) p = pws.createPeak(qlab, 1.54) + p.getQLabFrame() self.assertAlmostEquals( p.getQLabFrame().X(), 1.0, 3) # Now try to add the peak back @@ -50,6 +52,43 @@ def test_interface(self): # Peaks workspace will not be integrated by default. self.assertTrue(not pws.hasIntegratedPeaks()) + + def test_createPeakHKL(self): + pws = WorkspaceCreationHelper.createPeaksWorkspace(0, True) + lattice = pws.mutableSample().getOrientedLattice() + + # Simple test that the creational method is exposed + p = pws.createPeakHKL([1,1,1]) + self.assertTrue(IPeak != None) + + def test_peak_setQLabFrame(self): + pws = WorkspaceCreationHelper.createPeaksWorkspace(1, True) + p = pws.getPeak(0) + try: + p.setQLabFrame(V3D(1,1,1)) + except Exception: + self.fail("Tried setQLabFrame with one V3D argument") + + try: + p.setQLabFrame(V3D(1,1,1), 1) + except Exception: + self.fail("Tried setQLabFrame with one V3D argument and a double distance") + + def test_peak_setQSampleFrame(self): + pws = WorkspaceCreationHelper.createPeaksWorkspace(1, True) + p = pws.getPeak(0) + try: + p.setQSampleFrame(V3D(1,1,1)) + except Exception: + self.fail("Tried setQSampleFrame with one V3D argument") + + try: + p.setQSampleFrame(V3D(1,1,1), 1) + except Exception: + self.fail("Tried setQSampleFrame with one V3D argument and a double distance") + + + if __name__ == '__main__': unittest.main() diff --git a/Code/Mantid/Framework/PythonInterface/test/testhelpers/WorkspaceCreationHelperModule.cpp b/Code/Mantid/Framework/PythonInterface/test/testhelpers/WorkspaceCreationHelperModule.cpp index 28e4e94c7e5f..032de062643f 100644 --- a/Code/Mantid/Framework/PythonInterface/test/testhelpers/WorkspaceCreationHelperModule.cpp +++ b/Code/Mantid/Framework/PythonInterface/test/testhelpers/WorkspaceCreationHelperModule.cpp @@ -69,10 +69,12 @@ BOOST_PYTHON_MODULE(WorkspaceCreationHelper) //=================================== Peak Workspaces =================================== - // Forces the returns the be IEventWorkspace_sptr + // Forces the returns the be IPeaks_sptr typedef IPeaksWorkspace_sptr (*Signature1_Peaks)(const int); + typedef IPeaksWorkspace_sptr (*Signature2_Peaks)(const int, const bool); def("createPeaksWorkspace", (Signature1_Peaks)&createPeaksWorkspace); + def("createPeaksWorkspace", (Signature2_Peaks)&createPeaksWorkspace); //=================================== MD Workspaces =================================== diff --git a/Code/Mantid/Framework/TestHelpers/inc/MantidTestHelpers/ComponentCreationHelper.h b/Code/Mantid/Framework/TestHelpers/inc/MantidTestHelpers/ComponentCreationHelper.h index 030e53a84b3a..073fe49bd53c 100644 --- a/Code/Mantid/Framework/TestHelpers/inc/MantidTestHelpers/ComponentCreationHelper.h +++ b/Code/Mantid/Framework/TestHelpers/inc/MantidTestHelpers/ComponentCreationHelper.h @@ -150,6 +150,9 @@ createTestInstrumentRectangular(int num_banks, int pixels, Mantid::Geometry::Instrument_sptr createTestInstrumentRectangular2(int num_banks, int pixels, double pixelSpacing = 0.008); + +/// Creates a mimimal valid virtual instrument. +Mantid::Geometry::Instrument_sptr createMinimalInstrument(const Mantid::Kernel::V3D& sourcePos, const Mantid::Kernel::V3D& samplePos, const Mantid::Kernel::V3D& detectorPos ); } #endif // COMPONENTCREATIONHELPERS_H_ diff --git a/Code/Mantid/Framework/TestHelpers/inc/MantidTestHelpers/WorkspaceCreationHelper.h b/Code/Mantid/Framework/TestHelpers/inc/MantidTestHelpers/WorkspaceCreationHelper.h index ab03c4524252..82afcca56f62 100644 --- a/Code/Mantid/Framework/TestHelpers/inc/MantidTestHelpers/WorkspaceCreationHelper.h +++ b/Code/Mantid/Framework/TestHelpers/inc/MantidTestHelpers/WorkspaceCreationHelper.h @@ -294,7 +294,7 @@ Mantid::DataObjects::RebinnedOutput_sptr CreateRebinnedOutputWorkspace(); /// Create a simple peaks workspace containing the given number of peaks boost::shared_ptr -createPeaksWorkspace(const int numPeaks = 2); +createPeaksWorkspace(const int numPeaks = 2, const bool createOrientedLattice=false); /**Build table workspace with preprocessed detectors for existign worksapce with * instrument */ boost::shared_ptr diff --git a/Code/Mantid/Framework/TestHelpers/src/ComponentCreationHelper.cpp b/Code/Mantid/Framework/TestHelpers/src/ComponentCreationHelper.cpp index 110cb9803fb1..0618e788d5aa 100644 --- a/Code/Mantid/Framework/TestHelpers/src/ComponentCreationHelper.cpp +++ b/Code/Mantid/Framework/TestHelpers/src/ComponentCreationHelper.cpp @@ -20,9 +20,11 @@ #include "MantidGeometry/Instrument/DetectorGroup.h" #include "MantidGeometry/Instrument/Detector.h" #include "MantidGeometry/Instrument/RectangularDetector.h" +#include "MantidGeometry/Instrument/ReferenceFrame.h" #include #include +#include #include "MantidGeometry/IDetector.h" using namespace Mantid::Geometry; @@ -559,4 +561,45 @@ Instrument_sptr createTestInstrumentRectangular2(int num_banks, int pixels, return testInst; } + +/** + * createOneDetectorInstrument, creates the most simple possible definition of an instrument in which we can extract a valid L1 and L2 distance for unit calculations. + * + * Beam direction is along X, + * Up direction is Y + * + * @param sourcePos : V3D position + * @param samplePos : V3D sample position + * @param detectorPos : V3D detector position + * @return Instrument generated. + */ +Instrument_sptr createMinimalInstrument(const Mantid::Kernel::V3D& sourcePos, const Mantid::Kernel::V3D& samplePos, const Mantid::Kernel::V3D& detectorPos ) +{ + Instrument_sptr instrument = boost::make_shared(); + instrument->setReferenceFrame( + boost::make_shared(Mantid::Geometry::Y /*up*/, Mantid::Geometry::X /*along*/, Left, "0,0,0")); + + // A source + ObjComponent *source = new ObjComponent("source"); + source->setPos(sourcePos); + source->setShape(createSphere(0.01 /*1cm*/, V3D(0,0,0), "1")); + instrument->add(source); + instrument->markAsSource(source); + + // A sample + ObjComponent *sample = new ObjComponent("some-surface-holder"); + sample->setPos(samplePos); + sample->setShape(createSphere(0.01 /*1cm*/, V3D(0,0,0), "1")); + instrument->add(sample); + instrument->markAsSamplePos(sample); + + // A detector + Detector *det = new Detector("point-detector", 1 /*detector id*/, NULL); + det->setPos(detectorPos); + det->setShape(createSphere(0.01 /*1cm*/, V3D(0,0,0), "1")); + instrument->add(det); + instrument->markAsDetector(det); + + return instrument; +} } diff --git a/Code/Mantid/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp b/Code/Mantid/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp index 9e2b671e577f..11df5295a6c6 100644 --- a/Code/Mantid/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp +++ b/Code/Mantid/Framework/TestHelpers/src/WorkspaceCreationHelper.cpp @@ -318,7 +318,7 @@ create2DWorkspaceWithFullInstrument(int nhist, int nbins, bool includeMonitors, boost::shared_ptr testInst(new Instrument(instrumentName)); testInst->setReferenceFrame( - boost::shared_ptr(new ReferenceFrame(Y, X, Left, ""))); + boost::shared_ptr(new ReferenceFrame(Y, Z, Left, ""))); space->setInstrument(testInst); const double pixelRadius(0.05); @@ -366,14 +366,14 @@ create2DWorkspaceWithFullInstrument(int nhist, int nbins, bool includeMonitors, // Define a source and sample position // Define a source component ObjComponent *source = - new ObjComponent("moderator", Object_sptr(), testInst.get()); + new ObjComponent("moderator", ComponentCreationHelper::createSphere(0.1, V3D(0,0,0), "1"), testInst.get()); source->setPos(V3D(-20, 0.0, 0.0)); testInst->add(source); testInst->markAsSource(source); // Define a sample as a simple sphere ObjComponent *sample = - new ObjComponent("samplePos", Object_sptr(), testInst.get()); + new ObjComponent("samplePos", ComponentCreationHelper::createSphere(0.1, V3D(0,0,0), "1"), testInst.get()); testInst->setPos(0.0, 0.0, 0.0); testInst->add(sample); testInst->markAsSamplePos(sample); @@ -1182,7 +1182,7 @@ RebinnedOutput_sptr CreateRebinnedOutputWorkspace() { } Mantid::DataObjects::PeaksWorkspace_sptr -createPeaksWorkspace(const int numPeaks) { +createPeaksWorkspace(const int numPeaks, const bool createOrientedLattice) { PeaksWorkspace_sptr peaksWS(new PeaksWorkspace()); Instrument_sptr inst = ComponentCreationHelper::createTestInstrumentRectangular2(1, 10); @@ -1193,6 +1193,10 @@ createPeaksWorkspace(const int numPeaks) { peaksWS->addPeak(peak); } + if(createOrientedLattice) { + Mantid::Geometry::OrientedLattice lattice; + peaksWS->mutableSample().setOrientedLattice(&lattice); + } return peaksWS; } diff --git a/Code/Mantid/MantidQt/SliceViewer/src/ConcretePeaksPresenter.cpp b/Code/Mantid/MantidQt/SliceViewer/src/ConcretePeaksPresenter.cpp index aa5929add3b7..0eebd21b630b 100644 --- a/Code/Mantid/MantidQt/SliceViewer/src/ConcretePeaksPresenter.cpp +++ b/Code/Mantid/MantidQt/SliceViewer/src/ConcretePeaksPresenter.cpp @@ -451,7 +451,7 @@ void ConcretePeaksPresenter::setPeakSizeIntoProjection(const double fraction) { double ConcretePeaksPresenter::getPeakSizeOnProjection() const { double result = 0; - if (m_viewPeaks != NULL && m_viewPeaks->positionOnly()) { + if (m_viewPeaks != NULL && (m_peaksWS->getNumberPeaks() > 0) && m_viewPeaks->positionOnly()) { result = m_viewPeaks->getOccupancyInView(); } return result; @@ -459,7 +459,7 @@ double ConcretePeaksPresenter::getPeakSizeOnProjection() const { double ConcretePeaksPresenter::getPeakSizeIntoProjection() const { double result = 0; - if (m_viewPeaks != NULL && m_viewPeaks->positionOnly()) { + if (m_viewPeaks != NULL && (m_peaksWS->getNumberPeaks() > 0) && m_viewPeaks->positionOnly()) { result = m_viewPeaks->getOccupancyIntoView(); } return result; diff --git a/Code/Mantid/MantidQt/SliceViewer/test/MockObjects.h b/Code/Mantid/MantidQt/SliceViewer/test/MockObjects.h index da6b3e1a4c57..42cb6aa2ddcf 100644 --- a/Code/Mantid/MantidQt/SliceViewer/test/MockObjects.h +++ b/Code/Mantid/MantidQt/SliceViewer/test/MockObjects.h @@ -200,9 +200,9 @@ class MockPeakTransformFactory : public PeakTransformFactory MOCK_METHOD0(findDetector, bool()); MOCK_METHOD2(setQSampleFrame, - void(Mantid::Kernel::V3D QSampleFrame, double detectorDistance)); + void(Mantid::Kernel::V3D QSampleFrame, boost::optional detectorDistance)); MOCK_METHOD2(setQLabFrame, - void(Mantid::Kernel::V3D QLabFrame, double detectorDistance)); + void(Mantid::Kernel::V3D QLabFrame, boost::optional detectorDistance)); MOCK_METHOD1(setWavelength, void(double wavelength)); MOCK_CONST_METHOD0(getWavelength, diff --git a/Code/Mantid/Vates/VatesAPI/test/vtkPeakMarkerFactoryTest.h b/Code/Mantid/Vates/VatesAPI/test/vtkPeakMarkerFactoryTest.h index 025bf2135fd1..5e4fb59411a2 100644 --- a/Code/Mantid/Vates/VatesAPI/test/vtkPeakMarkerFactoryTest.h +++ b/Code/Mantid/Vates/VatesAPI/test/vtkPeakMarkerFactoryTest.h @@ -36,7 +36,7 @@ class MockPeaksWorkspace : public PeaksWorkspace MOCK_METHOD1(addPeak, void (const IPeak& ipeak)); MOCK_METHOD1(getPeak, Mantid::DataObjects::Peak & (int peakNum)); MOCK_CONST_METHOD1(getPeak, const Mantid::DataObjects::Peak & (int peakNum)); - MOCK_CONST_METHOD2(createPeak, Mantid::API::IPeak* (Mantid::Kernel::V3D QLabFrame, double detectorDistance)); + MOCK_CONST_METHOD2(createPeak, Mantid::API::IPeak* (Mantid::Kernel::V3D QLabFrame, boost::optional detectorDistance)); }; //===================================================================================== diff --git a/Code/Mantid/docs/source/algorithms/AddPeakHKL-v1.rst b/Code/Mantid/docs/source/algorithms/AddPeakHKL-v1.rst new file mode 100644 index 000000000000..ab768f8c24c9 --- /dev/null +++ b/Code/Mantid/docs/source/algorithms/AddPeakHKL-v1.rst @@ -0,0 +1,62 @@ + +.. algorithm:: + +.. summary:: + +.. alias:: + +.. properties:: + +Description +----------- + +Add a peak in the HKL frame to an existing :ref:`PeaksWorkspace `. The OrientedLattice must be provided and the Goniometer should be set correctly before running the algorithm. + +The peak is added to the existing PeaksWorkspace. Run information and goniometer setting and the UB matrix are taken from the provided PeaksWorkspace. + + +Usage +----- + +**Example - AddPeakHKL** + +.. testcode:: AddPeakHKLExample + + import os + import numpy + import mantid.kernel + + # Create an instrument workspace + inst_dir = config.getInstrumentDirectory() + sxd_ws = LoadEmptyInstrument(os.path.join(inst_dir, "SXD_Definition.xml")) + AddSampleLog(sxd_ws, 'run_number', '1', 'Number') + + # Create a peaks workspace from the instrument workspace + peak_ws = CreatePeaksWorkspace(InstrumentWorkspace=sxd_ws, NumberOfPeaks=0) + peak_ws.mutableRun().addProperty('run_number', sxd_ws.run().getProperty('run_number'), True) + + # Apply a UB + ub = numpy.array([[-0.15097235, 0.09164432, 0.00519473], [ 0.0831895, 0.14123681, -0.06719047], [-0.03845029, -0.05534039, -0.1633801 ]]) + SetUB(peak_ws, UB=ub) + + # Add a new peak + AddPeakHKL(peak_ws, [2, 0, -4]) + + # Get info on newly added peak + peak = peak_ws.getPeak(0) + print 'Peak wavelength', round(peak.getWavelength(), 4) + print 'Peak detector id', peak.getDetectorID() + print 'Peak run number', peak.getRunNumber() + print 'Peak HKL', peak.getHKL() + +Output: + +.. testoutput:: AddPeakHKLExample + + Peak wavelength 1.8423 + Peak detector id 25766 + Peak run number 1 + Peak HKL [2,0,-4] + +.. categories:: +