From 8ac50cb8b0d19a5b13ab019de3ad07baa8665531 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Wed, 27 Aug 2014 11:14:33 +0100 Subject: [PATCH 01/37] Add a SampleShapeValidator type It is used for checking that a workspace has a valid sample shape. Refs #10169 --- Code/Mantid/Framework/API/CMakeLists.txt | 31 ++++++----- .../API/inc/MantidAPI/SampleShapeValidator.h | 52 +++++++++++++++++++ .../API/src/SampleShapeValidator.cpp | 50 ++++++++++++++++++ .../API/test/SampleShapeValidatorTest.h | 46 ++++++++++++++++ 4 files changed, 165 insertions(+), 14 deletions(-) create mode 100644 Code/Mantid/Framework/API/inc/MantidAPI/SampleShapeValidator.h create mode 100644 Code/Mantid/Framework/API/src/SampleShapeValidator.cpp create mode 100644 Code/Mantid/Framework/API/test/SampleShapeValidatorTest.h diff --git a/Code/Mantid/Framework/API/CMakeLists.txt b/Code/Mantid/Framework/API/CMakeLists.txt index ae0dfd6951d5..e5a323064068 100644 --- a/Code/Mantid/Framework/API/CMakeLists.txt +++ b/Code/Mantid/Framework/API/CMakeLists.txt @@ -10,7 +10,7 @@ set ( SRC_FILES src/AnalysisDataService.cpp src/ArchiveSearchFactory.cpp src/Axis.cpp - src/BinEdgeAxis.cpp + src/BinEdgeAxis.cpp src/BoxController.cpp src/CatalogFactory.cpp src/CatalogManager.cpp @@ -42,9 +42,9 @@ set ( SRC_FILES src/FunctionProperty.cpp src/FunctionValues.cpp src/GridDomain.cpp - src/GridDomain1D.cpp - src/HistoryItem.cpp - src/HistoryView.cpp + src/GridDomain1D.cpp + src/HistoryItem.cpp + src/HistoryView.cpp src/IDomainCreator.cpp src/IEventList.cpp src/IEventWorkspace.cpp @@ -104,8 +104,9 @@ set ( SRC_FILES src/Run.cpp src/Sample.cpp src/SampleEnvironment.cpp + src/SampleShapeValidator.cpp src/ScopedWorkspace.cpp - src/ScriptBuilder.cpp + src/ScriptBuilder.cpp src/ScriptRepository.cpp src/ScriptRepositoryFactory.cpp src/SpectraAxis.cpp @@ -143,7 +144,7 @@ set ( INC_FILES inc/MantidAPI/AnalysisDataService.h inc/MantidAPI/ArchiveSearchFactory.h inc/MantidAPI/Axis.h - inc/MantidAPI/BinEdgeAxis.h + inc/MantidAPI/BinEdgeAxis.h inc/MantidAPI/BoxController.h inc/MantidAPI/CatalogFactory.h inc/MantidAPI/CatalogManager.h @@ -180,9 +181,9 @@ set ( INC_FILES inc/MantidAPI/FunctionProperty.h inc/MantidAPI/FunctionValues.h inc/MantidAPI/GridDomain.h - inc/MantidAPI/GridDomain1D.h + inc/MantidAPI/GridDomain1D.h inc/MantidAPI/HistoryItem.h - inc/MantidAPI/HistoryView.h + inc/MantidAPI/HistoryView.h inc/MantidAPI/IAlgorithm.h inc/MantidAPI/IArchiveSearch.h inc/MantidAPI/IBackgroundFunction.h @@ -262,8 +263,9 @@ set ( INC_FILES inc/MantidAPI/Run.h inc/MantidAPI/Sample.h inc/MantidAPI/SampleEnvironment.h + inc/MantidAPI/SampleShapeValidator.h inc/MantidAPI/ScopedWorkspace.h - inc/MantidAPI/ScriptBuilder.h + inc/MantidAPI/ScriptBuilder.h inc/MantidAPI/ScriptRepository.h inc/MantidAPI/ScriptRepositoryFactory.h inc/MantidAPI/SingleValueParameter.h @@ -296,7 +298,7 @@ set ( TEST_FILES AlgorithmTest.h AnalysisDataServiceTest.h AsynchronousTest.h - BinEdgeAxisTest.h + BinEdgeAxisTest.h BoxControllerTest.h CompositeFunctionTest.h CoordTransformTest.h @@ -316,8 +318,8 @@ set ( TEST_FILES FunctionPropertyTest.h FunctionTest.h FunctionValuesTest.h - HistoryItemTest.h - HistoryViewTest.h + HistoryItemTest.h + HistoryViewTest.h IEventListTest.h IFunction1DSpectrumTest.h IFunction1DTest.h @@ -352,9 +354,10 @@ set ( TEST_FILES PropertyNexusTest.h RunTest.h SampleEnvironmentTest.h + SampleShapeValidatorTest.h SampleTest.h - ScriptBuilderTest.h ScopedWorkspaceTest.h + ScriptBuilderTest.h SpectraAxisTest.h SpectrumDetectorMappingTest.h TextAxisTest.h @@ -362,8 +365,8 @@ set ( TEST_FILES VectorParameterTest.h WorkspaceFactoryTest.h WorkspaceGroupTest.h - WorkspaceHistoryTest.h WorkspaceHistoryIOTest.h + WorkspaceHistoryTest.h WorkspaceOpOverloadsTest.h WorkspacePropertyTest.h ) diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/SampleShapeValidator.h b/Code/Mantid/Framework/API/inc/MantidAPI/SampleShapeValidator.h new file mode 100644 index 000000000000..57270adc5444 --- /dev/null +++ b/Code/Mantid/Framework/API/inc/MantidAPI/SampleShapeValidator.h @@ -0,0 +1,52 @@ +#ifndef MANTID_API_SAMPLESHAPEVALIDATOR_H_ +#define MANTID_API_SAMPLESHAPEVALIDATOR_H_ + +#include "MantidAPI/DllConfig.h" +#include "MantidAPI/ExperimentInfo.h" + +#include "MantidKernel/TypedValidator.h" + +namespace Mantid +{ + namespace API + { + + /** + Verify that a workspace has valid sample shape. + + 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 MANTID_API_DLL SampleShapeValidator : + public Kernel::TypedValidator > + { + public: + std::string getType() const; + Kernel::IValidator_sptr clone() const; + + private: + std::string checkValidity(const boost::shared_ptr& value) const; + }; + + } // namespace API +} // namespace Mantid + +#endif /* MANTID_API_SAMPLESHAPEVALIDATOR_H_ */ diff --git a/Code/Mantid/Framework/API/src/SampleShapeValidator.cpp b/Code/Mantid/Framework/API/src/SampleShapeValidator.cpp new file mode 100644 index 000000000000..a4b6d74adafd --- /dev/null +++ b/Code/Mantid/Framework/API/src/SampleShapeValidator.cpp @@ -0,0 +1,50 @@ +//----------------------------------------------------------------------------- +// Includes +//----------------------------------------------------------------------------- +#include "MantidAPI/SampleShapeValidator.h" +#include "MantidGeometry/Objects/Object.h" + +namespace Mantid +{ + namespace API + { + //----------------------------------------------------------------------------- + // Public methods + //----------------------------------------------------------------------------- + + /// @copydoc TypedValidator::getType + std::string SampleShapeValidator::getType() const + { + return "SampleShape"; + } + + /// @copydoc TypedValidator::clone + Kernel::IValidator_sptr SampleShapeValidator::clone() const + { + return boost::make_shared(); + } + + //----------------------------------------------------------------------------- + // Private methods + //----------------------------------------------------------------------------- + + /** + * Checks that the workspace has a valid sample shape defined + * @param value :: The workspace to test + * @return A user level description if a problem exists or "" + */ + std::string SampleShapeValidator::checkValidity( const boost::shared_ptr& value ) const + { + const auto & sampleShape = value->sample().getShape(); + if(sampleShape.hasValidShape()) + { + return ""; + } + else + { + return "Invalid or no shape defined for sample"; + } + } + + } // namespace API +} // namespace Mantid diff --git a/Code/Mantid/Framework/API/test/SampleShapeValidatorTest.h b/Code/Mantid/Framework/API/test/SampleShapeValidatorTest.h new file mode 100644 index 000000000000..e61bcef54ef4 --- /dev/null +++ b/Code/Mantid/Framework/API/test/SampleShapeValidatorTest.h @@ -0,0 +1,46 @@ +#ifndef MANTID_API_SAMPLESHAPEVALIDATORTEST_H_ +#define MANTID_API_SAMPLESHAPEVALIDATORTEST_H_ + +#include + +#include "MantidAPI/SampleShapeValidator.h" +#include "MantidTestHelpers/ComponentCreationHelper.h" +#include "MantidTestHelpers/FakeObjects.h" + +#include "boost/make_shared.hpp" + +using Mantid::API::SampleShapeValidator; + +class SampleShapeValidatorTest : 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 SampleShapeValidatorTest *createSuite() { return new SampleShapeValidatorTest(); } + static void destroySuite( SampleShapeValidatorTest *suite ) { delete suite; } + + void test_validator_passes_for_workspace_with_defined_sample_shape() + { + auto fakeWS = boost::make_shared(); + // Add a sample shape + auto sphere = ComponentCreationHelper::createSphere(1.0, V3D(), "sphere"); + fakeWS->mutableSample().setShape(*sphere); + + auto sampleValidator = boost::make_shared(); + TS_ASSERT_EQUALS( sampleValidator->isValid(fakeWS), "" ); + } + + void test_validator_throws_error_for_workspace_without_shape() + { + auto fakeWS = boost::make_shared(); + + auto sampleValidator = boost::make_shared(); + TS_ASSERT_EQUALS( sampleValidator->isValid(fakeWS), + "Invalid or no shape defined for sample" ); + } + + +}; + + +#endif /* MANTID_API_SAMPLESHAPEVALIDATORTEST_H_ */ From 660c36a138f7a9dc0ff79739d0e751af408a3b17 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Mon, 1 Sep 2014 17:56:21 +0100 Subject: [PATCH 02/37] Add unit & performance test for V3D::rotate. Refs #10169 --- Code/Mantid/Framework/Kernel/test/V3DTest.h | 99 +++++++++++++++++++++ 1 file changed, 99 insertions(+) diff --git a/Code/Mantid/Framework/Kernel/test/V3DTest.h b/Code/Mantid/Framework/Kernel/test/V3DTest.h index 98f6367c1948..e5d7fb2112c9 100644 --- a/Code/Mantid/Framework/Kernel/test/V3DTest.h +++ b/Code/Mantid/Framework/Kernel/test/V3DTest.h @@ -8,6 +8,7 @@ #include #include "MantidKernel/V3D.h" +#include "MantidKernel/Matrix.h" #include "MantidTestHelpers/NexusTestHelper.h" using Mantid::Kernel::V3D; @@ -300,6 +301,63 @@ class V3DTest: public CxxTest::TestSuite TS_ASSERT_DELTA( a.angle(d), M_PI, 0.0001); } + void testRotate() + { + V3D direc(1,1,1); + const double theta = 45.0*M_PI/180.0; + const double invRt2(1.0/sqrt(2.0)); + + // rotate around X + Mantid::Kernel::Matrix rx(3,3); + rx[0][0] = 1.0; + rx[1][1] = cos(theta); + rx[1][2] = -sin(theta); + rx[2][2] = cos(theta); + rx[2][1] = sin(theta); + direc.rotate(rx); + + TS_ASSERT_DELTA(direc.X(), 1.0, 1e-08); + TS_ASSERT_DELTA(direc.Y(), 0.0, 1e-08); + TS_ASSERT_DELTA(direc.Z(), 2.0*invRt2, 1e-08); + + // rotate around Y + direc = V3D(1,1,1); + Mantid::Kernel::Matrix ry(3,3); + ry[0][0] = cos(theta); + ry[0][2] = sin(theta); + ry[1][1] = 1.0; + ry[2][0] = -sin(theta); + ry[2][2] = cos(theta); + direc.rotate(ry); + + TS_ASSERT_DELTA(direc.X(), 2.0*invRt2, 1e-08); + TS_ASSERT_DELTA(direc.Y(), 1.0, 1e-08); + TS_ASSERT_DELTA(direc.Z(), 0.0, 1e-08); + + // rotate around Z + direc = V3D(1,1,1); + Mantid::Kernel::Matrix rz(3,3); + rz[0][0] = cos(theta); + rz[0][1] = -sin(theta); + rz[1][0] = sin(theta); + rz[1][1] = cos(theta); + rz[2][2] = 1.0; + direc.rotate(rz); + + TS_ASSERT_DELTA(direc.X(), 0.0, 1e-08); + TS_ASSERT_DELTA(direc.Y(), 2.0*invRt2, 1e-08); + TS_ASSERT_DELTA(direc.Z(), 1.0, 1e-08); + + // General rotation + Mantid::Kernel::Matrix Rt = rz*ry*rx; + direc = V3D(1,1,1); + direc.rotate(Rt); + + TS_ASSERT_DELTA(direc.X(), invRt2*(1+invRt2), 1e-08); + TS_ASSERT_DELTA(direc.Y(), invRt2*(1+invRt2), 1e-08); + TS_ASSERT_DELTA(direc.Z(), 1.0-invRt2, 1e-08); + } + void testSpherical() { double r = 3, theta = 45.0, phi = 45.0; @@ -539,4 +597,45 @@ class V3DTest: public CxxTest::TestSuite }; +//--------------------------------------------------------------------------- +// Performance tests +//--------------------------------------------------------------------------- + +class V3DTestPerformance : 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 V3DTestPerformance *createSuite() { return new V3DTestPerformance(); } + static void destroySuite( V3DTestPerformance *suite ) { delete suite; } + + V3DTestPerformance() + { + const double theta = 45.0*M_PI/180.0; + + // rotate around X + m_rotx = Mantid::Kernel::Matrix(3,3); + m_rotx[0][0] = 1.0; + m_rotx[1][1] = cos(theta); + m_rotx[1][2] = -sin(theta); + m_rotx[2][2] = cos(theta); + m_rotx[2][1] = sin(theta); + } + + void testRotate() + { + V3D direction(1.0,1.0,1.0); + for(size_t i = 0; i < 100000; ++i) + { + direction = V3D(1.0,1.0,1.0); + direction.rotate(m_rotx); + } + // Do something so the compiler doesn't optimise the loop away + TS_ASSERT_DELTA(direction.Y(), 0.0, 1e-08); + } + +private: + Mantid::Kernel::Matrix m_rotx; +}; + #endif From 86d75d393245c1ecc139acd3ad840d9281c4ef1e Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Tue, 2 Sep 2014 08:19:34 +0100 Subject: [PATCH 03/37] Fix doxygen warnings in SampleShapeValidator Refs #10169 --- Code/Mantid/Framework/API/src/SampleShapeValidator.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Mantid/Framework/API/src/SampleShapeValidator.cpp b/Code/Mantid/Framework/API/src/SampleShapeValidator.cpp index a4b6d74adafd..21ef26c0e499 100644 --- a/Code/Mantid/Framework/API/src/SampleShapeValidator.cpp +++ b/Code/Mantid/Framework/API/src/SampleShapeValidator.cpp @@ -12,13 +12,13 @@ namespace Mantid // Public methods //----------------------------------------------------------------------------- - /// @copydoc TypedValidator::getType + /// @return A string identifier for the type of validator std::string SampleShapeValidator::getType() const { return "SampleShape"; } - /// @copydoc TypedValidator::clone + /// @return A copy of the validator as a new object Kernel::IValidator_sptr SampleShapeValidator::clone() const { return boost::make_shared(); From 576347b030ee2b752c54f31caf5ac7c9157eeb63 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Mon, 1 Sep 2014 17:57:56 +0100 Subject: [PATCH 04/37] Improve performance of V3D::rotate. Avoids instantiating two additional matrices for the calculation. Refs #10169 --- Code/Mantid/Framework/Kernel/src/V3D.cpp | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/Code/Mantid/Framework/Kernel/src/V3D.cpp b/Code/Mantid/Framework/Kernel/src/V3D.cpp index 8e06fb397002..1710acb8373b 100644 --- a/Code/Mantid/Framework/Kernel/src/V3D.cpp +++ b/Code/Mantid/Framework/Kernel/src/V3D.cpp @@ -556,15 +556,10 @@ V3D::rotate(const Kernel::Matrix& A) @param A :: Rotation matrix (needs to be >3x3) */ { - Matrix Pv(3,1); - Pv[0][0]=x; - Pv[1][0]=y; - Pv[2][0]=z; - Matrix Po=A*Pv; - x=Po[0][0]; - y=Po[1][0]; - z=Po[2][0]; - return; + double xold(x), yold(y), zold(z); + x = A[0][0]*xold + A[0][1]*yold + A[0][2]*zold; + y = A[1][0]*xold + A[1][1]*yold + A[1][2]*zold; + z = A[2][0]*xold + A[2][1]*yold + A[2][2]*zold; } /** From 769898783948258b70b4d87719f74ac894c34e92 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Mon, 1 Sep 2014 11:12:07 +0100 Subject: [PATCH 05/37] Checkpoint CalculateMSVesuvio Refs #10169 --- .../Framework/CurveFitting/CMakeLists.txt | 7 +- .../MantidCurveFitting/CalculateMSVesuvio.h | 140 ++++++ .../CurveFitting/src/CalculateMSVesuvio.cpp | 421 ++++++++++++++++++ .../test/CalculateMSVesuvioTest.h | 147 ++++++ 4 files changed, 713 insertions(+), 2 deletions(-) create mode 100644 Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h create mode 100644 Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp create mode 100644 Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h diff --git a/Code/Mantid/Framework/CurveFitting/CMakeLists.txt b/Code/Mantid/Framework/CurveFitting/CMakeLists.txt index c0906757d86a..403c02e38b34 100644 --- a/Code/Mantid/Framework/CurveFitting/CMakeLists.txt +++ b/Code/Mantid/Framework/CurveFitting/CMakeLists.txt @@ -12,6 +12,7 @@ set ( SRC_FILES src/Bk2BkExpConvPV.cpp src/BoundaryConstraint.cpp src/CalculateGammaBackground.cpp + src/CalculateMSVesuvio.cpp src/Chebyshev.cpp src/ComptonPeakProfile.cpp src/ComptonProfile.cpp @@ -99,7 +100,7 @@ set ( SRC_FILES src/FakeMinimizer.cpp ) - + set ( SRC_UNITY_IGNORE_FILES src/Fit1D.cpp src/GSLFunctions.cpp ) set ( INC_FILES @@ -117,6 +118,7 @@ set ( INC_FILES inc/MantidCurveFitting/Bk2BkExpConvPV.h inc/MantidCurveFitting/BoundaryConstraint.h inc/MantidCurveFitting/CalculateGammaBackground.h + inc/MantidCurveFitting/CalculateMSVesuvio.h inc/MantidCurveFitting/Chebyshev.h inc/MantidCurveFitting/ComptonPeakProfile.h inc/MantidCurveFitting/ComptonProfile.h @@ -206,7 +208,7 @@ set ( INC_FILES inc/MantidCurveFitting/VesuvioResolution.h inc/MantidCurveFitting/Voigt.h ) - + set ( TEST_FILES # ChebyshevPolynomialBackgroundTest.h # RefinePowderInstrumentParametersTest.h @@ -220,6 +222,7 @@ set ( TEST_FILES Bk2BkExpConvPVTest.h BoundaryConstraintTest.h CalculateGammaBackgroundTest.h + CalculateMSVesuvioTest.h ChebyshevTest.h CompositeFunctionTest.h ComptonPeakProfileTest.h diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h new file mode 100644 index 000000000000..9ba2e2e8c0af --- /dev/null +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -0,0 +1,140 @@ +#ifndef MANTID_CURVEFITTING_CALCULATEMSVESUVIO_H_ +#define MANTID_CURVEFITTING_CALCULATEMSVESUVIO_H_ +//----------------------------------------------------------------------------- +// Includes +//----------------------------------------------------------------------------- +#include "MantidAPI/Algorithm.h" +#include "MantidGeometry/Instrument/ReferenceFrame.h" + +#include +#include +#include + +namespace Mantid +{ + namespace CurveFitting + { + //----------------------------------------------------------------------------- + // CurveFitting forward declarations + //----------------------------------------------------------------------------- + struct DetectorParams; + struct ResolutionParams; + + /** + Calculates the multiple scattering & total scattering contributions + for a flat-plate or cylindrical sample. + + 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 CalculateMSVesuvio : public API::Algorithm + { + private: + // Struct to store cache instrument geometry + struct InstrumentGeometry + { + InstrumentGeometry(); + + boost::shared_ptr refframe; + double srcR1; + double srcR2; + double sampleHeight; + double sampleWidth; + }; + // Produces random numbers with various probability distributions + class RandomNumberGenerator + { + typedef boost::uniform_real uniform_double; + typedef boost::normal_distribution gaussian_double; + public: + RandomNumberGenerator(const int seed); + /// Returns a flat random number between 0.0 & 1.0 + double flat(); + /// Returns a random number distributed by a normal distribution + double gaussian(const double mean, const double sigma); + + private: + boost::mt19937 m_generator; + }; + // Stores counts for each scatter order + // for a "run" of a given number of events + struct Simulation + { + Simulation(const size_t order, const size_t ntimes); + + std::vector> counts; + size_t maxorder; + double weight; + size_t nmscat; + }; + // Accumulates and averages the results of each simulation + struct SimulationAggregator + { + SimulationAggregator(const size_t nruns, + const size_t order, + const size_t ntimes); + // Adds a result as part of the average + void add(const Simulation & result); + + Simulation averaged; + double prefactor; + }; + + public: + CalculateMSVesuvio(); + ~CalculateMSVesuvio(); + + 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(); + + void cacheInputGeometry(); + void calculateMS(const size_t wsIndex, API::ISpectrum & totalsc, + API::ISpectrum & multsc) const; + Simulation simulate(const size_t nevents, const size_t nscatters, + const DetectorParams & detpar, + const ResolutionParams &respar) const; + double calculateTOF(const size_t nscatters, + const DetectorParams & detpar, + const ResolutionParams &respar, + Simulation & counts) const; + + // single-event helpers + double initialTOF(const double mean, const double sigma) const; + double moderatorPos(double & widthPos, double & heightPos) const; + + // Member Variables + RandomNumberGenerator *m_randgen; + InstrumentGeometry m_instgeom; + + API::MatrixWorkspace_sptr m_inputWS; + API::Progress *m_progress; + }; + + } // namespace CurveFitting +} // namespace Mantid + +#endif /* MANTID_CURVEFITTING_CALCULATEMSVESUVIO_H_ */ diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp new file mode 100644 index 000000000000..2af53eacf4d4 --- /dev/null +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -0,0 +1,421 @@ +//----------------------------------------------------------------------------- +// Includes +// +#include "MantidCurveFitting/CalculateMSVesuvio.h" +// Use helpers for storing detector/resolution parameters +#include "MantidCurveFitting/ConvertToYSpace.h" +#include "MantidCurveFitting/VesuvioResolution.h" + +#include "MantidAPI/SampleShapeValidator.h" +#include "MantidAPI/WorkspaceValidators.h" + +#include "MantidGeometry/Instrument/DetectorGroup.h" +#include "MantidGeometry/Instrument/ParameterMap.h" + +#include "MantidKernel/BoundedValidator.h" +#include "MantidKernel/CompositeValidator.h" +#include "MantidKernel/MersenneTwister.h" + +#include + +namespace Mantid +{ + namespace CurveFitting + { + using namespace Kernel; + using namespace API; + + namespace + { + const size_t NSIMULATIONS = 10; + const size_t NEVENTS = 500000; + const size_t NSCATTERS = 3; + } // end anonymous namespace + + //------------------------------------------------------------------------- + // InstrumentGeometry cache + //------------------------------------------------------------------------- + + CalculateMSVesuvio::InstrumentGeometry::InstrumentGeometry() : + refframe(), srcR1(0.0), srcR2(0.0), + sampleHeight(0.0), sampleWidth(0.0) + { + } + + //------------------------------------------------------------------------- + // RandomNumberGenerator helper + //------------------------------------------------------------------------- + /** + * Produces random numbers with various probability distributions + */ + CalculateMSVesuvio:: + RandomNumberGenerator::RandomNumberGenerator(const int seed) : m_generator() + { + m_generator.seed(static_cast(seed)); + } + /// Returns a flat random number between 0.0 & 1.0 + double CalculateMSVesuvio:: + RandomNumberGenerator::flat() + { + return uniform_double()(m_generator, + uniform_double::param_type(0.0, 1.0)); + } + /// Returns a random number distributed by a normal distribution + double CalculateMSVesuvio:: + RandomNumberGenerator::gaussian(const double mean, const double sigma) + { + return gaussian_double()(m_generator, + gaussian_double::param_type(mean, sigma)); + } + + //------------------------------------------------------------------------- + // Simulation helpers + //------------------------------------------------------------------------- + /** + * Stores counts for each scatter order + * for a "run" of a given number of events + */ + CalculateMSVesuvio::Simulation:: + Simulation(const size_t order, const size_t ntimes) : + counts(order, std::vector(ntimes)), + maxorder(order), weight(0.0), nmscat(0) + {} + + /** + * Accumulates and averages the results + * of each simulation + */ + CalculateMSVesuvio::SimulationAggregator:: + SimulationAggregator(const size_t nruns, + const size_t order, + const size_t ntimes) : + averaged(order, ntimes), + prefactor(1.0/static_cast(nruns)) + {} + + /** Adds a result as part of the average + * @param result A new simulation result + */ + void CalculateMSVesuvio::SimulationAggregator:: + add(const Simulation & result) + { + // No check is performed whether the number of + // stated runs has been reached or the order is the same + for(size_t i = 0; i < averaged.maxorder; ++i) + { + auto & avgcounts = averaged.counts[i]; + const auto & rescounts = result.counts[i]; + std::vector::iterator avgIter(avgcounts.begin()); + std::vector::const_iterator resIter(rescounts.begin()); + for(; avgIter != avgcounts.end(); ++avgIter, ++resIter) + { + *avgIter += prefactor*(*resIter); + } + } + } + + //------------------------------------------------------------------------- + // Algorithm definitions + //------------------------------------------------------------------------- + + // Register the algorithm into the AlgorithmFactory + DECLARE_ALGORITHM(CalculateMSVesuvio) + + /// Constructor + CalculateMSVesuvio::CalculateMSVesuvio() : Algorithm(), + m_randgen(NULL), m_instgeom(), + + m_inputWS(), + m_progress(NULL) + { + } + + /// Destructor + CalculateMSVesuvio::~CalculateMSVesuvio() + { + delete m_randgen; + } + + /// @copydoc Algorithm::name + const std::string CalculateMSVesuvio::name() const + { + return "CalculateMSVesuvio"; + } + + /// @copydoc Algorithm::version + int CalculateMSVesuvio::version() const + { + return 1; + } + + /// @copydoc Algorithm::category + const std::string CalculateMSVesuvio::category() const + { + return "Corrections"; + } + + /// @copydoc Algorithm::summary + const std::string CalculateMSVesuvio::summary() const + { + return "Corrects for the effects of multiple scattering " + "on a flat plate or cylindrical sample."; + } + + /** + * Initialize the algorithm's properties. + */ + void CalculateMSVesuvio::init() + { + // Inputs + auto inputWSValidator = boost::make_shared(); + inputWSValidator->add("TOF"); + inputWSValidator->add(); + declareProperty(new WorkspaceProperty<>("InputWorkspace","", + Direction::Input, inputWSValidator), + "Input workspace to be corrected, in units of TOF."); + + auto positiveNonZero = boost::make_shared>(); + positiveNonZero->setLower(0.0); + positiveNonZero->setLowerExclusive(true); + declareProperty("BeamUmbraRadius", -1.0, positiveNonZero, + "Radius, in cm, of part in total shadow."); + declareProperty("BeamPenumbraRadius", -1.0, positiveNonZero, + "Radius, in cm, of part in partial shadow."); + setPropertyGroup("BeamUmbraRadius", "Beam"); + setPropertyGroup("BeamPenumbraRadius", "Beam"); + + auto positiveInt = boost::make_shared>(); + positiveInt->setLower(1); + declareProperty("Seed", 123456789, positiveInt, + "Seed the random number generator with this value"); + + // Outputs + declareProperty(new WorkspaceProperty<>("TotalScatteringWS","", Direction::Output), + "Workspace to store the calculated total scattering counts"); + declareProperty(new WorkspaceProperty<>("MultipleScatteringWS","", Direction::Output), + "Workspace to store the calculated total scattering counts"); + } + + /** + * Execute the algorithm. + */ + void CalculateMSVesuvio::exec() + { + m_inputWS = getProperty("InputWorkspace"); + cacheInputGeometry(); + // Values used frequently + const int64_t nhist = static_cast(m_inputWS->getNumberHistograms()); + + // Create new workspaces + MatrixWorkspace_sptr totalsc = WorkspaceFactory::Instance().create(m_inputWS); + MatrixWorkspace_sptr multsc = WorkspaceFactory::Instance().create(m_inputWS); + + // Initialize random number generator + m_randgen = new RandomNumberGenerator(getProperty("Seed")); + + // Setup progress + m_progress = new API::Progress(this, 0.0, 1.0, nhist); + for(int64_t i = 0; i < nhist; ++i) + { + m_progress->report("Calculating corrections"); + + // Copy over the X-values + const MantidVec & xValues = m_inputWS->readX(i); + totalsc->dataX(i) = xValues; + multsc->dataX(i) = xValues; + + // Final detector position + Geometry::IDetector_const_sptr detector; + try + { + detector = m_inputWS->getDetector(i); + } + catch(Kernel::Exception::NotFoundError&) + { + // intel compiler doesn't like continue in a catch inside an OMP loop + } + if(!detector) + { + std::ostringstream os; + os << "No valid detector object found for spectrum at workspace index '" << i << "'. No correction calculated."; + g_log.information(os.str()); + continue; + } + + // the output spectrum objects have references to where the data will be stored + calculateMS(i, *totalsc->getSpectrum(i), *multsc->getSpectrum(i)); + } + + setProperty("TotalScatteringWS", totalsc); + setProperty("MultipleScatteringWS", multsc); + } + + /** + * Caches sample, detector geometry for speed in later calculations + */ + void CalculateMSVesuvio::cacheInputGeometry() + { + const auto instrument = m_inputWS->getInstrument(); + const auto rframe = instrument->getReferenceFrame(); + m_instgeom.refframe = rframe; + + m_instgeom.srcR1 = getProperty("BeamUmbraRadius"); + m_instgeom.srcR2 = getProperty("BeamPenumbraRadius"); + if(m_instgeom.srcR2 < m_instgeom.srcR1) + { + std::ostringstream os; + os << "Invalid beam radius parameters. Penumbra value=" + << m_instgeom.srcR2 << " < Umbra value=" + << m_instgeom.srcR1; + throw std::invalid_argument(os.str()); + } + + // Sample shape + const auto & sampleShape = m_inputWS->sample().getShape(); + // We know the shape is valid from the property validator but we + // need to check if it is cuboid or cylinder + int objType(-1); + double radius(-1.0), height(-1.0); + std::vector pts; + sampleShape.GetObjectGeom(objType, pts, radius, height); + if(objType != 1 && objType != 3) + { + throw std::invalid_argument("Invalid sample shape. Currently only " + "cuboid or cylinder are supported"); + } + assert(pts.size() == 4); + if(objType == 1) // cuboid + { + auto horiz = rframe->pointingHorizontal(); + m_instgeom.sampleWidth = fabs(pts[0][horiz] - pts[3][horiz]); + auto up = rframe->pointingUp(); + m_instgeom.sampleHeight = fabs(pts[0][up] - pts[1][up]); + } + else + { + m_instgeom.sampleWidth = 2.0*radius; + m_instgeom.sampleHeight = height; + } + } + + /** + * Calculate the total scattering and contributions from higher-order scattering for given + * spectrum + * @param wsIndex The index on the input workspace for the chosen spectrum + * @param totalsc A non-const reference to the spectrum that will contain the total scattering calculation + * @param multsc A non-const reference to the spectrum that will contain the multiple scattering contribution + */ + void CalculateMSVesuvio::calculateMS(const size_t wsIndex, API::ISpectrum & totalsc, + API::ISpectrum & multsc) const + { + // Detector information + DetectorParams detpar = ConvertToYSpace::getDetectorParameters(m_inputWS, wsIndex); + // t0 is stored in seconds here, whereas here we want microseconds + detpar.t0 *= 1e6; + + const Geometry::IDetector_const_sptr detector = m_inputWS->getDetector(wsIndex); + const auto & pmap = m_inputWS->instrumentParameters(); + // Resolution information + ResolutionParams respar; + respar.dl1 = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_l1"); + respar.dl2 = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_l2"); + respar.dthe = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_theta"); //radians + respar.dEnLorentz = ConvertToYSpace::getComponentParameter(detector, pmap, "hwhm_lorentz"); + respar.dEnGauss = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_gauss"); + + // Final counts averaged over all simulations + SimulationAggregator avgCounts(NSIMULATIONS, NSCATTERS, m_inputWS->blocksize()); + for(size_t i = 0; i < NSIMULATIONS; ++i) + { + avgCounts.add(simulate(NEVENTS, NSCATTERS, detpar, respar)); + } + + } + + /** + * Perform a single simulation of a given number of events for up to a maximum number of + * scatterings on a chosen detector + * @param nevents The number of neutron events to simulate + * @param nscatters Maximum order of scattering that should be simulated + * @param detpar Detector information describing the final detector position + * @param respar Resolution information on the intrument as a whole + * @return A new simulation object storing the calculated number of counts + */ + CalculateMSVesuvio::Simulation + CalculateMSVesuvio::simulate(const size_t nevents, const size_t nscatters, + const DetectorParams & detpar, + const ResolutionParams &respar) const + { + Simulation simulCounts(NSCATTERS, m_inputWS->blocksize()); + for(size_t i = 0; i < nevents; ++i) + { + simulCounts.weight += calculateTOF(nscatters, detpar, respar, simulCounts); + } + + return simulCounts; + } + + /** + * + * @param nscatters Maximum order of scattering that should be simulated + * @param detpar Detector information describing the final detector position + * @param respar Resolution information on the intrument as a whole + * @param counts [Output] Store the calculated counts here + * @return + */ + double CalculateMSVesuvio::calculateTOF(const size_t nscatters, const DetectorParams &detpar, + const ResolutionParams &respar, + Simulation &counts) const + { + double weightSum(0.0); + std::vector weightsPerScatter(nscatters, 1.0); // start at 1.0 + std::vector tofs(nscatters, 0.0); + + tofs[0] = initialTOF(0.0, detpar.t0); + // moderator coord in lab frame + V3D srcLab(0.0, -detpar.l1, 0.0); + weightsPerScatter[0] *= moderatorPos(srcLab[0], srcLab[2]); +// if(heightLab > m_instgeom.sampleHeight || +// widthLab > m_instgeom.sampleWidth) return 0.0; // misses sample + + + return weightSum; + } + + /** + * Sample a tof value from a gaussian distribution + * @param centre The value of the centra point of the distribution + * @param sigma Width of the distribution + * @return New variable distributed accordingly + */ + double CalculateMSVesuvio::initialTOF(const double centre, const double sigma) const + { + return m_randgen->gaussian(centre, sigma); + } + + /** + * Sample from the moderator assuming it can be seen + * as a cylindrical ring with inner and outer radius + * @param widthPos [Out] Position in the width direction of the generated point + * @param heightPos [Out] Position in the height direction of the generated point + */ + double CalculateMSVesuvio::moderatorPos(double &widthPos, double &heightPos) const + { + double radius(-1.0); + do + { + widthPos = -m_instgeom.srcR2 + 2.0*m_instgeom.srcR2*m_randgen->flat(); + heightPos = -m_instgeom.srcR2 + 2.0*m_instgeom.srcR2*m_randgen->flat(); + using std::sqrt; + radius = sqrt(widthPos*widthPos + heightPos*heightPos); + } + while(radius > m_instgeom.srcR2); + + if(radius > m_instgeom.srcR1) + return (m_instgeom.srcR2 - radius)/(m_instgeom.srcR2 - m_instgeom.srcR1); + else + return 1.0; // inside umbra unit weight + } + + } // namespace Algorithms +} // namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h new file mode 100644 index 000000000000..80f724597bba --- /dev/null +++ b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h @@ -0,0 +1,147 @@ +#ifndef MANTID_CURVEFITTING_CALCULATEMSVESUIVIOTEST_H_ +#define MANTID_CURVEFITTING_CALCULATEMSVESUIVIOTEST_H_ + +#include + +#include "MantidCurveFitting/CalculateMSVesuvio.h" + +#include "MantidTestHelpers/ComponentCreationHelper.h" +#include "MantidTestHelpers/WorkspaceCreationHelper.h" + +#include "ComptonProfileTestHelpers.h" + +using Mantid::CurveFitting::CalculateMSVesuvio; + +class CalculateMSVesuvioTest : 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 CalculateMSVesuvioTest *createSuite() { return new CalculateMSVesuvioTest(); } + static void destroySuite( CalculateMSVesuvioTest *suite ) { delete suite; } + + // ------------------------ Success Cases ----------------------------------------- + + void test_init() + { + CalculateMSVesuvio alg; + TS_ASSERT_THROWS_NOTHING(alg.initialize()); + TS_ASSERT(alg.isInitialized()); + } + + void test_exec_with_flat_plate_sample() + { + auto alg = createTestAlgorithm(createFlatPlateSampleWS()); + + TS_ASSERT_THROWS_NOTHING(alg->execute()); + TS_ASSERT(alg->isExecuted()); + } + + // ------------------------ Failure Cases ----------------------------------------- + + void test_setting_input_workspace_not_in_tof_throws_invalid_argument() + { + CalculateMSVesuvio alg; + alg.initialize(); + + auto testWS = WorkspaceCreationHelper::Create2DWorkspace(1, 1); + TS_ASSERT_THROWS(alg.setProperty("InputWorkspace", testWS), std::invalid_argument); + } + + void test_setting_workspace_with_no_sample_shape_throws_invalid_argument() + { + CalculateMSVesuvio alg; + alg.initialize(); + + auto testWS = WorkspaceCreationHelper::Create2DWorkspace(1, 1); + testWS->getAxis(0)->setUnit("TOF"); + TS_ASSERT_THROWS(alg.setProperty("InputWorkspace", testWS), std::invalid_argument); + } + + void test_input_workspace_and_sampleshape_not_cuboid_or_cylinder_throws_invalid_argument_on_execution() + { + auto testWS = createFlatPlateSampleWS(); + auto sampleShape = ComponentCreationHelper::createSphere(1, Mantid::Kernel::V3D(), + "sample-shape"); + testWS->mutableSample().setShape(*sampleShape); // overwrite shape + auto alg = createTestAlgorithm(testWS); + + TS_ASSERT_THROWS(alg->execute(), std::invalid_argument); + } + + void test_setting_zero_or_negative_beam_radius_values_throws_invalid_argument() + { + CalculateMSVesuvio alg; + alg.initialize(); + + TS_ASSERT_THROWS(alg.setProperty("BeamUmbraRadius", -1.5), std::invalid_argument); + TS_ASSERT_THROWS(alg.setProperty("BeamUmbraRadius", 0.0), std::invalid_argument); + TS_ASSERT_THROWS(alg.setProperty("BeamPenumbraRadius", -1.5), std::invalid_argument); + TS_ASSERT_THROWS(alg.setProperty("BeamPenumbraRadius", 0.0), std::invalid_argument); + } + + void test_setting_umbra_less_than_penumbra_throws_invalid_argument() + { + auto testWS = createFlatPlateSampleWS(); + auto alg = createTestAlgorithm(testWS); + + alg->setProperty("BeamUmbraRadius", 2.5); + alg->setProperty("BeamPenumbraRadius", 1.5); + + TS_ASSERT_THROWS(alg->execute(), std::invalid_argument); + } + +private: + + Mantid::API::IAlgorithm_sptr createTestAlgorithm(const Mantid::API::MatrixWorkspace_sptr & inputWS) + { + Mantid::API::IAlgorithm_sptr alg = boost::shared_ptr(new CalculateMSVesuvio); + alg->initialize(); + alg->setRethrows(true); + alg->setChild(true); + // inputs + alg->setProperty("InputWorkspace", inputWS); + alg->setProperty("BeamUmbraRadius", 1.5); + alg->setProperty("BeamPenumbraRadius", 2.5); + // outputs + alg->setPropertyValue("TotalScatteringWS", "__unused_for_child"); + alg->setPropertyValue("MultipleScatteringWS", "__unused_for_child"); + + return alg; + } + + struct ones + { + double operator()(const double, size_t) { return 1.0; } // don't care about Y values, just use 1.0 everywhere + }; + + Mantid::API::MatrixWorkspace_sptr createFlatPlateSampleWS() + { + auto testWS = createTestWorkspace(); + // Sample shape + const double height(0.1), width(0.02), thick(0.005); + auto sampleShape = ComponentCreationHelper::createCuboid(width, height, thick); + testWS->mutableSample().setShape(*sampleShape); + + return testWS; + } + + + + Mantid::API::MatrixWorkspace_sptr createTestWorkspace() + { + using namespace Mantid::Geometry; + using namespace Mantid::Kernel; + + const int nhist(1); + const double x0(50.0), x1(562.0), dx(1.0); + const bool singleMassSpec(false), foilChanger(false); + auto ws2d = ComptonProfileTestHelpers::createTestWorkspace(nhist, x0, x1, dx, singleMassSpec, foilChanger); + + return ws2d; + } + +}; + + +#endif /* MANTID_CURVEFITTING_CALCULATEMSVESUIVIOTEST_H_ */ From 56052cfc29f7dca3bed952e29ad550028d0267e7 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Wed, 3 Sep 2014 17:39:04 +0100 Subject: [PATCH 06/37] Add code for calculation of first scattering. Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 49 ++-- .../inc/MantidCurveFitting/ConvertToYSpace.h | 1 + .../CurveFitting/src/CalculateMSVesuvio.cpp | 268 ++++++++++++------ .../CurveFitting/src/ConvertToYSpace.cpp | 5 +- .../test/CalculateMSVesuvioTest.h | 31 +- 5 files changed, 237 insertions(+), 117 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index 9ba2e2e8c0af..c8b03cdff0c7 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -5,6 +5,7 @@ //----------------------------------------------------------------------------- #include "MantidAPI/Algorithm.h" #include "MantidGeometry/Instrument/ReferenceFrame.h" +#include "MantidKernel/V3D.h" #include #include @@ -48,17 +49,6 @@ namespace Mantid class DLLExport CalculateMSVesuvio : public API::Algorithm { private: - // Struct to store cache instrument geometry - struct InstrumentGeometry - { - InstrumentGeometry(); - - boost::shared_ptr refframe; - double srcR1; - double srcR2; - double sampleHeight; - double sampleWidth; - }; // Produces random numbers with various probability distributions class RandomNumberGenerator { @@ -117,21 +107,40 @@ namespace Mantid Simulation simulate(const size_t nevents, const size_t nscatters, const DetectorParams & detpar, const ResolutionParams &respar) const; - double calculateTOF(const size_t nscatters, - const DetectorParams & detpar, - const ResolutionParams &respar, - Simulation & counts) const; + double calculateCounts(const size_t nscatters, + const DetectorParams & detpar, + const ResolutionParams &respar, + Simulation & counts) const; // single-event helpers - double initialTOF(const double mean, const double sigma) const; - double moderatorPos(double & widthPos, double & heightPos) const; + double generateSrcPos(const double l, Kernel::V3D & srcPos) const; + double generateE0(const double l1, const double t2, double &weight) const; + double generateTOF(const double gaussTOF, const double en0, const double dl1) const; + Kernel::V3D generateScatter(const Kernel::V3D &startPos, const Kernel::V3D &direc, + double &weight) const; // Member Variables - RandomNumberGenerator *m_randgen; - InstrumentGeometry m_instgeom; + RandomNumberGenerator *m_randgen; // random number generator + + size_t m_acrossDir, m_upDir, m_beamDir; // indices of each direction + double m_srcR1; // beam umbra radius (m) + double m_srcR2; // beam penumbra radius (m) + double m_halfSampleHeight; // half-height of sample (m) + double m_halfSampleWidth; // half-width of sample (m) + double m_halfSampleThick; // half-thickness of sample(m) + double m_maxWidthSampleFrame; // Maximum width in sample frame (m) + Kernel::DblMatrix const *m_goniometer; // sample rotation + Geometry::Object const *m_sampleShape; // sample shape + Kernel::V3D m_samplePos; + + double m_mu; //attenuation xsec + + double m_tmin; // minimum tof value + double m_tmax; // maximum tof value + double m_dt; // tof value step - API::MatrixWorkspace_sptr m_inputWS; API::Progress *m_progress; + API::MatrixWorkspace_sptr m_inputWS; }; } // namespace CurveFitting diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ConvertToYSpace.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ConvertToYSpace.h index 3bbbfeaa2292..caadecb04434 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ConvertToYSpace.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/ConvertToYSpace.h @@ -14,6 +14,7 @@ namespace CurveFitting { double l1; ///< source-sample distance in metres double l2; ///< sample-detector distance in metres + Kernel::V3D pos; ///< Full 3D position double theta; ///< scattering angle in radians double t0; ///< time delay in seconds double efixed; ///< final energy diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 2af53eacf4d4..ce1e1279a513 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -9,8 +9,8 @@ #include "MantidAPI/SampleShapeValidator.h" #include "MantidAPI/WorkspaceValidators.h" -#include "MantidGeometry/Instrument/DetectorGroup.h" #include "MantidGeometry/Instrument/ParameterMap.h" +#include "MantidGeometry/Objects/Track.h" #include "MantidKernel/BoundedValidator.h" #include "MantidKernel/CompositeValidator.h" @@ -22,25 +22,22 @@ namespace Mantid { namespace CurveFitting { - using namespace Kernel; using namespace API; + using namespace Kernel; + using Geometry::Link; + using Geometry::ParameterMap; + using Geometry::Track; namespace { const size_t NSIMULATIONS = 10; - const size_t NEVENTS = 500000; + const size_t NEVENTS = 100000; const size_t NSCATTERS = 3; - } // end anonymous namespace + const size_t MAX_SCATTER_PT_TRIES = 500; + /// Conversion constant + const double MASS_TO_MEV = 0.5*PhysicalConstants::NeutronMass/PhysicalConstants::meV; - //------------------------------------------------------------------------- - // InstrumentGeometry cache - //------------------------------------------------------------------------- - - CalculateMSVesuvio::InstrumentGeometry::InstrumentGeometry() : - refframe(), srcR1(0.0), srcR2(0.0), - sampleHeight(0.0), sampleWidth(0.0) - { - } + } // end anonymous namespace //------------------------------------------------------------------------- // RandomNumberGenerator helper @@ -123,10 +120,13 @@ namespace Mantid /// Constructor CalculateMSVesuvio::CalculateMSVesuvio() : Algorithm(), - m_randgen(NULL), m_instgeom(), - - m_inputWS(), - m_progress(NULL) + m_randgen(NULL), + m_acrossDir(0), m_upDir(1), m_beamDir(3), m_srcR1(0.0), m_srcR2(0.0), + m_halfSampleHeight(0.0), m_halfSampleWidth(0.0), m_halfSampleThick(0.0), + m_maxWidthSampleFrame(0.0), m_goniometer(NULL), m_sampleShape(NULL), m_samplePos(), + m_mu(-1.0), + m_tmin(-1.0), m_tmax(-1.0), m_dt(-1.0), + m_progress(NULL), m_inputWS() { } @@ -204,7 +204,10 @@ namespace Mantid m_inputWS = getProperty("InputWorkspace"); cacheInputGeometry(); // Values used frequently - const int64_t nhist = static_cast(m_inputWS->getNumberHistograms()); + const auto & inX = m_inputWS->readX(0); + m_tmin = inX.front(); + m_tmax = inX.back(); + m_dt = inX[1] - m_tmin; // Create new workspaces MatrixWorkspace_sptr totalsc = WorkspaceFactory::Instance().create(m_inputWS); @@ -214,6 +217,7 @@ namespace Mantid m_randgen = new RandomNumberGenerator(getProperty("Seed")); // Setup progress + const int64_t nhist = static_cast(m_inputWS->getNumberHistograms()); m_progress = new API::Progress(this, 0.0, 1.0, nhist); for(int64_t i = 0; i < nhist; ++i) { @@ -256,46 +260,38 @@ namespace Mantid void CalculateMSVesuvio::cacheInputGeometry() { const auto instrument = m_inputWS->getInstrument(); + m_samplePos = instrument->getSample()->getPos(); + const auto rframe = instrument->getReferenceFrame(); - m_instgeom.refframe = rframe; + m_acrossDir = rframe->pointingHorizontal(); + m_upDir = rframe->pointingUp(); + m_beamDir = rframe->pointingAlongBeam(); - m_instgeom.srcR1 = getProperty("BeamUmbraRadius"); - m_instgeom.srcR2 = getProperty("BeamPenumbraRadius"); - if(m_instgeom.srcR2 < m_instgeom.srcR1) + m_srcR1 = getProperty("BeamUmbraRadius"); + m_srcR2 = getProperty("BeamPenumbraRadius"); + if(m_srcR2 < m_srcR1) { std::ostringstream os; os << "Invalid beam radius parameters. Penumbra value=" - << m_instgeom.srcR2 << " < Umbra value=" - << m_instgeom.srcR1; + << m_srcR2 << " < Umbra value=" + << m_srcR1; throw std::invalid_argument(os.str()); } + // Sample rotation specified by a goniometer + m_goniometer = &(m_inputWS->run().getGoniometerMatrix()); // Sample shape - const auto & sampleShape = m_inputWS->sample().getShape(); - // We know the shape is valid from the property validator but we - // need to check if it is cuboid or cylinder - int objType(-1); - double radius(-1.0), height(-1.0); - std::vector pts; - sampleShape.GetObjectGeom(objType, pts, radius, height); - if(objType != 1 && objType != 3) - { - throw std::invalid_argument("Invalid sample shape. Currently only " - "cuboid or cylinder are supported"); - } - assert(pts.size() == 4); - if(objType == 1) // cuboid - { - auto horiz = rframe->pointingHorizontal(); - m_instgeom.sampleWidth = fabs(pts[0][horiz] - pts[3][horiz]); - auto up = rframe->pointingUp(); - m_instgeom.sampleHeight = fabs(pts[0][up] - pts[1][up]); - } - else - { - m_instgeom.sampleWidth = 2.0*radius; - m_instgeom.sampleHeight = height; - } + m_sampleShape = &(m_inputWS->sample().getShape()); + // We know the shape is valid from the property validator + // Use the bounding box as an approximation to determine the extents + // as this will match in both height and width for a cuboid & cylinder + // sample shape + Geometry::BoundingBox bounds = m_sampleShape->getBoundingBox(); + V3D boxWidth = bounds.width(); + // Use half-width/height for easier calculation later + m_halfSampleWidth = 0.5*boxWidth[m_acrossDir]; + m_halfSampleHeight = 0.5*boxWidth[m_upDir]; + m_halfSampleThick = 0.5*boxWidth[m_beamDir]; } /** @@ -349,7 +345,7 @@ namespace Mantid Simulation simulCounts(NSCATTERS, m_inputWS->blocksize()); for(size_t i = 0; i < nevents; ++i) { - simulCounts.weight += calculateTOF(nscatters, detpar, respar, simulCounts); + simulCounts.weight += calculateCounts(nscatters, detpar, respar, simulCounts); } return simulCounts; @@ -363,59 +359,169 @@ namespace Mantid * @param counts [Output] Store the calculated counts here * @return */ - double CalculateMSVesuvio::calculateTOF(const size_t nscatters, const DetectorParams &detpar, - const ResolutionParams &respar, - Simulation &counts) const + double CalculateMSVesuvio::calculateCounts(const size_t nscatters, const DetectorParams &detpar, + const ResolutionParams &respar, + Simulation &counts) const { double weightSum(0.0); - std::vector weightsPerScatter(nscatters, 1.0); // start at 1.0 + std::vector weights(nscatters, 1.0); // start at 1.0 std::vector tofs(nscatters, 0.0); - tofs[0] = initialTOF(0.0, detpar.t0); - // moderator coord in lab frame - V3D srcLab(0.0, -detpar.l1, 0.0); - weightsPerScatter[0] *= moderatorPos(srcLab[0], srcLab[2]); -// if(heightLab > m_instgeom.sampleHeight || -// widthLab > m_instgeom.sampleWidth) return 0.0; // misses sample + // Initial TOF based on uncertainty in time measurement on detector + // moderator coord in lab frame + V3D srcPos; + generateSrcPos(detpar.l1, srcPos); // fills in x,y,z + // transform to sample frame + srcPos.rotate(*m_goniometer); + if(fabs(srcPos[0]) > m_halfSampleWidth || + fabs(srcPos[1]) > m_halfSampleHeight) return 0.0; // misses sample + + const double vel2 = sqrt(detpar.efixed/MASS_TO_MEV); + const double t2 = detpar.l2/vel2; + double en0 = generateE0(detpar.l1, t2, weights[0]); + tofs[0] = generateTOF(en0, detpar.t0, respar.dl1); // correction for resolution in l1 + + // Neutron paths. No beam divergence so initial direction is parallel to beam direction + V3D startPos(srcPos); + V3D particleDir; + particleDir[m_beamDir] = 1.0; + + V3D scatterPt = generateScatter(startPos, particleDir, weights[0]); + double distFromStart = scatterPt.distance(startPos); + // Compute TOF for first scatter event + double vel = sqrt(en0/MASS_TO_MEV); + tofs[0] += (distFromStart*1e6/vel); return weightSum; } - /** - * Sample a tof value from a gaussian distribution - * @param centre The value of the centra point of the distribution - * @param sigma Width of the distribution - * @return New variable distributed accordingly - */ - double CalculateMSVesuvio::initialTOF(const double centre, const double sigma) const - { - return m_randgen->gaussian(centre, sigma); - } - /** * Sample from the moderator assuming it can be seen * as a cylindrical ring with inner and outer radius - * @param widthPos [Out] Position in the width direction of the generated point - * @param heightPos [Out] Position in the height direction of the generated point + * @param l1 Src-sample distance (m) + * @param srcPos [Out] Position in the height direction of the generated point */ - double CalculateMSVesuvio::moderatorPos(double &widthPos, double &heightPos) const + double CalculateMSVesuvio::generateSrcPos(const double l1, + Kernel::V3D &srcPos) const { - double radius(-1.0); + double radius(-1.0), widthPos(0.0), heightPos(0.0); do { - widthPos = -m_instgeom.srcR2 + 2.0*m_instgeom.srcR2*m_randgen->flat(); - heightPos = -m_instgeom.srcR2 + 2.0*m_instgeom.srcR2*m_randgen->flat(); + widthPos = -m_srcR2 + 2.0*m_srcR2*m_randgen->flat(); + heightPos = -m_srcR2 + 2.0*m_srcR2*m_randgen->flat(); using std::sqrt; radius = sqrt(widthPos*widthPos + heightPos*heightPos); } - while(radius > m_instgeom.srcR2); - - if(radius > m_instgeom.srcR1) - return (m_instgeom.srcR2 - radius)/(m_instgeom.srcR2 - m_instgeom.srcR1); + while(radius > m_srcR2); + // assign to output + srcPos[m_acrossDir] = widthPos; + srcPos[m_upDir] = heightPos; + srcPos[m_beamDir] = -l1; + + if(radius > m_srcR1) + return (m_srcR2 - radius)/(m_srcR2 - m_srcR1); else return 1.0; // inside umbra unit weight } + /** + * Generate an incident energy based on a randomly-selected TOF value + * It is assigned a weight = (2.0*E0/(T-t2))/E0^0.9. + * @param l1 Distance from src to sample (metres) + * @param t2 Nominal time from sample to detector (seconds) + * @param weight [Out] Weight factor to modify for the generated energy value + * @return + */ + double CalculateMSVesuvio::generateE0(const double l1, const double t2, double &weight) const + { + const double tof = m_tmin + (m_tmax - m_tmin)*m_randgen->flat(); + const double t1 = (tof - t2); + const double vel0 = l1/t1; + const double en0 = MASS_TO_MEV*vel0*vel0; + + weight *= 2.0*weight/t1/pow(weight, 0.9); + weight *= 1e-4; // Reduce weight to ~1 + + return en0; + } + + /** + * Generate an initial tof from this distribution: + * 1-(0.5*X**2/T0**2+X/T0+1)*EXP(-X/T0), where x is the time and t0 + * is the src-sample time. + * @param dt0 Error in time resolution (us) + * @param en0 Value of the incident energy + * @param dl1 S.d of moderator to sample distance + * @return tof Guass TOF modified for asymmetric pulse + */ + double CalculateMSVesuvio::generateTOF(const double en0, const double dt0, + const double dl1) const + { + const double vel1 = sqrt(en0/MASS_TO_MEV); + const double dt1 = (dl1/vel1)*1e6; + const double xmin(0.0), xmax(15.0*dt1); + double dx = 0.5*(xmax - xmin); + // Generate a random y position in th distribution + const double yv = m_randgen->flat(); + + double xt(xmin); + double tof = m_randgen->gaussian(0.0, dt0); + while(true) + { + xt += dx; + //Y=1-(0.5*X**2/T0**2+X/T0+1)*EXP(-X/T0) + double y = 1.0 - (0.5*xt*xt/(dt1*dt1) + xt/dt1 + 1)*exp(-xt/dt1); + if(fabs(y - yv) < 1e-4) + { + tof += xt - 3*dt1; + break; + } + if(y > yv) + { + dx = -fabs(0.5*dx); + } + else + { + dx = fabs(0.5*dx); + } + } + return tof; + } + + /** + * Generate a scatter event and update the weight according to the + * amount the beam would be attenuted by the sample + * @param startPos Starting position + * @param direc Direction of travel for the neutron + * @param weight [InOut] Multiply the incoming weight by the attenuation factor + * @return The generated scattering point + */ + Kernel::V3D CalculateMSVesuvio::generateScatter(const Kernel::V3D &startPos, const Kernel::V3D &direc, + double &weight) const + { + Track particleTrack(startPos, direc); + if(m_sampleShape->interceptSurface(particleTrack) != 1) + { + throw std::runtime_error("CalculateMSVesuvio::calculateCounts - " + "Sample shape appears to have a hole in it?. Unable to continue"); + } + // Find distance inside object and compute probability of scattering + const auto & link = particleTrack.begin(); + double totalObjectDist = link->distInsideObject; + const double scatterProb = 1.0 - exp(-m_mu*totalObjectDist); + // Select a random point on the track that is the actual scatter point + // from the scattering probability distribution + const double dist = -log(1.0 - m_randgen->flat()*scatterProb)/m_mu; + // From start point advance in direction of travel by computed distance to find scatter point + // Track is defined as set of links and exit point of first link is entry to sample! + V3D scatterPt = link->entryPoint; + scatterPt += direc*dist; + // Update weight + weight *= scatterProb; + + return scatterPt; + } + } // namespace Algorithms } // namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/src/ConvertToYSpace.cpp b/Code/Mantid/Framework/CurveFitting/src/ConvertToYSpace.cpp index 0c615888a88a..687fcf100e59 100644 --- a/Code/Mantid/Framework/CurveFitting/src/ConvertToYSpace.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/ConvertToYSpace.cpp @@ -34,10 +34,10 @@ namespace Mantid //---------------------------------------------------------------------------------------------- /// Algorithm's name for identification. @see Algorithm::name - const std::string ConvertToYSpace::name() const { return "ConvertToYSpace";}; + const std::string ConvertToYSpace::name() const { return "ConvertToYSpace";} /// Algorithm's version for identification. @see Algorithm::version - int ConvertToYSpace::version() const { return 1;}; + int ConvertToYSpace::version() const { return 1;} /// Algorithm's category for identification. @see Algorithm::category const std::string ConvertToYSpace::category() const { return "Transforms\\Units";} @@ -75,6 +75,7 @@ namespace Mantid const auto & pmap = ws->constInstrumentParameters(); detpar.l1 = sample->getDistance(*source); detpar.l2 = det->getDistance(*sample); + detpar.pos = det->getPos(); detpar.theta = ws->detectorTwoTheta(det); detpar.t0 = ConvertToYSpace::getComponentParameter(det, pmap, "t0")*1e-6; // Convert to seconds detpar.efixed = ConvertToYSpace::getComponentParameter(det, pmap, "efixed"); diff --git a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h index 80f724597bba..6bdfef3266c5 100644 --- a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h +++ b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h @@ -4,6 +4,7 @@ #include #include "MantidCurveFitting/CalculateMSVesuvio.h" +#include "MantidGeometry/Instrument/Goniometer.h" #include "MantidTestHelpers/ComponentCreationHelper.h" #include "MantidTestHelpers/WorkspaceCreationHelper.h" @@ -29,7 +30,7 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite TS_ASSERT(alg.isInitialized()); } - void test_exec_with_flat_plate_sample() + void test_exec_with_flat_plate_sample_and_no_goniometer() { auto alg = createTestAlgorithm(createFlatPlateSampleWS()); @@ -37,6 +38,20 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite TS_ASSERT(alg->isExecuted()); } + void test_exec_with_flat_plate_sample_and_goniometer() + { + auto testWS = createFlatPlateSampleWS(); + Mantid::Geometry::Goniometer sampleRot; + // 45.0 deg rotation around Y + sampleRot.pushAxis("phi", 0.0, 1.0, 0.0, 45.0, + Mantid::Geometry::CW, Mantid::Geometry::angDegrees); + testWS->mutableRun().setGoniometer(sampleRot, false); + auto alg = createTestAlgorithm(testWS); + + TS_ASSERT_THROWS_NOTHING(alg->execute()); + TS_ASSERT(alg->isExecuted()); + } + // ------------------------ Failure Cases ----------------------------------------- void test_setting_input_workspace_not_in_tof_throws_invalid_argument() @@ -58,17 +73,6 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite TS_ASSERT_THROWS(alg.setProperty("InputWorkspace", testWS), std::invalid_argument); } - void test_input_workspace_and_sampleshape_not_cuboid_or_cylinder_throws_invalid_argument_on_execution() - { - auto testWS = createFlatPlateSampleWS(); - auto sampleShape = ComponentCreationHelper::createSphere(1, Mantid::Kernel::V3D(), - "sample-shape"); - testWS->mutableSample().setShape(*sampleShape); // overwrite shape - auto alg = createTestAlgorithm(testWS); - - TS_ASSERT_THROWS(alg->execute(), std::invalid_argument); - } - void test_setting_zero_or_negative_beam_radius_values_throws_invalid_argument() { CalculateMSVesuvio alg; @@ -119,7 +123,7 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite { auto testWS = createTestWorkspace(); // Sample shape - const double height(0.1), width(0.02), thick(0.005); + const double height(0.05), width(0.01), thick(0.0025); auto sampleShape = ComponentCreationHelper::createCuboid(width, height, thick); testWS->mutableSample().setShape(*sampleShape); @@ -127,7 +131,6 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite } - Mantid::API::MatrixWorkspace_sptr createTestWorkspace() { using namespace Mantid::Geometry; From 37b3a7e4ce66db6aa355f91a7364578d990c3e37 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Thu, 4 Sep 2014 16:56:28 +0100 Subject: [PATCH 07/37] Check point CalculateMSVesuvio Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 42 ++- .../CurveFitting/src/CalculateMSVesuvio.cpp | 249 ++++++++++++++---- .../test/CalculateMSVesuvioTest.h | 34 +++ 3 files changed, 271 insertions(+), 54 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index c8b03cdff0c7..370c389aa162 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -49,6 +49,30 @@ namespace Mantid class DLLExport CalculateMSVesuvio : public API::Algorithm { private: + + // Holds date on the compton scattering properties of an atom + struct ComptonNeutronAtom + { + ComptonNeutronAtom() + : mass(-1.0), sclength(-1.0), profile(-1.0) {} + double mass; // in amu + double sclength; // 4pi/xsec + double profile; // s.d of J(y) + }; + + // Holds data about sample as a whole. + struct SampleComptonProperties + { + SampleComptonProperties(const int nprops) + : atoms(nprops), density(-1.0), totalxsec(-1.0), + mu(-1.0) {} + + std::vector atoms; + double density; // g/cm^3 + double totalxsec; // total free-scattering cross section + double mu; // attenuation factor (1/m) + }; + // Produces random numbers with various probability distributions class RandomNumberGenerator { @@ -62,8 +86,10 @@ namespace Mantid double gaussian(const double mean, const double sigma); private: + RandomNumberGenerator(); boost::mt19937 m_generator; }; + // Stores counts for each scatter order // for a "run" of a given number of events struct Simulation @@ -75,6 +101,7 @@ namespace Mantid double weight; size_t nmscat; }; + // Accumulates and averages the results of each simulation struct SimulationAggregator { @@ -101,7 +128,7 @@ namespace Mantid void init(); void exec(); - void cacheInputGeometry(); + void cacheInputs(); void calculateMS(const size_t wsIndex, API::ISpectrum & totalsc, API::ISpectrum & multsc) const; Simulation simulate(const size_t nevents, const size_t nscatters, @@ -110,19 +137,22 @@ namespace Mantid double calculateCounts(const size_t nscatters, const DetectorParams & detpar, const ResolutionParams &respar, - Simulation & counts) const; + Simulation & simulation) const; // single-event helpers - double generateSrcPos(const double l, Kernel::V3D & srcPos) const; + Kernel::V3D generateSrcPos(const double l1) const; double generateE0(const double l1, const double t2, double &weight) const; double generateTOF(const double gaussTOF, const double en0, const double dl1) const; Kernel::V3D generateScatter(const Kernel::V3D &startPos, const Kernel::V3D &direc, double &weight) const; + std::pair calculateE1Range(const double theta, const double en0) const; + double partialDiffXSec(const double en0, const double en1, const double theta) const; // Member Variables RandomNumberGenerator *m_randgen; // random number generator - size_t m_acrossDir, m_upDir, m_beamDir; // indices of each direction + size_t m_acrossIdx, m_upIdx, m_beamIdx; // indices of each direction + Kernel::V3D m_beamDir; // Directional vector for beam double m_srcR1; // beam umbra radius (m) double m_srcR2; // beam penumbra radius (m) double m_halfSampleHeight; // half-height of sample (m) @@ -131,9 +161,7 @@ namespace Mantid double m_maxWidthSampleFrame; // Maximum width in sample frame (m) Kernel::DblMatrix const *m_goniometer; // sample rotation Geometry::Object const *m_sampleShape; // sample shape - Kernel::V3D m_samplePos; - - double m_mu; //attenuation xsec + SampleComptonProperties *m_sampleProps; // description of sample properties double m_tmin; // minimum tof value double m_tmax; // maximum tof value diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index ce1e1279a513..64fcfa94e4fd 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -12,6 +12,8 @@ #include "MantidGeometry/Instrument/ParameterMap.h" #include "MantidGeometry/Objects/Track.h" +#include "MantidKernel/ArrayLengthValidator.h" +#include "MantidKernel/ArrayProperty.h" #include "MantidKernel/BoundedValidator.h" #include "MantidKernel/CompositeValidator.h" #include "MantidKernel/MersenneTwister.h" @@ -121,10 +123,10 @@ namespace Mantid /// Constructor CalculateMSVesuvio::CalculateMSVesuvio() : Algorithm(), m_randgen(NULL), - m_acrossDir(0), m_upDir(1), m_beamDir(3), m_srcR1(0.0), m_srcR2(0.0), + m_acrossIdx(0), m_upIdx(1), m_beamIdx(3), m_beamDir(), m_srcR1(0.0), m_srcR2(0.0), m_halfSampleHeight(0.0), m_halfSampleWidth(0.0), m_halfSampleThick(0.0), - m_maxWidthSampleFrame(0.0), m_goniometer(NULL), m_sampleShape(NULL), m_samplePos(), - m_mu(-1.0), + m_maxWidthSampleFrame(0.0), m_goniometer(NULL), m_sampleShape(NULL), + m_sampleProps(NULL), m_tmin(-1.0), m_tmax(-1.0), m_dt(-1.0), m_progress(NULL), m_inputWS() { @@ -134,6 +136,8 @@ namespace Mantid CalculateMSVesuvio::~CalculateMSVesuvio() { delete m_randgen; + delete m_progress; + delete m_sampleProps; } /// @copydoc Algorithm::name @@ -174,9 +178,28 @@ namespace Mantid Direction::Input, inputWSValidator), "Input workspace to be corrected, in units of TOF."); + // -- Sample -- + auto positiveInt = boost::make_shared>(); + positiveInt->setLower(1); + declareProperty("NoOfMasses", -1, positiveInt, + "The number of masses contained within the sample"); + auto positiveNonZero = boost::make_shared>(); positiveNonZero->setLower(0.0); positiveNonZero->setLowerExclusive(true); + declareProperty("SampleDensity", -1.0, positiveNonZero, + "The density of the sample in gm/cm^3"); + + auto nonEmptyArray = boost::make_shared>(); + declareProperty(new ArrayProperty("AtomicProperties", nonEmptyArray), + "Atomic properties of masses within the sample. " + "The expected format is 3 consecutive values per mass: " + "mass(amu), cross-section (barns) & s.d of Compton profile."); + setPropertyGroup("NoOfMasses", "Sample"); + setPropertyGroup("SampleDensity", "Sample"); + setPropertyGroup("AtomicProperties", "Sample"); + + // -- Beam -- declareProperty("BeamUmbraRadius", -1.0, positiveNonZero, "Radius, in cm, of part in total shadow."); declareProperty("BeamPenumbraRadius", -1.0, positiveNonZero, @@ -184,8 +207,7 @@ namespace Mantid setPropertyGroup("BeamUmbraRadius", "Beam"); setPropertyGroup("BeamPenumbraRadius", "Beam"); - auto positiveInt = boost::make_shared>(); - positiveInt->setLower(1); + // -- Algorithm -- declareProperty("Seed", 123456789, positiveInt, "Seed the random number generator with this value"); @@ -202,12 +224,7 @@ namespace Mantid void CalculateMSVesuvio::exec() { m_inputWS = getProperty("InputWorkspace"); - cacheInputGeometry(); - // Values used frequently - const auto & inX = m_inputWS->readX(0); - m_tmin = inX.front(); - m_tmax = inX.back(); - m_dt = inX[1] - m_tmin; + cacheInputs(); // Create new workspaces MatrixWorkspace_sptr totalsc = WorkspaceFactory::Instance().create(m_inputWS); @@ -255,17 +272,18 @@ namespace Mantid } /** - * Caches sample, detector geometry for speed in later calculations + * Caches inputs insuitable form for speed in later calculations */ - void CalculateMSVesuvio::cacheInputGeometry() + void CalculateMSVesuvio::cacheInputs() { + // -- Geometry -- const auto instrument = m_inputWS->getInstrument(); - m_samplePos = instrument->getSample()->getPos(); + m_beamDir = instrument->getSample()->getPos() - instrument->getSource()->getPos(); const auto rframe = instrument->getReferenceFrame(); - m_acrossDir = rframe->pointingHorizontal(); - m_upDir = rframe->pointingUp(); - m_beamDir = rframe->pointingAlongBeam(); + m_acrossIdx = rframe->pointingHorizontal(); + m_upIdx = rframe->pointingUp(); + m_beamIdx = rframe->pointingAlongBeam(); m_srcR1 = getProperty("BeamUmbraRadius"); m_srcR2 = getProperty("BeamPenumbraRadius"); @@ -289,9 +307,48 @@ namespace Mantid Geometry::BoundingBox bounds = m_sampleShape->getBoundingBox(); V3D boxWidth = bounds.width(); // Use half-width/height for easier calculation later - m_halfSampleWidth = 0.5*boxWidth[m_acrossDir]; - m_halfSampleHeight = 0.5*boxWidth[m_upDir]; - m_halfSampleThick = 0.5*boxWidth[m_beamDir]; + m_halfSampleWidth = 0.5*boxWidth[m_acrossIdx]; + m_halfSampleHeight = 0.5*boxWidth[m_upIdx]; + m_halfSampleThick = 0.5*boxWidth[m_beamIdx]; + + // -- Workspace -- + const auto & inX = m_inputWS->readX(0); + m_tmin = inX.front(); + m_tmax = inX.back(); + m_dt = inX[1] - m_tmin; + + // -- Sample -- + int nmasses = getProperty("NoOfMasses"); + std::vector sampleInfo = getProperty("AtomicProperties"); + int nInputAtomProps = static_cast(sampleInfo.size()); + const int nExptdAtomProp(3); + if(nInputAtomProps != nExptdAtomProp*nmasses) + { + std::ostringstream os; + os << "Inconsistent AtomicProperties list defined. Expected " << nExptdAtomProp*nmasses + << " values, however, only " << sampleInfo.size() << " have been given."; + throw std::invalid_argument(os.str()); + } + m_sampleProps = new SampleComptonProperties(nInputAtomProps); + m_sampleProps->density = getProperty("SampleDensity"); + + double totalMass(0.0); + m_sampleProps->totalxsec = 0.0; + for(int i = 0; i < nInputAtomProps; ++i) + { + auto & comptonAtom = m_sampleProps->atoms[i]; + comptonAtom.mass = sampleInfo[nExptdAtomProp*i]; + totalMass += comptonAtom.mass; + + const double xsec = sampleInfo[nExptdAtomProp*i + 1]; + comptonAtom.sclength = sqrt(xsec/4.0*M_PI); + const double factor = 1.0 + (PhysicalConstants::NeutronMassAMU/comptonAtom.mass); + m_sampleProps->totalxsec += (xsec/(factor*factor)); + + comptonAtom.profile = sampleInfo[nExptdAtomProp*i + 2]; + } + const double numberDensity = m_sampleProps->density*1e6/totalMass; // formula units/m^3 + m_sampleProps->mu = numberDensity*m_sampleProps->totalxsec*1e-28; } /** @@ -356,22 +413,21 @@ namespace Mantid * @param nscatters Maximum order of scattering that should be simulated * @param detpar Detector information describing the final detector position * @param respar Resolution information on the intrument as a whole - * @param counts [Output] Store the calculated counts here + * @param simulation [Output] Store the calculated counts here * @return */ double CalculateMSVesuvio::calculateCounts(const size_t nscatters, const DetectorParams &detpar, const ResolutionParams &respar, - Simulation &counts) const + Simulation &simulation) const { double weightSum(0.0); - std::vector weights(nscatters, 1.0); // start at 1.0 - std::vector tofs(nscatters, 0.0); + std::vector weights(nscatters, 1.0), // start at 1.0 + tofs(nscatters, 0.0), scAngs(nscatters, 0.0),en0(nscatters, 0.0); // Initial TOF based on uncertainty in time measurement on detector // moderator coord in lab frame - V3D srcPos; - generateSrcPos(detpar.l1, srcPos); // fills in x,y,z + V3D srcPos = generateSrcPos(detpar.l1); // transform to sample frame srcPos.rotate(*m_goniometer); if(fabs(srcPos[0]) > m_halfSampleWidth || @@ -379,20 +435,47 @@ namespace Mantid const double vel2 = sqrt(detpar.efixed/MASS_TO_MEV); const double t2 = detpar.l2/vel2; - double en0 = generateE0(detpar.l1, t2, weights[0]); - tofs[0] = generateTOF(en0, detpar.t0, respar.dl1); // correction for resolution in l1 + en0[0] = generateE0(detpar.l1, t2, weights[0]); + tofs[0] = generateTOF(en0[0], detpar.t0, respar.dl1); // correction for resolution in l1 - // Neutron paths. No beam divergence so initial direction is parallel to beam direction - V3D startPos(srcPos); - V3D particleDir; - particleDir[m_beamDir] = 1.0; + // Neutron path + // Algorithm has initial direction pointing to origin + V3D particleDir = V3D() - srcPos; - V3D scatterPt = generateScatter(startPos, particleDir, weights[0]); - double distFromStart = scatterPt.distance(startPos); + // first scatter + V3D scatterPt = generateScatter(srcPos, particleDir, weights[0]); + double distFromStart = scatterPt.distance(srcPos); // Compute TOF for first scatter event - double vel = sqrt(en0/MASS_TO_MEV); + double vel = sqrt(en0[0]/MASS_TO_MEV); tofs[0] += (distFromStart*1e6/vel); + // multiple scatters + for(size_t i = 1; i < nscatters; ++i) + { + weights[i] = weights[i-1]; + tofs[i] = tofs[i-1]; + + // Generate a new direction of travel + double randth = acos(2.0*m_randgen->flat() - 1.0); + double randphi = 2.0*M_PI*m_randgen->flat(); + V3D oldDir = particleDir; + particleDir.spherical_rad(1.0, randth, randphi); + scAngs[i-1] = particleDir.angle(oldDir); + + // Update weight + scatterPt = generateScatter(scatterPt, particleDir, weights[i]); + auto e1range = calculateE1Range(scAngs[i-1], en0[i-1]); + en0[i] = e1range.first + m_randgen->flat()*(e1range.second - e1range.first); + const double d2sig =partialDiffXSec(en0[i-1], en0[i], scAngs[i-1]); + double weight = d2sig*4.0*M_PI*(e1range.second - e1range.first)/m_sampleProps->totalxsec; + // accumulate total weight + simulation.nmscat += 1; + weightSum += weight; + weights[i] *= weight; // account for this scatter on top of previous + + // Increment time of flight... + } + return weightSum; } @@ -400,10 +483,9 @@ namespace Mantid * Sample from the moderator assuming it can be seen * as a cylindrical ring with inner and outer radius * @param l1 Src-sample distance (m) - * @param srcPos [Out] Position in the height direction of the generated point + * @returns Position on the moderator of the generated point */ - double CalculateMSVesuvio::generateSrcPos(const double l1, - Kernel::V3D &srcPos) const + V3D CalculateMSVesuvio::generateSrcPos(const double l1) const { double radius(-1.0), widthPos(0.0), heightPos(0.0); do @@ -415,14 +497,12 @@ namespace Mantid } while(radius > m_srcR2); // assign to output - srcPos[m_acrossDir] = widthPos; - srcPos[m_upDir] = heightPos; - srcPos[m_beamDir] = -l1; + V3D srcPos; + srcPos[m_acrossIdx] = widthPos; + srcPos[m_upIdx] = heightPos; + srcPos[m_beamIdx] = -l1; - if(radius > m_srcR1) - return (m_srcR2 - radius)/(m_srcR2 - m_srcR1); - else - return 1.0; // inside umbra unit weight + return srcPos; } /** @@ -509,10 +589,10 @@ namespace Mantid // Find distance inside object and compute probability of scattering const auto & link = particleTrack.begin(); double totalObjectDist = link->distInsideObject; - const double scatterProb = 1.0 - exp(-m_mu*totalObjectDist); + const double scatterProb = 1.0 - exp(-m_sampleProps->mu*totalObjectDist); // Select a random point on the track that is the actual scatter point // from the scattering probability distribution - const double dist = -log(1.0 - m_randgen->flat()*scatterProb)/m_mu; + const double dist = -log(1.0 - m_randgen->flat()*scatterProb)/m_sampleProps->mu; // From start point advance in direction of travel by computed distance to find scatter point // Track is defined as set of links and exit point of first link is entry to sample! V3D scatterPt = link->entryPoint; @@ -523,5 +603,80 @@ namespace Mantid return scatterPt; } + /** + * @param theta Neutron scattering angle (radians) + * @param en0 Computed incident energy + * @return The range of allowed final energies for the neutron + */ + std::pair CalculateMSVesuvio::calculateE1Range(const double theta, const double en0) const + { + const double k0 = sqrt(en0/PhysicalConstants::E_mev_toNeutronWavenumberSq); + const double sth(sin(theta)), cth(cos(theta)); + + double e1min(1e10), e1max(-1e10); // large so that anything else is smaller + const auto & atoms = m_sampleProps->atoms; + for(size_t i = 0; i < atoms.size(); ++i) + { + const double mass = atoms[i].mass; + + const double fraction = (cth + sqrt(mass*mass - sth*sth))/(1.0 + mass); + const double k1 = fraction*k0; + const double en1 = PhysicalConstants::E_mev_toNeutronWavenumberSq*k1*k1; + const double qr = sqrt(k0*k0 + k1*k1 - 2.0*k0*k1*cth); + const double wr = en0 - en1; + const double width = PhysicalConstants::E_mev_toNeutronWavenumberSq*atoms[i].profile*qr/mass; + const double e1a = en0 - wr - 10.0*width; + const double e1b = en0 - wr + 10.0*width; + if(e1a < e1min) e1min = e1a; + if(e1b > e1min) e1max = e1b; + } + if(e1min < 0.0) e1min = 0.0; + return std::make_pair(e1min, e1max); + } + + /** + * Compute the partial differential cross section for this energy and theta. + * @param en0 Initial energy (meV) + * @param en1 Final energy (meV) + * @param theta Scattering angle + * @return Value of differential cross section + */ + double CalculateMSVesuvio::partialDiffXSec(const double en0, const double en1, const double theta) const + { + const double rt2pi = sqrt(2.0*M_PI); + + const double k0 = sqrt(en0/PhysicalConstants::E_mev_toNeutronWavenumberSq); + const double k1 = sqrt(en1/PhysicalConstants::E_mev_toNeutronWavenumberSq); + const double q = sqrt(k0*k0 + k1*k1 - 2.0*k0*k1*cos(theta)); + const double w = en0 - en1; + + double pdcs(0.0); + const auto & atoms = m_sampleProps->atoms; + if(q > 0.0) // avoid continuous checking in loop + { + for(size_t i = 0; i < atoms.size(); ++i) + { + const double jstddev = atoms[i].profile; + const double mass = atoms[i].mass; + const double y = 0.5*mass*w/(4.18036*q) - q; + const double jy = exp(-0.5*y*y/(jstddev*jstddev))/(jstddev*rt2pi); + const double sqw = mass*jy/(4.18036*q); + + const double sclength = atoms[i].sclength; + pdcs += sclength*sclength*(k1/k0)*sqw; + } + } + else + { + for(size_t i = 0; i < atoms.size(); ++i) + { + const double sclength = atoms[i].sclength; + pdcs += sclength*sclength; + } + } + + return pdcs; + } + } // namespace Algorithms } // namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h index 6bdfef3266c5..0d3dfabc2335 100644 --- a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h +++ b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h @@ -73,6 +73,36 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite TS_ASSERT_THROWS(alg.setProperty("InputWorkspace", testWS), std::invalid_argument); } + void test_setting_nmasses_zero_or_negative_throws_invalid_argument() + { + CalculateMSVesuvio alg; + alg.initialize(); + + TS_ASSERT_THROWS(alg.setProperty("NoOfMasses", -1), std::invalid_argument); + TS_ASSERT_THROWS(alg.setProperty("NoOfMasses", 0), std::invalid_argument); + } + + void test_setting_sampledensity_zero_or_negative_throws_invalid_argument() + { + CalculateMSVesuvio alg; + alg.initialize(); + + TS_ASSERT_THROWS(alg.setProperty("SampleDensity", -1), std::invalid_argument); + TS_ASSERT_THROWS(alg.setProperty("SampleDensity", 0), std::invalid_argument); + } + + + void test_setting_atomic_properties_not_length_three_times_nmasses_throws_invalid_argument_on_execute() + { + auto alg = createTestAlgorithm(createFlatPlateSampleWS()); + + alg->setProperty("NoOfMasses", 2); + const double sampleProps[5] = {1.007900, 0.9272392, 5.003738, 16.00000, 3.2587662E-02}; + alg->setProperty("AtomicProperties", std::vector(sampleProps, sampleProps + 5)); + + TS_ASSERT_THROWS(alg->execute(), std::invalid_argument); + } + void test_setting_zero_or_negative_beam_radius_values_throws_invalid_argument() { CalculateMSVesuvio alg; @@ -105,6 +135,10 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite alg->setChild(true); // inputs alg->setProperty("InputWorkspace", inputWS); + alg->setProperty("NoOfMasses", 3); + alg->setProperty("SampleDensity", 241.0); + const double sampleProps[9] = {1.007900, 0.9272392, 5.003738, 16.00000, 3.2587662E-02, 13.92299, 27.50000, 4.0172841E-02, 15.07701}; + alg->setProperty("AtomicProperties", std::vector(sampleProps, sampleProps + 9)); alg->setProperty("BeamUmbraRadius", 1.5); alg->setProperty("BeamPenumbraRadius", 2.5); // outputs From 5ba4dc5864d367a88c2deda927b3e96d32d81e9c Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Tue, 9 Sep 2014 17:35:26 +0100 Subject: [PATCH 08/37] Finished off algorithm. Needs further testing. Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 68 +- .../CurveFitting/src/CalculateMSVesuvio.cpp | 628 +++++++++++++++--- .../test/CalculateMSVesuvioTest.h | 59 +- .../test/ComptonProfileTestHelpers.h | 37 +- 4 files changed, 669 insertions(+), 123 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index 370c389aa162..d1d6dc322d75 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -101,28 +101,47 @@ namespace Mantid double weight; size_t nmscat; }; + // Stores counts for each scatter order with errors + struct SimulationWithErrors + { + SimulationWithErrors(const size_t order, const size_t ntimes) : + sim(order, ntimes), errors(order, std::vector(ntimes)) {} + + void normalise(); + Simulation sim; + std::vector> errors; + + }; - // Accumulates and averages the results of each simulation + // Accumulates and averages the results of a set of simulations struct SimulationAggregator { - SimulationAggregator(const size_t nruns, - const size_t order, - const size_t ntimes); - // Adds a result as part of the average - void add(const Simulation & result); - - Simulation averaged; - double prefactor; + SimulationAggregator(const size_t nruns); + + // Creates a placeholder for a new simulation + Simulation & newSimulation(const size_t order, + const size_t ntimes); + SimulationWithErrors average() const; + + std::vector results; }; public: CalculateMSVesuvio(); ~CalculateMSVesuvio(); - virtual const std::string name() const; - virtual int version() const; - virtual const std::string category() const; - virtual const std::string summary() const; + /// @copydoc Algorithm::name + virtual const std::string name() const { return "CalculateMSVesuvio"; } + /// @copydoc Algorithm::version + virtual int version() const { return 1; } + /// @copydoc Algorithm::category + virtual const std::string category() const { return "Corrections"; } + /// @copydoc Algorithm::summary + virtual const std::string summary() const + { + return "Corrects for the effects of multiple scattering " + "on a flat plate or cylindrical sample."; + } private: void init(); @@ -131,9 +150,10 @@ namespace Mantid void cacheInputs(); void calculateMS(const size_t wsIndex, API::ISpectrum & totalsc, API::ISpectrum & multsc) const; - Simulation simulate(const size_t nevents, const size_t nscatters, - const DetectorParams & detpar, - const ResolutionParams &respar) const; + void simulate(const size_t nevents, const size_t nscatters, + const DetectorParams & detpar, + const ResolutionParams &respar, + Simulation & simulCounts) const; double calculateCounts(const size_t nscatters, const DetectorParams & detpar, const ResolutionParams &respar, @@ -147,25 +167,23 @@ namespace Mantid double &weight) const; std::pair calculateE1Range(const double theta, const double en0) const; double partialDiffXSec(const double en0, const double en1, const double theta) const; + Kernel::V3D generateDetectorPos(const double l2, const double angle, const double energy) const; + double generateE1(const double angle, const double e1nom, const double e1res) const; // Member Variables RandomNumberGenerator *m_randgen; // random number generator size_t m_acrossIdx, m_upIdx, m_beamIdx; // indices of each direction Kernel::V3D m_beamDir; // Directional vector for beam - double m_srcR1; // beam umbra radius (m) - double m_srcR2; // beam penumbra radius (m) - double m_halfSampleHeight; // half-height of sample (m) - double m_halfSampleWidth; // half-width of sample (m) - double m_halfSampleThick; // half-thickness of sample(m) + double m_srcR1, m_srcR2; // beam umbra, penumbra radius (m) + double m_halfSampleHeight, m_halfSampleWidth, m_halfSampleThick; // (m) double m_maxWidthSampleFrame; // Maximum width in sample frame (m) Kernel::DblMatrix const *m_goniometer; // sample rotation Geometry::Object const *m_sampleShape; // sample shape SampleComptonProperties *m_sampleProps; // description of sample properties - - double m_tmin; // minimum tof value - double m_tmax; // maximum tof value - double m_dt; // tof value step + double m_detHeight, m_detWidth, m_detThick; // (m) + double m_tmin, m_tmax, m_delt; // min, max & dt TOF value + double m_foilRes; // resolution in energy of foil API::Progress *m_progress; API::MatrixWorkspace_sptr m_inputWS; diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 64fcfa94e4fd..b36b7d0f83b5 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -1,6 +1,6 @@ //----------------------------------------------------------------------------- // Includes -// +//----------------------------------------------------------------------------- #include "MantidCurveFitting/CalculateMSVesuvio.h" // Use helpers for storing detector/resolution parameters #include "MantidCurveFitting/ConvertToYSpace.h" @@ -17,6 +17,7 @@ #include "MantidKernel/BoundedValidator.h" #include "MantidKernel/CompositeValidator.h" #include "MantidKernel/MersenneTwister.h" +#include "MantidKernel/VectorHelper.h" #include @@ -39,6 +40,274 @@ namespace Mantid /// Conversion constant const double MASS_TO_MEV = 0.5*PhysicalConstants::NeutronMass/PhysicalConstants::meV; + /** + * Generate the final energy of a neutron for gold foil analyser at 293K + * in double-difference mode: + * - THIN FOIL NUMBER DENSITY = 1.456E20 ATOMS/SQ CM. + * - THICK FOIL NUMBER DENSITY = 3.0* 1.456E20 ATOMS/SQ CM. + * @param randv A random number between 0.0 & 1.0, sample from a flat distribution + * @return A value to use for the final energy + */ + double finalEnergyAuDD(const double randv) + { + // Tabulated values of absoprtion energies from S.F. Mughabghab, Neutron Cross Sections, Academic + // Press, Orlando, Florida, 1984. + const double ENERGIES[300] = {\ + 2000.0, 2020.7, 2041.5, 2062.2, 2082.9, 2103.7, 2124.4, 2145.2, 2165.9, 2186.6, 2207.4, 2228.1, + 2248.8, 2269.6, 2290.3, 2311.0, 2331.8, 2352.5, 2373.2, 2394.0, 2414.7, 2435.5, 2456.2, 2476.9, + 2497.7, 2518.4, 2539.1, 2559.9, 2580.6, 2601.3, 2622.1, 2642.8, 2663.5, 2684.3, 2705.0, 2725.8, + 2746.5, 2767.2, 2788.0, 2808.7, 2829.4, 2850.2, 2870.9, 2891.6, 2912.4, 2933.1, 2953.8, 2974.6, + 2995.3, 3016.1, 3036.8, 3057.5, 3078.3, 3099.0, 3119.7, 3140.5, 3161.2, 3181.9, 3202.7, 3223.4, + 3244.1, 3264.9, 3285.6, 3306.4, 3327.1, 3347.8, 3368.6, 3389.3, 3410.0, 3430.8, 3451.5, 3472.2, + 3493.0, 3513.7, 3534.4, 3555.2, 3575.9, 3596.7, 3617.4, 3638.1, 3658.9, 3679.6, 3700.3, 3721.1, + 3741.8, 3762.5, 3783.3, 3804.0, 3824.7, 3845.5, 3866.2, 3887.0, 3907.7, 3928.4, 3949.2, 3969.9, + 3990.6, 4011.4, 4032.1, 4052.8, 4073.6, 4094.3, 4115.1, 4135.8, 4156.5, 4177.3, 4198.0, 4218.7, + 4239.5, 4260.2, 4280.9, 4301.7, 4322.4, 4343.1, 4363.9, 4384.6, 4405.4, 4426.1, 4446.8, 4467.6, + 4488.3, 4509.0, 4529.8, 4550.5, 4571.2, 4592.0, 4612.7, 4633.4, 4654.2, 4674.9, 4695.7, 4716.4, + 4737.1, 4757.9, 4778.6, 4799.3, 4820.1, 4840.8, 4861.5, 4882.3, 4903.0, 4923.7, 4944.5, 4965.2, + 4986.0, 5006.7, 5027.4, 5048.2, 5068.9, 5089.6, 5110.4, 5131.1, 5151.8, 5172.6, 5193.3, 5214.0, + 5234.8, 5255.5, 5276.3, 5297.0, 5317.7, 5338.5, 5359.2, 5379.9, 5400.7, 5421.4, 5442.1, 5462.9, + 5483.6, 5504.3, 5525.1, 5545.8, 5566.6, 5587.3, 5608.0, 5628.8, 5649.5, 5670.2, 5691.0, 5711.7, + 5732.4, 5753.2, 5773.9, 5794.6, 5815.4, 5836.1, 5856.9, 5877.6, 5898.3, 5919.1, 5939.8, 5960.5, + 5981.3, 6002.0, 6022.7, 6043.5, 6064.2, 6085.0, 6105.7, 6126.4, 6147.2, 6167.9, 6188.6, 6209.4, + 6230.1, 6250.8, 6271.6, 6292.3, 6313.0, 6333.8, 6354.5, 6375.3, 6396.0, 6416.7, 6437.5, 6458.2, + 6478.9, 6499.7, 6520.4, 6541.1, 6561.9, 6582.6, 6603.3, 6624.1, 6644.8, 6665.6, 6686.3, 6707.0, + 6727.8, 6748.5, 6769.2, 6790.0, 6810.7, 6831.4, 6852.2, 6872.9, 6893.6, 6914.4, 6935.1, 6955.9, + 6976.6, 6997.3, 7018.1, 7038.8, 7059.5, 7080.3, 7101.0, 7121.7, 7142.5, 7163.2, 7183.9, 7204.7, + 7225.4, 7246.2, 7266.9, 7287.6, 7308.4, 7329.1, 7349.8, 7370.6, 7391.3, 7412.0, 7432.8, 7453.5, + 7474.2, 7495.0, 7515.7, 7536.5, 7557.2, 7577.9, 7598.7, 7619.4, 7640.1, 7660.9, 7681.6, 7702.3, + 7723.1, 7743.8, 7764.5, 7785.3, 7806.0, 7826.8, 7847.5, 7868.2, 7889.0, 7909.7, 7930.4, 7951.2, + 7971.9, 7992.6, 8013.4, 8034.1, 8054.8, 8075.6, 8096.3, 8117.1, 8137.8, 8158.5, 8179.3, 8200.0 + }; + + const double XVALUES[300] = {\ + 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, + 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, + 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00010, 0.00010, + 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, + 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, + 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, + 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, 0.00030, 0.00030, 0.00030, 0.00030, 0.00030, 0.00030, + 0.00030, 0.00030, 0.00040, 0.00040, 0.00040, 0.00040, 0.00040, 0.00050, 0.00050, 0.00050, 0.00050, 0.00060, + 0.00060, 0.00070, 0.00070, 0.00070, 0.00080, 0.00090, 0.00090, 0.00100, 0.00110, 0.00110, 0.00120, 0.00130, + 0.00150, 0.00160, 0.00170, 0.00190, 0.00210, 0.00230, 0.00260, 0.00290, 0.00320, 0.00360, 0.00410, 0.00470, + 0.00540, 0.00620, 0.00720, 0.00840, 0.00990, 0.01180, 0.01420, 0.01740, 0.02140, 0.02680, 0.03410, 0.04400, + 0.05770, 0.07680, 0.10360, 0.14050, 0.18960, 0.25110, 0.32310, 0.40240, 0.48540, 0.56870, 0.64930, 0.72370, + 0.78850, 0.84150, 0.88240, 0.91260, 0.93440, 0.95000, 0.96130, 0.96960, 0.97570, 0.98030, 0.98380, 0.98650, + 0.98870, 0.99040, 0.99180, 0.99290, 0.99380, 0.99460, 0.99520, 0.99580, 0.99620, 0.99660, 0.99700, 0.99730, + 0.99750, 0.99770, 0.99790, 0.99810, 0.99830, 0.99840, 0.99850, 0.99860, 0.99870, 0.99880, 0.99890, 0.99900, + 0.99900, 0.99910, 0.99910, 0.99920, 0.99920, 0.99930, 0.99930, 0.99940, 0.99940, 0.99940, 0.99940, 0.99950, + 0.99950, 0.99950, 0.99950, 0.99960, 0.99960, 0.99960, 0.99960, 0.99960, 0.99960, 0.99970, 0.99970, 0.99970, + 0.99970, 0.99970, 0.99970, 0.99970, 0.99970, 0.99970, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, + 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99990, 0.99990, + 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, + 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, + 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, + 0.99990, 0.99990, 0.99990, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, + 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, + 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000 + }; + + for(size_t i = 0; i < 299; ++i) + { + if(XVALUES[i] < randv && XVALUES[i+1] > randv) + { + double ef = ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); + if(ef < 100.0) ef = 0.0; + return ef; + } + } + return 0.0; + + + } + + /** + * Generate the final energy of a neutron for gold foil analyser at 293K + * with number density of 7.35E19 atoms/cm^2 in yap difference mode. + * @param randv A random number between 0.0 & 1.0, sample from a flat distribution + * @return A value to use for the final energy + */ + double finalEnergyAuYap(const double randv) + { + // Tabulated values of absoprtion energies from S.F. Mughabghab, Neutron Cross Sections, Academic + // Press, Orlando, Florida, 1984. + const double ENERGIES[600] = {\ + 4000.0, 4003.3, 4006.7, 4010.0, 4013.4, 4016.7, 4020.0, 4023.4, 4026.7, 4030.1, 4033.4, 4036.7, + 4040.1, 4043.4, 4046.7, 4050.1, 4053.4, 4056.8, 4060.1, 4063.4, 4066.8, 4070.1, 4073.5, 4076.8, + 4080.1, 4083.5, 4086.8, 4090.2, 4093.5, 4096.8, 4100.2, 4103.5, 4106.8, 4110.2, 4113.5, 4116.9, + 4120.2, 4123.5, 4126.9, 4130.2, 4133.6, 4136.9, 4140.2, 4143.6, 4146.9, 4150.3, 4153.6, 4156.9, + 4160.3, 4163.6, 4166.9, 4170.3, 4173.6, 4177.0, 4180.3, 4183.6, 4187.0, 4190.3, 4193.7, 4197.0, + 4200.3, 4203.7, 4207.0, 4210.4, 4213.7, 4217.0, 4220.4, 4223.7, 4227.0, 4230.4, 4233.7, 4237.1, + 4240.4, 4243.7, 4247.1, 4250.4, 4253.8, 4257.1, 4260.4, 4263.8, 4267.1, 4270.5, 4273.8, 4277.1, + 4280.5, 4283.8, 4287.1, 4290.5, 4293.8, 4297.2, 4300.5, 4303.8, 4307.2, 4310.5, 4313.9, 4317.2, + 4320.5, 4323.9, 4327.2, 4330.6, 4333.9, 4337.2, 4340.6, 4343.9, 4347.2, 4350.6, 4353.9, 4357.3, + 4360.6, 4363.9, 4367.3, 4370.6, 4374.0, 4377.3, 4380.6, 4384.0, 4387.3, 4390.7, 4394.0, 4397.3, + 4400.7, 4404.0, 4407.3, 4410.7, 4414.0, 4417.4, 4420.7, 4424.0, 4427.4, 4430.7, 4434.1, 4437.4, + 4440.7, 4444.1, 4447.4, 4450.8, 4454.1, 4457.4, 4460.8, 4464.1, 4467.4, 4470.8, 4474.1, 4477.5, + 4480.8, 4484.1, 4487.5, 4490.8, 4494.2, 4497.5, 4500.8, 4504.2, 4507.5, 4510.9, 4514.2, 4517.5, + 4520.9, 4524.2, 4527.5, 4530.9, 4534.2, 4537.6, 4540.9, 4544.2, 4547.6, 4550.9, 4554.3, 4557.6, + 4560.9, 4564.3, 4567.6, 4571.0, 4574.3, 4577.6, 4581.0, 4584.3, 4587.6, 4591.0, 4594.3, 4597.7, + 4601.0, 4604.3, 4607.7, 4611.0, 4614.4, 4617.7, 4621.0, 4624.4, 4627.7, 4631.1, 4634.4, 4637.7, + 4641.1, 4644.4, 4647.7, 4651.1, 4654.4, 4657.8, 4661.1, 4664.4, 4667.8, 4671.1, 4674.5, 4677.8, + 4681.1, 4684.5, 4687.8, 4691.2, 4694.5, 4697.8, 4701.2, 4704.5, 4707.8, 4711.2, 4714.5, 4717.9, + 4721.2, 4724.5, 4727.9, 4731.2, 4734.6, 4737.9, 4741.2, 4744.6, 4747.9, 4751.3, 4754.6, 4757.9, + 4761.3, 4764.6, 4767.9, 4771.3, 4774.6, 4778.0, 4781.3, 4784.6, 4788.0, 4791.3, 4794.7, 4798.0, + 4801.3, 4804.7, 4808.0, 4811.4, 4814.7, 4818.0, 4821.4, 4824.7, 4828.0, 4831.4, 4834.7, 4838.1, + 4841.4, 4844.7, 4848.1, 4851.4, 4854.8, 4858.1, 4861.4, 4864.8, 4868.1, 4871.5, 4874.8, 4878.1, + 4881.5, 4884.8, 4888.1, 4891.5, 4894.8, 4898.2, 4901.5, 4904.8, 4908.2, 4911.5, 4914.9, 4918.2, + 4921.5, 4924.9, 4928.2, 4931.6, 4934.9, 4938.2, 4941.6, 4944.9, 4948.2, 4951.6, 4954.9, 4958.3, + 4961.6, 4964.9, 4968.3, 4971.6, 4975.0, 4978.3, 4981.6, 4985.0, 4988.3, 4991.7, 4995.0, 4998.3, + 5001.7, 5005.0, 5008.3, 5011.7, 5015.0, 5018.4, 5021.7, 5025.0, 5028.4, 5031.7, 5035.1, 5038.4, + 5041.7, 5045.1, 5048.4, 5051.8, 5055.1, 5058.4, 5061.8, 5065.1, 5068.4, 5071.8, 5075.1, 5078.5, + 5081.8, 5085.1, 5088.5, 5091.8, 5095.2, 5098.5, 5101.8, 5105.2, 5108.5, 5111.9, 5115.2, 5118.5, + 5121.9, 5125.2, 5128.5, 5131.9, 5135.2, 5138.6, 5141.9, 5145.2, 5148.6, 5151.9, 5155.3, 5158.6, + 5161.9, 5165.3, 5168.6, 5172.0, 5175.3, 5178.6, 5182.0, 5185.3, 5188.6, 5192.0, 5195.3, 5198.7, + 5202.0, 5205.3, 5208.7, 5212.0, 5215.4, 5218.7, 5222.0, 5225.4, 5228.7, 5232.1, 5235.4, 5238.7, + 5242.1, 5245.4, 5248.7, 5252.1, 5255.4, 5258.8, 5262.1, 5265.4, 5268.8, 5272.1, 5275.5, 5278.8, + 5282.1, 5285.5, 5288.8, 5292.2, 5295.5, 5298.8, 5302.2, 5305.5, 5308.8, 5312.2, 5315.5, 5318.9, + 5322.2, 5325.5, 5328.9, 5332.2, 5335.6, 5338.9, 5342.2, 5345.6, 5348.9, 5352.3, 5355.6, 5358.9, + 5362.3, 5365.6, 5368.9, 5372.3, 5375.6, 5379.0, 5382.3, 5385.6, 5389.0, 5392.3, 5395.7, 5399.0, + 5402.3, 5405.7, 5409.0, 5412.4, 5415.7, 5419.0, 5422.4, 5425.7, 5429.0, 5432.4, 5435.7, 5439.1, + 5442.4, 5445.7, 5449.1, 5452.4, 5455.8, 5459.1, 5462.4, 5465.8, 5469.1, 5472.5, 5475.8, 5479.1, + 5482.5, 5485.8, 5489.1, 5492.5, 5495.8, 5499.2, 5502.5, 5505.8, 5509.2, 5512.5, 5515.9, 5519.2, + 5522.5, 5525.9, 5529.2, 5532.6, 5535.9, 5539.2, 5542.6, 5545.9, 5549.2, 5552.6, 5555.9, 5559.3, + 5562.6, 5565.9, 5569.3, 5572.6, 5576.0, 5579.3, 5582.6, 5586.0, 5589.3, 5592.7, 5596.0, 5599.3, + 5602.7, 5606.0, 5609.3, 5612.7, 5616.0, 5619.4, 5622.7, 5626.0, 5629.4, 5632.7, 5636.1, 5639.4, + 5642.7, 5646.1, 5649.4, 5652.8, 5656.1, 5659.4, 5662.8, 5666.1, 5669.4, 5672.8, 5676.1, 5679.5, + 5682.8, 5686.1, 5689.5, 5692.8, 5696.2, 5699.5, 5702.8, 5706.2, 5709.5, 5712.9, 5716.2, 5719.5, + 5722.9, 5726.2, 5729.5, 5732.9, 5736.2, 5739.6, 5742.9, 5746.2, 5749.6, 5752.9, 5756.3, 5759.6, + 5762.9, 5766.3, 5769.6, 5773.0, 5776.3, 5779.6, 5783.0, 5786.3, 5789.6, 5793.0, 5796.3, 5799.7, + 5803.0, 5806.3, 5809.7, 5813.0, 5816.4, 5819.7, 5823.0, 5826.4, 5829.7, 5833.1, 5836.4, 5839.7, + 5843.1, 5846.4, 5849.7, 5853.1, 5856.4, 5859.8, 5863.1, 5866.4, 5869.8, 5873.1, 5876.5, 5879.8, + 5883.1, 5886.5, 5889.8, 5893.2, 5896.5, 5899.8, 5903.2, 5906.5, 5909.8, 5913.2, 5916.5, 5919.9, + 5923.2, 5926.5, 5929.9, 5933.2, 5936.6, 5939.9, 5943.2, 5946.6, 5949.9, 5953.3, 5956.6, 5959.9, + 5963.3, 5966.6, 5970.0, 5973.3, 5976.6, 5980.0, 5983.3, 5986.6, 5990.0, 5993.3, 5996.7, 6000.0 + }; + + const double XVALUES[600] = {\ + 0.00000, 0.00000, 0.00000, 0.00002, 0.00003, 0.00003, 0.00004, 0.00005, 0.00005, 0.00006, 0.00007, 0.00007, + 0.00008, 0.00009, 0.00010, 0.00010, 0.00011, 0.00012, 0.00013, 0.00014, 0.00015, 0.00015, 0.00016, 0.00017, + 0.00018, 0.00019, 0.00020, 0.00021, 0.00022, 0.00023, 0.00024, 0.00025, 0.00026, 0.00027, 0.00028, 0.00029, + 0.00030, 0.00031, 0.00032, 0.00033, 0.00034, 0.00035, 0.00037, 0.00038, 0.00039, 0.00040, 0.00041, 0.00043, + 0.00044, 0.00045, 0.00047, 0.00048, 0.00049, 0.00051, 0.00052, 0.00054, 0.00055, 0.00057, 0.00058, 0.00060, + 0.00061, 0.00063, 0.00065, 0.00066, 0.00068, 0.00070, 0.00072, 0.00074, 0.00075, 0.00077, 0.00079, 0.00081, + 0.00083, 0.00085, 0.00087, 0.00089, 0.00092, 0.00094, 0.00096, 0.00098, 0.00101, 0.00103, 0.00106, 0.00108, + 0.00111, 0.00113, 0.00116, 0.00118, 0.00121, 0.00124, 0.00127, 0.00130, 0.00133, 0.00136, 0.00139, 0.00142, + 0.00146, 0.00149, 0.00152, 0.00156, 0.00159, 0.00163, 0.00167, 0.00171, 0.00174, 0.00178, 0.00182, 0.00187, + 0.00191, 0.00195, 0.00200, 0.00204, 0.00209, 0.00214, 0.00219, 0.00224, 0.00229, 0.00235, 0.00240, 0.00246, + 0.00251, 0.00257, 0.00263, 0.00269, 0.00276, 0.00282, 0.00289, 0.00296, 0.00303, 0.00310, 0.00318, 0.00325, + 0.00333, 0.00341, 0.00349, 0.00358, 0.00367, 0.00376, 0.00385, 0.00394, 0.00404, 0.00414, 0.00425, 0.00435, + 0.00446, 0.00458, 0.00469, 0.00481, 0.00494, 0.00507, 0.00520, 0.00533, 0.00548, 0.00562, 0.00577, 0.00592, + 0.00608, 0.00625, 0.00642, 0.00659, 0.00677, 0.00696, 0.00716, 0.00736, 0.00757, 0.00778, 0.00800, 0.00823, + 0.00847, 0.00872, 0.00898, 0.00924, 0.00952, 0.00980, 0.01010, 0.01041, 0.01073, 0.01106, 0.01141, 0.01177, + 0.01214, 0.01253, 0.01293, 0.01335, 0.01379, 0.01425, 0.01472, 0.01522, 0.01573, 0.01627, 0.01683, 0.01742, + 0.01803, 0.01867, 0.01934, 0.02004, 0.02077, 0.02154, 0.02234, 0.02317, 0.02405, 0.02497, 0.02594, 0.02695, + 0.02801, 0.02913, 0.03030, 0.03153, 0.03282, 0.03419, 0.03561, 0.03712, 0.03871, 0.04037, 0.04213, 0.04398, + 0.04594, 0.04799, 0.05017, 0.05246, 0.05488, 0.05743, 0.06013, 0.06297, 0.06598, 0.06915, 0.07251, 0.07605, + 0.07979, 0.08374, 0.08791, 0.09230, 0.09694, 0.10183, 0.10698, 0.11241, 0.11812, 0.12411, 0.13041, 0.13703, + 0.14395, 0.15119, 0.15877, 0.16667, 0.17490, 0.18347, 0.19237, 0.20159, 0.21114, 0.22100, 0.23117, 0.24164, + 0.25240, 0.26344, 0.27473, 0.28628, 0.29807, 0.31007, 0.32228, 0.33468, 0.34725, 0.35999, 0.37286, 0.38586, + 0.39898, 0.41219, 0.42549, 0.43886, 0.45228, 0.46575, 0.47925, 0.49277, 0.50628, 0.51980, 0.53329, 0.54674, + 0.56016, 0.57350, 0.58677, 0.59996, 0.61304, 0.62600, 0.63883, 0.65152, 0.66403, 0.67638, 0.68853, 0.70048, + 0.71220, 0.72369, 0.73492, 0.74590, 0.75659, 0.76700, 0.77711, 0.78691, 0.79640, 0.80557, 0.81442, 0.82294, + 0.83113, 0.83900, 0.84654, 0.85376, 0.86067, 0.86726, 0.87355, 0.87954, 0.88525, 0.89067, 0.89583, 0.90073, + 0.90537, 0.90979, 0.91398, 0.91794, 0.92170, 0.92527, 0.92865, 0.93185, 0.93489, 0.93776, 0.94049, 0.94307, + 0.94552, 0.94784, 0.95005, 0.95213, 0.95412, 0.95600, 0.95779, 0.95949, 0.96110, 0.96264, 0.96410, 0.96549, + 0.96681, 0.96807, 0.96927, 0.97041, 0.97150, 0.97254, 0.97353, 0.97447, 0.97538, 0.97624, 0.97706, 0.97785, + 0.97860, 0.97933, 0.98002, 0.98068, 0.98131, 0.98192, 0.98250, 0.98306, 0.98359, 0.98411, 0.98460, 0.98507, + 0.98553, 0.98596, 0.98638, 0.98679, 0.98718, 0.98755, 0.98791, 0.98826, 0.98859, 0.98892, 0.98923, 0.98953, + 0.98981, 0.99009, 0.99036, 0.99062, 0.99087, 0.99111, 0.99135, 0.99158, 0.99179, 0.99201, 0.99221, 0.99241, + 0.99260, 0.99279, 0.99296, 0.99314, 0.99331, 0.99347, 0.99363, 0.99378, 0.99393, 0.99408, 0.99422, 0.99435, + 0.99448, 0.99461, 0.99473, 0.99486, 0.99497, 0.99509, 0.99520, 0.99530, 0.99541, 0.99551, 0.99561, 0.99571, + 0.99580, 0.99589, 0.99598, 0.99607, 0.99615, 0.99623, 0.99631, 0.99639, 0.99647, 0.99654, 0.99661, 0.99668, + 0.99675, 0.99682, 0.99688, 0.99694, 0.99701, 0.99707, 0.99713, 0.99718, 0.99724, 0.99729, 0.99735, 0.99740, + 0.99745, 0.99750, 0.99755, 0.99760, 0.99764, 0.99769, 0.99773, 0.99778, 0.99782, 0.99786, 0.99790, 0.99794, + 0.99798, 0.99802, 0.99806, 0.99809, 0.99813, 0.99816, 0.99820, 0.99823, 0.99827, 0.99830, 0.99833, 0.99836, + 0.99839, 0.99842, 0.99845, 0.99848, 0.99850, 0.99853, 0.99856, 0.99859, 0.99861, 0.99864, 0.99866, 0.99869, + 0.99871, 0.99873, 0.99876, 0.99878, 0.99880, 0.99882, 0.99884, 0.99886, 0.99889, 0.99891, 0.99893, 0.99895, + 0.99896, 0.99898, 0.99900, 0.99902, 0.99904, 0.99906, 0.99907, 0.99909, 0.99911, 0.99912, 0.99914, 0.99915, + 0.99917, 0.99919, 0.99920, 0.99922, 0.99923, 0.99924, 0.99926, 0.99927, 0.99929, 0.99930, 0.99931, 0.99933, + 0.99934, 0.99935, 0.99936, 0.99938, 0.99939, 0.99940, 0.99941, 0.99942, 0.99943, 0.99944, 0.99946, 0.99947, + 0.99948, 0.99949, 0.99950, 0.99951, 0.99952, 0.99953, 0.99954, 0.99955, 0.99956, 0.99956, 0.99957, 0.99958, + 0.99959, 0.99960, 0.99961, 0.99962, 0.99963, 0.99963, 0.99964, 0.99965, 0.99966, 0.99967, 0.99967, 0.99968, + 0.99969, 0.99970, 0.99970, 0.99971, 0.99972, 0.99973, 0.99973, 0.99974, 0.99975, 0.99975, 0.99976, 0.99977, + 0.99977, 0.99978, 0.99979, 0.99979, 0.99980, 0.99980, 0.99981, 0.99982, 0.99982, 0.99983, 0.99983, 0.99984, + 0.99984, 0.99985, 0.99985, 0.99986, 0.99986, 0.99987, 0.99988, 0.99988, 0.99989, 0.99989, 0.99990, 0.99990, + 0.99990, 0.99991, 0.99991, 0.99992, 0.99992, 0.99993, 0.99993, 0.99994, 0.99994, 0.99994, 0.99995, 0.99995, + 0.99996, 0.99996, 0.99997, 0.99997, 0.99997, 0.99998, 0.99998, 0.99998, 0.99999, 0.99999, 1.00000, 1.00000 + }; + + for(size_t i = 0; i < 599; ++i) + { + if(XVALUES[i] < randv && XVALUES[i+1] > randv) + { + return ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); + } + } + return 0.0; + } + + /** + * Generate the final energy of a neutron for gold foil analyser at 293K + * with number density of 7.35E19 atoms/cm^2 in double-difference mode. + * @param randv A random number between 0.0 & 1.0, sample from a flat distribution + * @return A value to use for the final energy + */ + double finalEnergyUranium(const double randv) + { + const double ENERGIES[201] = {\ + 5959.0, 5967.7, 5976.4, 5985.1, 5993.8, 6002.5, 6011.2, 6019.9, 6028.6, 6037.3, 6046.0, 6054.8, + 6063.5, 6072.2, 6080.9, 6089.6, 6098.3, 6107.0, 6115.7, 6124.4, 6133.1, 6141.8, 6150.5, 6159.2, + 6167.9, 6176.6, 6185.3, 6194.0, 6202.7, 6211.4, 6220.1, 6228.9, 6237.6, 6246.3, 6255.0, 6263.7, + 6272.4, 6281.1, 6289.8, 6298.5, 6307.2, 6315.9, 6324.6, 6333.3, 6342.0, 6350.7, 6359.4, 6368.1, + 6376.8, 6385.5, 6394.3, 6403.0, 6411.7, 6420.4, 6429.1, 6437.8, 6446.5, 6455.2, 6463.9, 6472.6, + 6481.3, 6490.0, 6498.7, 6507.4, 6516.1, 6524.8, 6533.5, 6542.2, 6550.9, 6559.6, 6568.4, 6577.1, + 6585.8, 6594.5, 6603.2, 6611.9, 6620.6, 6629.3, 6638.0, 6646.7, 6655.4, 6664.1, 6672.8, 6681.5, + 6690.2, 6698.9, 6707.6, 6716.3, 6725.0, 6733.7, 6742.5, 6751.2, 6759.9, 6768.6, 6777.3, 6786.0, + 6794.7, 6803.4, 6812.1, 6820.8, 6829.5, 6838.2, 6846.9, 6855.6, 6864.3, 6873.0, 6881.7, 6890.4, + 6899.1, 6907.8, 6916.5, 6925.3, 6934.0, 6942.7, 6951.4, 6960.1, 6968.8, 6977.5, 6986.2, 6994.9, + 7003.6, 7012.3, 7021.0, 7029.7, 7038.4, 7047.1, 7055.8, 7064.5, 7073.2, 7081.9, 7090.6, 7099.4, + 7108.1, 7116.8, 7125.5, 7134.2, 7142.9, 7151.6, 7160.3, 7169.0, 7177.7, 7186.4, 7195.1, 7203.8, + 7212.5, 7221.2, 7229.9, 7238.6, 7247.3, 7256.0, 7264.8, 7273.5, 7282.2, 7290.9, 7299.6, 7308.3, + 7317.0, 7325.7, 7334.4, 7343.1, 7351.8, 7360.5, 7369.2, 7377.9, 7386.6, 7395.3, 7404.0, 7412.7, + 7421.4, 7430.1, 7438.9, 7447.6, 7456.3, 7465.0, 7473.7, 7482.4, 7491.1, 7499.8, 7508.5, 7517.2, + 7525.9, 7534.6, 7543.3, 7552.0, 7560.7, 7569.4, 7578.1, 7586.8, 7595.5, 7604.2, 7613.0, 7621.7, + 7630.4, 7639.1, 7647.8, 7656.5, 7665.2, 7673.9, 7682.6, 7691.3, 7700.0 + }; + + const double XVALUES[201] = {\ + 0.00000, 0.00000, 0.00000, 0.00020, 0.00030, 0.00040, 0.00050, 0.00060, 0.00070, 0.00080, 0.00090, 0.00110, + 0.00120, 0.00140, 0.00150, 0.00170, 0.00190, 0.00210, 0.00230, 0.00250, 0.00270, 0.00290, 0.00310, 0.00340, + 0.00360, 0.00390, 0.00410, 0.00440, 0.00470, 0.00500, 0.00530, 0.00560, 0.00590, 0.00620, 0.00650, 0.00690, + 0.00720, 0.00760, 0.00800, 0.00840, 0.00880, 0.00920, 0.00960, 0.01010, 0.01050, 0.01100, 0.01150, 0.01210, + 0.01270, 0.01330, 0.01390, 0.01460, 0.01530, 0.01610, 0.01690, 0.01780, 0.01870, 0.01970, 0.02090, 0.02210, + 0.02350, 0.02500, 0.02660, 0.02850, 0.03070, 0.03320, 0.03620, 0.03990, 0.04440, 0.05020, 0.05780, 0.06790, + 0.08120, 0.09880, 0.12150, 0.15020, 0.18520, 0.22640, 0.27340, 0.32510, 0.38050, 0.43830, 0.49720, 0.55580, + 0.61290, 0.66710, 0.71740, 0.76250, 0.80190, 0.83510, 0.86220, 0.88380, 0.90050, 0.91340, 0.92340, 0.93100, + 0.93710, 0.94200, 0.94600, 0.94940, 0.95230, 0.95490, 0.95710, 0.95920, 0.96100, 0.96270, 0.96430, 0.96580, + 0.96710, 0.96840, 0.96950, 0.97060, 0.97170, 0.97270, 0.97360, 0.97450, 0.97540, 0.97620, 0.97700, 0.97770, + 0.97840, 0.97910, 0.97980, 0.98040, 0.98100, 0.98160, 0.98220, 0.98280, 0.98330, 0.98390, 0.98440, 0.98490, + 0.98540, 0.98590, 0.98630, 0.98680, 0.98720, 0.98770, 0.98810, 0.98850, 0.98890, 0.98930, 0.98970, 0.99010, + 0.99050, 0.99090, 0.99130, 0.99160, 0.99200, 0.99230, 0.99270, 0.99300, 0.99330, 0.99360, 0.99400, 0.99430, + 0.99460, 0.99480, 0.99510, 0.99540, 0.99560, 0.99590, 0.99610, 0.99640, 0.99660, 0.99680, 0.99710, 0.99730, + 0.99750, 0.99770, 0.99780, 0.99800, 0.99820, 0.99840, 0.99850, 0.99870, 0.99880, 0.99890, 0.99910, 0.99920, + 0.99930, 0.99940, 0.99950, 0.99960, 0.99960, 0.99970, 0.99980, 0.99980, 0.99990, 0.99990, 0.99990, 1.00000, + 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000 + }; + + for(size_t i = 0; i < 299; ++i) + { + if(XVALUES[i] < randv && XVALUES[i+1] > randv) + { + return ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); + } + } + + return 0.0; + } + + } // end anonymous namespace //------------------------------------------------------------------------- @@ -68,7 +337,7 @@ namespace Mantid } //------------------------------------------------------------------------- - // Simulation helpers + // Simulation helper //------------------------------------------------------------------------- /** * Stores counts for each scatter order @@ -79,40 +348,107 @@ namespace Mantid counts(order, std::vector(ntimes)), maxorder(order), weight(0.0), nmscat(0) {} - + //------------------------------------------------------------------------- + // SimulationAggreator + //------------------------------------------------------------------------- /** * Accumulates and averages the results * of each simulation + * @param nruns The number of runs that will be computed */ CalculateMSVesuvio::SimulationAggregator:: - SimulationAggregator(const size_t nruns, - const size_t order, - const size_t ntimes) : - averaged(order, ntimes), - prefactor(1.0/static_cast(nruns)) - {} - - /** Adds a result as part of the average - * @param result A new simulation result + SimulationAggregator(const size_t nruns) + { + results.reserve(nruns); + } + + /** + * @param order The number of requested scatterings + * @param ntimes The number of times on input workspace + * @return A reference to a new Simulation object + */ + CalculateMSVesuvio::Simulation & + CalculateMSVesuvio::SimulationAggregator:: + newSimulation(const size_t order, const size_t ntimes) + { + results.push_back(Simulation(order, ntimes)); + return results.back(); + } + + /** + * @return The mean and standard deviation of the current set of simulations */ - void CalculateMSVesuvio::SimulationAggregator:: - add(const Simulation & result) + CalculateMSVesuvio::SimulationWithErrors + CalculateMSVesuvio::SimulationAggregator::average() const { - // No check is performed whether the number of - // stated runs has been reached or the order is the same - for(size_t i = 0; i < averaged.maxorder; ++i) + const size_t maxorder(results[0].maxorder), ntimes(results[0].counts[0].size()), + nruns(results.size()); + SimulationWithErrors retval(maxorder, ntimes); + + for(size_t i = 0; i < maxorder; ++i) { - auto & avgcounts = averaged.counts[i]; - const auto & rescounts = result.counts[i]; - std::vector::iterator avgIter(avgcounts.begin()); - std::vector::const_iterator resIter(rescounts.begin()); - for(; avgIter != avgcounts.end(); ++avgIter, ++resIter) + auto & orderCounts = retval.sim.counts[i]; + auto & orderErrors = retval.errors[i]; + for(size_t j = 0; j < ntimes; ++j) { - *avgIter += prefactor*(*resIter); + double mean(0.0); + size_t npoints(0); + for(size_t k = 0; k < nruns; ++k) + { + const double val = results[k].counts[i][j]; + if(val > 0.0) + { + mean += val; + npoints +=1; + } + } + const double dblPts = static_cast(npoints); + orderCounts[j] = mean/dblPts; + // error is std dev + double sumsq(0.0); + for(size_t k = 0; k < nruns; ++k) + { + const double val = results[k].counts[i][j]; + if(val > 0.0) + { + const double diff = (val - mean); + sumsq += diff*diff; + } + } + orderErrors[j] = sqrt(sumsq/(dblPts*(dblPts-1))); + } + } + + return retval; + } + //------------------------------------------------------------------------- + // SimulationWithErrors + //------------------------------------------------------------------------- + /** + * Normalise the counts so that the integral over the single-scatter + * events is 1. + */ + void CalculateMSVesuvio::SimulationWithErrors::normalise() + { + const double sumSingle = std::accumulate(sim.counts.front().begin(), + sim.counts.front().end(), 0.0); + const double invSum = 1.0/sumSingle; // multiply is faster + // Divide everything by the sum + const size_t nscatters = sim.counts.size(); + for(size_t i = 0; i < nscatters; ++i) + { + auto & counts = sim.counts[i]; + auto & scerrors = this->errors[i]; + for(auto cit = counts.begin(), eit = scerrors.begin(); cit != counts.end(); + ++cit, ++eit) + { + (*cit) *= invSum; + (*eit) *= invSum; } } } + //------------------------------------------------------------------------- // Algorithm definitions //------------------------------------------------------------------------- @@ -127,7 +463,8 @@ namespace Mantid m_halfSampleHeight(0.0), m_halfSampleWidth(0.0), m_halfSampleThick(0.0), m_maxWidthSampleFrame(0.0), m_goniometer(NULL), m_sampleShape(NULL), m_sampleProps(NULL), - m_tmin(-1.0), m_tmax(-1.0), m_dt(-1.0), + m_detHeight(-1.0), m_detWidth(-1.0), m_detThick(-1.0), + m_tmin(-1.0), m_tmax(-1.0), m_delt(-1.0), m_foilRes(-1.0), m_progress(NULL), m_inputWS() { } @@ -140,31 +477,6 @@ namespace Mantid delete m_sampleProps; } - /// @copydoc Algorithm::name - const std::string CalculateMSVesuvio::name() const - { - return "CalculateMSVesuvio"; - } - - /// @copydoc Algorithm::version - int CalculateMSVesuvio::version() const - { - return 1; - } - - /// @copydoc Algorithm::category - const std::string CalculateMSVesuvio::category() const - { - return "Corrections"; - } - - /// @copydoc Algorithm::summary - const std::string CalculateMSVesuvio::summary() const - { - return "Corrects for the effects of multiple scattering " - "on a flat plate or cylindrical sample."; - } - /** * Initialize the algorithm's properties. */ @@ -191,6 +503,7 @@ namespace Mantid "The density of the sample in gm/cm^3"); auto nonEmptyArray = boost::make_shared>(); + nonEmptyArray->setLengthMin(3); declareProperty(new ArrayProperty("AtomicProperties", nonEmptyArray), "Atomic properties of masses within the sample. " "The expected format is 3 consecutive values per mass: " @@ -315,12 +628,12 @@ namespace Mantid const auto & inX = m_inputWS->readX(0); m_tmin = inX.front(); m_tmax = inX.back(); - m_dt = inX[1] - m_tmin; + m_delt = inX[1] - m_tmin; // -- Sample -- int nmasses = getProperty("NoOfMasses"); std::vector sampleInfo = getProperty("AtomicProperties"); - int nInputAtomProps = static_cast(sampleInfo.size()); + const int nInputAtomProps = static_cast(sampleInfo.size()); const int nExptdAtomProp(3); if(nInputAtomProps != nExptdAtomProp*nmasses) { @@ -329,12 +642,13 @@ namespace Mantid << " values, however, only " << sampleInfo.size() << " have been given."; throw std::invalid_argument(os.str()); } - m_sampleProps = new SampleComptonProperties(nInputAtomProps); + const int natoms = nInputAtomProps/3; + m_sampleProps = new SampleComptonProperties(natoms); m_sampleProps->density = getProperty("SampleDensity"); double totalMass(0.0); m_sampleProps->totalxsec = 0.0; - for(int i = 0; i < nInputAtomProps; ++i) + for(int i = 0; i < natoms; ++i) { auto & comptonAtom = m_sampleProps->atoms[i]; comptonAtom.mass = sampleInfo[nExptdAtomProp*i]; @@ -349,6 +663,45 @@ namespace Mantid } const double numberDensity = m_sampleProps->density*1e6/totalMass; // formula units/m^3 m_sampleProps->mu = numberDensity*m_sampleProps->totalxsec*1e-28; + + // -- Detector geometry -- choose first detector that is not a monitor + Geometry::IDetector_const_sptr detPixel; + for(size_t i = 0; i < m_inputWS->getNumberHistograms(); ++i) + { + try + { + detPixel = m_inputWS->getDetector(i); + } + catch(Exception::NotFoundError &) + { + continue; + } + if(!detPixel->isMonitor()) break; + } + // Bounding box in detector frame + auto pixelShape = detPixel->shape(); + if(!pixelShape) + { + throw std::invalid_argument("Detector pixel has no defined shape!"); + } + Geometry::BoundingBox detBounds = pixelShape->getBoundingBox(); + V3D detBoxWidth = detBounds.width(); + m_detWidth = detBoxWidth[m_acrossIdx]; + m_detHeight = detBoxWidth[m_upIdx]; + m_detThick = detBoxWidth[m_beamIdx]; + + // Foil resolution + auto foil = instrument->getComponentByName("foil-pos0"); + if(!foil) + { + throw std::runtime_error("Workspace has no gold foil component defined."); + } + auto param = m_inputWS->instrumentParameters().get(foil.get(), "hwhm_lorentz"); + if(!param) + { + throw std::runtime_error("Foil component has no hwhm_lorentz parameter defined."); + } + m_foilRes = param->value(); } /** @@ -377,12 +730,40 @@ namespace Mantid respar.dEnGauss = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_gauss"); // Final counts averaged over all simulations - SimulationAggregator avgCounts(NSIMULATIONS, NSCATTERS, m_inputWS->blocksize()); - for(size_t i = 0; i < NSIMULATIONS; ++i) + const size_t nruns(NSIMULATIONS), nscatters(NSCATTERS), nevents(NEVENTS); + SimulationAggregator accumulator(nruns); + for(size_t i = 0; i < nruns; ++i) { - avgCounts.add(simulate(NEVENTS, NSCATTERS, detpar, respar)); + simulate(nevents, nscatters, detpar, respar, + accumulator.newSimulation(nscatters, m_inputWS->blocksize())); } + SimulationWithErrors avgCounts = accumulator.average(); + avgCounts.normalise(); + + // assign to output spectrum + auto & msscatY = multsc.dataY(); + auto & msscatE = multsc.dataE(); + for(size_t i = 0; i < nscatters; ++i) + { + const auto & counts = avgCounts.sim.counts[i]; + // equivalent to msscatY[j] += counts[j] + std::transform(counts.begin(), counts.end(), msscatY.begin(), msscatY.begin(), + std::plus()); + const auto & scerrors = avgCounts.errors[i]; + // sum errors in quadrature + std::transform(scerrors.begin(), scerrors.end(), msscatE.begin(), msscatE.begin(), + VectorHelper::SumGaussError()); + } + // for total scattering add on single-scatter events + auto & totalscY = totalsc.dataY(); + auto & totalscE = totalsc.dataE(); + const auto & counts0 = avgCounts.sim.counts.front(); + std::transform(counts0.begin(), counts0.end(), msscatY.begin(), totalscY.begin(), + std::plus()); + const auto & errors0 = avgCounts.errors.front(); + std::transform(errors0.begin(), errors0.end(), msscatE.begin(), totalscE.begin(), + VectorHelper::SumGaussError()); } /** @@ -392,20 +773,17 @@ namespace Mantid * @param nscatters Maximum order of scattering that should be simulated * @param detpar Detector information describing the final detector position * @param respar Resolution information on the intrument as a whole - * @return A new simulation object storing the calculated number of counts + * @param simulCounts Simulation object used to storing the calculated number of counts */ - CalculateMSVesuvio::Simulation - CalculateMSVesuvio::simulate(const size_t nevents, const size_t nscatters, - const DetectorParams & detpar, - const ResolutionParams &respar) const + void CalculateMSVesuvio::simulate(const size_t nevents, const size_t nscatters, + const DetectorParams & detpar, + const ResolutionParams &respar, + Simulation & simulCounts) const { - Simulation simulCounts(NSCATTERS, m_inputWS->blocksize()); for(size_t i = 0; i < nevents; ++i) { simulCounts.weight += calculateCounts(nscatters, detpar, respar, simulCounts); } - - return simulCounts; } /** @@ -422,7 +800,11 @@ namespace Mantid { double weightSum(0.0); std::vector weights(nscatters, 1.0), // start at 1.0 - tofs(nscatters, 0.0), scAngs(nscatters, 0.0),en0(nscatters, 0.0); + tofs(nscatters, 0.0), + scAngs(nscatters, 0.0), // scattering angles between each order + en1(nscatters, 0.0); + std::vector directions(nscatters), // directions after each scatter + scatterPts(nscatters); // origin of each scatter // Initial TOF based on uncertainty in time measurement on detector @@ -435,21 +817,23 @@ namespace Mantid const double vel2 = sqrt(detpar.efixed/MASS_TO_MEV); const double t2 = detpar.l2/vel2; - en0[0] = generateE0(detpar.l1, t2, weights[0]); - tofs[0] = generateTOF(en0[0], detpar.t0, respar.dl1); // correction for resolution in l1 + en1[0] = generateE0(detpar.l1, t2, weights[0]); + tofs[0] = generateTOF(en1[0], detpar.t0, respar.dl1); // correction for resolution in l1 // Neutron path // Algorithm has initial direction pointing to origin V3D particleDir = V3D() - srcPos; + directions[0] = particleDir; // first scatter - V3D scatterPt = generateScatter(srcPos, particleDir, weights[0]); - double distFromStart = scatterPt.distance(srcPos); + V3D startPos(srcPos); + scatterPts[0] = generateScatter(startPos, particleDir, weights[0]); + double distFromStart = startPos.distance(scatterPts[0]); // Compute TOF for first scatter event - double vel = sqrt(en0[0]/MASS_TO_MEV); + double vel = sqrt(en1[0]/MASS_TO_MEV); tofs[0] += (distFromStart*1e6/vel); - // multiple scatters + // multiple scatter events within sample, i.e not including zeroth for(size_t i = 1; i < nscatters; ++i) { weights[i] = weights[i-1]; @@ -460,13 +844,14 @@ namespace Mantid double randphi = 2.0*M_PI*m_randgen->flat(); V3D oldDir = particleDir; particleDir.spherical_rad(1.0, randth, randphi); + directions[i] = particleDir; scAngs[i-1] = particleDir.angle(oldDir); // Update weight - scatterPt = generateScatter(scatterPt, particleDir, weights[i]); - auto e1range = calculateE1Range(scAngs[i-1], en0[i-1]); - en0[i] = e1range.first + m_randgen->flat()*(e1range.second - e1range.first); - const double d2sig =partialDiffXSec(en0[i-1], en0[i], scAngs[i-1]); + scatterPts[i] = generateScatter(scatterPts[i-1], particleDir, weights[i]); + auto e1range = calculateE1Range(scAngs[i-1], en1[i-1]); + en1[i] = e1range.first + m_randgen->flat()*(e1range.second - e1range.first); + const double d2sig = partialDiffXSec(en1[i-1], en1[i], scAngs[i-1]); double weight = d2sig*4.0*M_PI*(e1range.second - e1range.first)/m_sampleProps->totalxsec; // accumulate total weight simulation.nmscat += 1; @@ -474,6 +859,50 @@ namespace Mantid weights[i] *= weight; // account for this scatter on top of previous // Increment time of flight... + const double distTravelled = scatterPts[i].distance(scatterPts[i-1]); + double vel = sqrt(en1[i]/MASS_TO_MEV); + tofs[i] += (distTravelled*1e6/vel); + } + + // force all orders in to current detector + const auto & inX = m_inputWS->readX(0); + for(size_t i = 0; i < nscatters; ++i) + { + V3D detPos = generateDetectorPos(detpar.l1, detpar.theta, en1[i]); + // transform to sample frame + detPos.rotate(*m_goniometer); + // Distance to exit the sample for this order + Geometry::Track scatterToDet(scatterPts[i], detPos - scatterPts[i]); + if(m_sampleShape->interceptSurface(scatterToDet) == 0) + { + throw std::logic_error("CalculateMSVesuvio::calculateCounts() - " + "Logical error. No intersection with sample, despite track " + "originating from with sample."); + } + const auto & link = scatterToDet.begin(); + double distToExit = link->distInsideObject; + // Weight by probability neutron leaves sample + weights[i] *= exp(-m_sampleProps->mu*distToExit); + + // Weight by cross-section for the final energy + const double efinal = generateE1(detpar.theta, detpar.efixed, m_foilRes); + weights[i] *= partialDiffXSec(en1[i], efinal, scAngs[i])/m_sampleProps->totalxsec; + + // final TOF + double vel = sqrt(efinal/MASS_TO_MEV); + tofs[i] += detpar.t0 + (scatterPts[i].distance(detPos)*1e6)/vel; + + // "Bin" weight into appropriate place + std::vector &counts = simulation.counts[i]; + const double finalTOF = tofs[i]; + auto uppIter = std::upper_bound(inX.begin(), inX.end(), finalTOF); + if(uppIter != inX.begin()) + { + // See which side of line between us and previous value it should fall + auto prevIter = uppIter - 1; + if(finalTOF < *uppIter - 0.5*(*uppIter - *prevIter)) --uppIter; + } + counts[std::distance(inX.begin(), uppIter)] += weights[i]; } return weightSum; @@ -678,5 +1107,54 @@ namespace Mantid return pdcs; } + /** + * Generate a random position within the final detector in the lab frame + * @param l2 The nominal distance from sample to detector + * @param angle The The scattering angle from the sample + * @param energy The final energy of the neutron + * @return A new position in the detector + */ + V3D CalculateMSVesuvio::generateDetectorPos(const double l2, const double angle, const double energy) const + { + const double mu = 7430.0/sqrt(energy); // Inverse attenuation length (m-1) for vesuvio det. + const double ps = 1.0 - exp(-mu*m_detThick); // Probability of detection in path length YD. + + const double width = -0.5*m_detWidth + m_detWidth*m_randgen->flat(); + const double beam = -log(1.0 - m_randgen->flat()*ps)/mu; + const double height = -0.5*m_detHeight + m_detHeight*m_randgen->flat(); + const double widthLab = (l2 + beam)*sin(angle) + width*cos(angle); + const double beamLab = (l2 + beam)*cos(angle) - width*sin(angle); + V3D detPos; + detPos[m_beamIdx] = beamLab; + detPos[m_acrossIdx] = widthLab; + detPos[m_upIdx] = height; + + return detPos; + } + + /** + * Generate the final energy of the analyser + * @param angle Detector angle from sample + * @param e1nom The nominal final energy of the analyzer + * @param e1res The resoltion in energy of the analyser + * @return A value for the final energy of the neutron + */ + double CalculateMSVesuvio::generateE1(const double angle, const double e1nom, + const double e1res) const + { + if(e1res == 0.0) return e1nom; + + const double randv = m_randgen->flat(); + if(e1nom < 5000.0) + { + if(angle > 90.0) return finalEnergyAuDD(randv); + else return finalEnergyAuYap(randv); + } + else + { + return finalEnergyUranium(randv); + } + } + } // namespace Algorithms } // namespace Mantid diff --git a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h index 0d3dfabc2335..bca1678e785d 100644 --- a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h +++ b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h @@ -5,6 +5,7 @@ #include "MantidCurveFitting/CalculateMSVesuvio.h" #include "MantidGeometry/Instrument/Goniometer.h" +#include "MantidGeometry/Objects/ShapeFactory.h" #include "MantidTestHelpers/ComponentCreationHelper.h" #include "MantidTestHelpers/WorkspaceCreationHelper.h" @@ -34,6 +35,8 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite { auto alg = createTestAlgorithm(createFlatPlateSampleWS()); + feenableexcept(FE_INVALID | FE_OVERFLOW); + TS_ASSERT_THROWS_NOTHING(alg->execute()); TS_ASSERT(alg->isExecuted()); } @@ -91,7 +94,6 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite TS_ASSERT_THROWS(alg.setProperty("SampleDensity", 0), std::invalid_argument); } - void test_setting_atomic_properties_not_length_three_times_nmasses_throws_invalid_argument_on_execute() { auto alg = createTestAlgorithm(createFlatPlateSampleWS()); @@ -125,6 +127,13 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite TS_ASSERT_THROWS(alg->execute(), std::invalid_argument); } + void test_input_workspace_with_detector_that_has_no_shape_throws_exception() + { + auto alg = createTestAlgorithm(createFlatPlateSampleWS()); + + TS_ASSERT_THROWS(alg->execute(), std::invalid_argument); + } + private: Mantid::API::IAlgorithm_sptr createTestAlgorithm(const Mantid::API::MatrixWorkspace_sptr & inputWS) @@ -137,7 +146,8 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite alg->setProperty("InputWorkspace", inputWS); alg->setProperty("NoOfMasses", 3); alg->setProperty("SampleDensity", 241.0); - const double sampleProps[9] = {1.007900, 0.9272392, 5.003738, 16.00000, 3.2587662E-02, 13.92299, 27.50000, 4.0172841E-02, 15.07701}; + const double sampleProps[9] = {1.007900, 0.9272392, 5.003738, 16.00000, 3.2587662E-02, 13.92299, + 27.50000, 4.0172841E-02, 15.07701}; alg->setProperty("AtomicProperties", std::vector(sampleProps, sampleProps + 9)); alg->setProperty("BeamUmbraRadius", 1.5); alg->setProperty("BeamPenumbraRadius", 2.5); @@ -148,32 +158,53 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite return alg; } - struct ones - { - double operator()(const double, size_t) { return 1.0; } // don't care about Y values, just use 1.0 everywhere - }; - - Mantid::API::MatrixWorkspace_sptr createFlatPlateSampleWS() + Mantid::API::MatrixWorkspace_sptr createFlatPlateSampleWS(const bool detShape = true) { - auto testWS = createTestWorkspace(); + auto testWS = createTestWorkspace(detShape); // Sample shape - const double height(0.05), width(0.01), thick(0.0025); - auto sampleShape = ComponentCreationHelper::createCuboid(width, height, thick); + const double halfHeight(0.05), halfWidth(0.05), halfThick(0.0025); + std::ostringstream sampleShapeXML; + sampleShapeXML << " " + << " " + << " " + << " " + << " " + << ""; + auto sampleShape = Mantid::Geometry::ShapeFactory().createShape(sampleShapeXML.str()); testWS->mutableSample().setShape(*sampleShape); return testWS; } - Mantid::API::MatrixWorkspace_sptr createTestWorkspace() + Mantid::API::MatrixWorkspace_sptr createTestWorkspace(const bool detShape = true) { using namespace Mantid::Geometry; using namespace Mantid::Kernel; const int nhist(1); const double x0(50.0), x1(562.0), dx(1.0); - const bool singleMassSpec(false), foilChanger(false); - auto ws2d = ComptonProfileTestHelpers::createTestWorkspace(nhist, x0, x1, dx, singleMassSpec, foilChanger); + const bool singleMassSpec(false), foilChanger(true); + auto ws2d = ComptonProfileTestHelpers::createTestWorkspace(nhist, x0, x1, dx, + singleMassSpec, foilChanger); + + if(detShape) + { + // replace instrument with one that has a detector with a shape + const std::string shapeXML = \ + "" + "" + "" + "" + "" + "" + ""; + const auto pos = ws2d->getDetector(0)->getPos(); + auto instrument = ComptonProfileTestHelpers::createTestInstrumentWithFoilChanger(1, pos, shapeXML); + ws2d->setInstrument(instrument); + ComptonProfileTestHelpers::addResolutionParameters(ws2d, 1); + ComptonProfileTestHelpers::addFoilResolution(ws2d, "foil-pos0"); + } return ws2d; } diff --git a/Code/Mantid/Framework/CurveFitting/test/ComptonProfileTestHelpers.h b/Code/Mantid/Framework/CurveFitting/test/ComptonProfileTestHelpers.h index 199fabefdd64..a7b923df3757 100644 --- a/Code/Mantid/Framework/CurveFitting/test/ComptonProfileTestHelpers.h +++ b/Code/Mantid/Framework/CurveFitting/test/ComptonProfileTestHelpers.h @@ -2,6 +2,7 @@ #define COMPTONPROFILETESTHELPERS_H_ #include "MantidGeometry/Instrument/Detector.h" +#include "MantidGeometry/Objects/ShapeFactory.h" #include "MantidKernel/MersenneTwister.h" #include "MantidTestHelpers/ComponentCreationHelper.h" @@ -14,9 +15,12 @@ namespace ComptonProfileTestHelpers static Mantid::API::MatrixWorkspace_sptr createTestWorkspace(const size_t nhist,const double x0, const double x1, const double dx, const bool singleMassSpectrum = false, const bool addFoilChanger = false); - static Mantid::Geometry::Instrument_sptr createTestInstrumentWithFoilChanger(const Mantid::detid_t id,const Mantid::Kernel::V3D &); + static Mantid::Geometry::Instrument_sptr createTestInstrumentWithFoilChanger(const Mantid::detid_t id, + const Mantid::Kernel::V3D &, + const std::string &detShapeXML = ""); static Mantid::Geometry::Instrument_sptr createTestInstrumentWithNoFoilChanger(const Mantid::detid_t id, - const Mantid::Kernel::V3D &); + const Mantid::Kernel::V3D &, + const std::string & detShape = ""); static void addResolutionParameters(const Mantid::API::MatrixWorkspace_sptr & ws, const Mantid::detid_t detID); static void addFoilResolution(const Mantid::API::MatrixWorkspace_sptr & ws, @@ -90,15 +94,19 @@ namespace ComptonProfileTestHelpers return ws2d; } - static Mantid::Geometry::Instrument_sptr createTestInstrumentWithFoilChanger(const Mantid::detid_t id, - const Mantid::Kernel::V3D & detPos) + static Mantid::Geometry::Instrument_sptr + createTestInstrumentWithFoilChanger(const Mantid::detid_t id, + const Mantid::Kernel::V3D & detPos, + const std::string &detShapeXML) { using Mantid::Kernel::V3D; using namespace Mantid::Geometry; - auto inst = createTestInstrumentWithNoFoilChanger(id, detPos); + auto inst = createTestInstrumentWithNoFoilChanger(id, detPos, detShapeXML); // add changer - auto changerShape = ComponentCreationHelper::createCappedCylinder(0.05,0.4,V3D(0.0,-0.2,0.0),V3D(0.0,1,0.0), "cylinder"); + auto changerShape = \ + ComponentCreationHelper::createCappedCylinder(0.05, 0.4, V3D(0.0,-0.2,0.0), + V3D(0.0,1,0.0), "cylinder"); auto *changer = new ObjComponent("foil-changer",changerShape); changer->setPos(V3D(0.0,0.0,0.0)); inst->add(changer); @@ -120,8 +128,10 @@ namespace ComptonProfileTestHelpers return inst; } - static Mantid::Geometry::Instrument_sptr createTestInstrumentWithNoFoilChanger(const Mantid::detid_t id, - const Mantid::Kernel::V3D & detPos) + static Mantid::Geometry::Instrument_sptr + createTestInstrumentWithNoFoilChanger(const Mantid::detid_t id, + const Mantid::Kernel::V3D & detPos, + const std::string &detShapeXML) { using Mantid::Kernel::V3D; using namespace Mantid::Geometry; @@ -140,7 +150,16 @@ namespace ComptonProfileTestHelpers inst->markAsSamplePos(sampleHolder); //Just give it a single detector - auto *det0 = new Detector("det0",id,NULL); + Detector *det0(NULL); + if(!detShapeXML.empty()) + { + auto shape = ShapeFactory().createShape(detShapeXML); + det0 = new Detector("det0", id, shape, NULL); + } + else + { + det0 = new Detector("det0", id, NULL); + } det0->setPos(detPos); inst->add(det0); inst->markAsDetector(det0); From 843a44604f3bf52bf027bc0814e4521966f513bd Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Fri, 19 Sep 2014 17:36:26 +0100 Subject: [PATCH 09/37] Futher debugging on Vesuvio MS calculation. We now get something that looks like a distribution but it doesn't match the VMS output yet. Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 6 +- .../CurveFitting/src/CalculateMSVesuvio.cpp | 181 ++++++++++-------- .../test/CalculateMSVesuvioTest.h | 18 +- .../inc/MantidKernel/PhysicalConstants.h | 15 +- 4 files changed, 124 insertions(+), 96 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index d1d6dc322d75..6e8d0e45579d 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -98,8 +98,6 @@ namespace Mantid std::vector> counts; size_t maxorder; - double weight; - size_t nmscat; }; // Stores counts for each scatter order with errors struct SimulationWithErrors @@ -163,8 +161,8 @@ namespace Mantid Kernel::V3D generateSrcPos(const double l1) const; double generateE0(const double l1, const double t2, double &weight) const; double generateTOF(const double gaussTOF, const double en0, const double dl1) const; - Kernel::V3D generateScatter(const Kernel::V3D &startPos, const Kernel::V3D &direc, - double &weight) const; + bool generateScatter(const Kernel::V3D &startPos, const Kernel::V3D &direc, double &weight, + Kernel::V3D &scatterPt) const; std::pair calculateE1Range(const double theta, const double en0) const; double partialDiffXSec(const double en0, const double en1, const double theta) const; Kernel::V3D generateDetectorPos(const double l2, const double angle, const double energy) const; diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index b36b7d0f83b5..da609f862280 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -34,9 +34,9 @@ namespace Mantid namespace { const size_t NSIMULATIONS = 10; - const size_t NEVENTS = 100000; + const size_t NEVENTS = 500000; const size_t NSCATTERS = 3; - const size_t MAX_SCATTER_PT_TRIES = 500; + const size_t MAX_SCATTER_PT_TRIES = 25; /// Conversion constant const double MASS_TO_MEV = 0.5*PhysicalConstants::NeutronMass/PhysicalConstants::meV; @@ -313,6 +313,7 @@ namespace Mantid //------------------------------------------------------------------------- // RandomNumberGenerator helper //------------------------------------------------------------------------- + /** * Produces random numbers with various probability distributions */ @@ -346,7 +347,7 @@ namespace Mantid CalculateMSVesuvio::Simulation:: Simulation(const size_t order, const size_t ntimes) : counts(order, std::vector(ntimes)), - maxorder(order), weight(0.0), nmscat(0) + maxorder(order) {} //------------------------------------------------------------------------- // SimulationAggreator @@ -394,28 +395,36 @@ namespace Mantid double mean(0.0); size_t npoints(0); for(size_t k = 0; k < nruns; ++k) - { - const double val = results[k].counts[i][j]; - if(val > 0.0) - { - mean += val; - npoints +=1; - } - } - const double dblPts = static_cast(npoints); - orderCounts[j] = mean/dblPts; - // error is std dev - double sumsq(0.0); - for(size_t k = 0; k < nruns; ++k) { const double val = results[k].counts[i][j]; if(val > 0.0) { - const double diff = (val - mean); - sumsq += diff*diff; + mean += val; + npoints +=1; + } + } + if(npoints < 2) + { + orderCounts[j] = 0.0; + orderErrors[j] = 0.0; + } + else + { + const double dblPts = static_cast(npoints); + orderCounts[j] = mean/dblPts; + // error is std dev + double sumsq(0.0); + for(size_t k = 0; k < nruns; ++k) + { + const double val = results[k].counts[i][j]; + if(val > 0.0) + { + const double diff = (val - mean); + sumsq += diff*diff; + } } + orderErrors[j] = sqrt(sumsq/(dblPts*(dblPts-1))); } - orderErrors[j] = sqrt(sumsq/(dblPts*(dblPts-1))); } } @@ -432,23 +441,25 @@ namespace Mantid { const double sumSingle = std::accumulate(sim.counts.front().begin(), sim.counts.front().end(), 0.0); - const double invSum = 1.0/sumSingle; // multiply is faster - // Divide everything by the sum - const size_t nscatters = sim.counts.size(); - for(size_t i = 0; i < nscatters; ++i) + if(sumSingle > 0.0) { - auto & counts = sim.counts[i]; - auto & scerrors = this->errors[i]; - for(auto cit = counts.begin(), eit = scerrors.begin(); cit != counts.end(); - ++cit, ++eit) + const double invSum = 1.0/sumSingle; // multiply is faster + // Divide everything by the sum + const size_t nscatters = sim.counts.size(); + for(size_t i = 0; i < nscatters; ++i) { - (*cit) *= invSum; - (*eit) *= invSum; + auto & counts = sim.counts[i]; + auto & scerrors = this->errors[i]; + for(auto cit = counts.begin(), eit = scerrors.begin(); cit != counts.end(); + ++cit, ++eit) + { + (*cit) *= invSum; + (*eit) *= invSum; + } } } } - //------------------------------------------------------------------------- // Algorithm definitions //------------------------------------------------------------------------- @@ -582,7 +593,7 @@ namespace Mantid setProperty("TotalScatteringWS", totalsc); setProperty("MultipleScatteringWS", multsc); - } + } /** * Caches inputs insuitable form for speed in later calculations @@ -626,9 +637,9 @@ namespace Mantid // -- Workspace -- const auto & inX = m_inputWS->readX(0); - m_tmin = inX.front(); - m_tmax = inX.back(); - m_delt = inX[1] - m_tmin; + m_tmin = inX.front()*1e-06; + m_tmax = inX.back()*1e-06; + m_delt = (inX[1] - m_tmin)*1e-06; // -- Sample -- int nmasses = getProperty("NoOfMasses"); @@ -646,13 +657,13 @@ namespace Mantid m_sampleProps = new SampleComptonProperties(natoms); m_sampleProps->density = getProperty("SampleDensity"); - double totalMass(0.0); + double totalMass(0.0); // total mass in grams m_sampleProps->totalxsec = 0.0; for(int i = 0; i < natoms; ++i) { auto & comptonAtom = m_sampleProps->atoms[i]; comptonAtom.mass = sampleInfo[nExptdAtomProp*i]; - totalMass += comptonAtom.mass; + totalMass += comptonAtom.mass*PhysicalConstants::AtomicMassUnit*1000; const double xsec = sampleInfo[nExptdAtomProp*i + 1]; comptonAtom.sclength = sqrt(xsec/4.0*M_PI); @@ -679,8 +690,8 @@ namespace Mantid if(!detPixel->isMonitor()) break; } // Bounding box in detector frame - auto pixelShape = detPixel->shape(); - if(!pixelShape) + Geometry::Object_const_sptr pixelShape = detPixel->shape(); + if(!pixelShape || !pixelShape->hasValidShape()) { throw std::invalid_argument("Detector pixel has no defined shape!"); } @@ -782,7 +793,7 @@ namespace Mantid { for(size_t i = 0; i < nevents; ++i) { - simulCounts.weight += calculateCounts(nscatters, detpar, respar, simulCounts); + calculateCounts(nscatters, detpar, respar, simulCounts); } } @@ -792,21 +803,13 @@ namespace Mantid * @param detpar Detector information describing the final detector position * @param respar Resolution information on the intrument as a whole * @param simulation [Output] Store the calculated counts here - * @return + * @return The sum of the weights for all scatters */ double CalculateMSVesuvio::calculateCounts(const size_t nscatters, const DetectorParams &detpar, const ResolutionParams &respar, Simulation &simulation) const { double weightSum(0.0); - std::vector weights(nscatters, 1.0), // start at 1.0 - tofs(nscatters, 0.0), - scAngs(nscatters, 0.0), // scattering angles between each order - en1(nscatters, 0.0); - std::vector directions(nscatters), // directions after each scatter - scatterPts(nscatters); // origin of each scatter - - // Initial TOF based on uncertainty in time measurement on detector // moderator coord in lab frame V3D srcPos = generateSrcPos(detpar.l1); @@ -815,6 +818,12 @@ namespace Mantid if(fabs(srcPos[0]) > m_halfSampleWidth || fabs(srcPos[1]) > m_halfSampleHeight) return 0.0; // misses sample + // track various variables during calculation + std::vector weights(nscatters, 1.0), // start at 1.0 + tofs(nscatters, 0.0), + scAngs(nscatters, 0.0), // scattering angles between each order + en1(nscatters, 0.0); + const double vel2 = sqrt(detpar.efixed/MASS_TO_MEV); const double t2 = detpar.l2/vel2; en1[0] = generateE0(detpar.l1, t2, weights[0]); @@ -823,15 +832,16 @@ namespace Mantid // Neutron path // Algorithm has initial direction pointing to origin V3D particleDir = V3D() - srcPos; - directions[0] = particleDir; + particleDir.normalize(); // first scatter + std::vector scatterPts(nscatters); // track origin of each scatter V3D startPos(srcPos); - scatterPts[0] = generateScatter(startPos, particleDir, weights[0]); + generateScatter(startPos, particleDir, weights[0], scatterPts[0]); double distFromStart = startPos.distance(scatterPts[0]); // Compute TOF for first scatter event - double vel = sqrt(en1[0]/MASS_TO_MEV); - tofs[0] += (distFromStart*1e6/vel); + const double vel0 = sqrt(en1[0]/MASS_TO_MEV); + tofs[0] += (distFromStart*1e6/vel0); // multiple scatter events within sample, i.e not including zeroth for(size_t i = 1; i < nscatters; ++i) @@ -840,39 +850,55 @@ namespace Mantid tofs[i] = tofs[i-1]; // Generate a new direction of travel - double randth = acos(2.0*m_randgen->flat() - 1.0); - double randphi = 2.0*M_PI*m_randgen->flat(); V3D oldDir = particleDir; - particleDir.spherical_rad(1.0, randth, randphi); - directions[i] = particleDir; - scAngs[i-1] = particleDir.angle(oldDir); + size_t ntries(0); + do + { + double randth = acos(2.0*m_randgen->flat() - 1.0); + double randphi = 2.0*M_PI*m_randgen->flat(); + particleDir.spherical_rad(1.0, randth, randphi); + particleDir.normalize(); + scAngs[i-1] = particleDir.angle(oldDir); + // Update weight + const double wgt = weights[i]; + if(generateScatter(scatterPts[i-1], particleDir, weights[i], scatterPts[i])) + break; + else + { + weights[i] = wgt; // put it back to what it was + ++ntries; + } + } + while(ntries < MAX_SCATTER_PT_TRIES); + if(ntries == MAX_SCATTER_PT_TRIES) + { + throw std::runtime_error("Unable to generate scatter point in sample. Check sample shape."); + } - // Update weight - scatterPts[i] = generateScatter(scatterPts[i-1], particleDir, weights[i]); auto e1range = calculateE1Range(scAngs[i-1], en1[i-1]); en1[i] = e1range.first + m_randgen->flat()*(e1range.second - e1range.first); const double d2sig = partialDiffXSec(en1[i-1], en1[i], scAngs[i-1]); double weight = d2sig*4.0*M_PI*(e1range.second - e1range.first)/m_sampleProps->totalxsec; // accumulate total weight - simulation.nmscat += 1; weightSum += weight; weights[i] *= weight; // account for this scatter on top of previous // Increment time of flight... - const double distTravelled = scatterPts[i].distance(scatterPts[i-1]); - double vel = sqrt(en1[i]/MASS_TO_MEV); - tofs[i] += (distTravelled*1e6/vel); + const double veli = sqrt(en1[i]/MASS_TO_MEV); + tofs[i] += (scatterPts[i].distance(scatterPts[i-1])*1e6/veli); } // force all orders in to current detector const auto & inX = m_inputWS->readX(0); for(size_t i = 0; i < nscatters; ++i) { - V3D detPos = generateDetectorPos(detpar.l1, detpar.theta, en1[i]); + V3D detPos = generateDetectorPos(detpar.l2, detpar.theta, en1[i]); // transform to sample frame detPos.rotate(*m_goniometer); // Distance to exit the sample for this order - Geometry::Track scatterToDet(scatterPts[i], detPos - scatterPts[i]); + V3D detDirection = detPos - scatterPts[i]; + detDirection.normalize(); + Geometry::Track scatterToDet(scatterPts[i], detDirection); if(m_sampleShape->interceptSurface(scatterToDet) == 0) { throw std::logic_error("CalculateMSVesuvio::calculateCounts() - " @@ -889,8 +915,8 @@ namespace Mantid weights[i] *= partialDiffXSec(en1[i], efinal, scAngs[i])/m_sampleProps->totalxsec; // final TOF - double vel = sqrt(efinal/MASS_TO_MEV); - tofs[i] += detpar.t0 + (scatterPts[i].distance(detPos)*1e6)/vel; + const double veli = sqrt(efinal/MASS_TO_MEV); + tofs[i] += detpar.t0 + (scatterPts[i].distance(detPos)*1e6)/veli; // "Bin" weight into appropriate place std::vector &counts = simulation.counts[i]; @@ -902,7 +928,8 @@ namespace Mantid auto prevIter = uppIter - 1; if(finalTOF < *uppIter - 0.5*(*uppIter - *prevIter)) --uppIter; } - counts[std::distance(inX.begin(), uppIter)] += weights[i]; + size_t idx = std::distance(inX.begin(), uppIter); + counts[idx] += weights[i]; } return weightSum; @@ -949,7 +976,7 @@ namespace Mantid const double vel0 = l1/t1; const double en0 = MASS_TO_MEV*vel0*vel0; - weight *= 2.0*weight/t1/pow(weight, 0.9); + weight = 2.0*en0/t1/pow(en0, 0.9); weight *= 1e-4; // Reduce weight to ~1 return en0; @@ -1004,16 +1031,16 @@ namespace Mantid * @param startPos Starting position * @param direc Direction of travel for the neutron * @param weight [InOut] Multiply the incoming weight by the attenuation factor - * @return The generated scattering point + * @param scatterPt [Out] Generated scattering point + * @return True if the scatter event was generated, false otherwise */ - Kernel::V3D CalculateMSVesuvio::generateScatter(const Kernel::V3D &startPos, const Kernel::V3D &direc, - double &weight) const + bool CalculateMSVesuvio::generateScatter(const Kernel::V3D &startPos, const Kernel::V3D &direc, + double &weight, V3D &scatterPt) const { Track particleTrack(startPos, direc); if(m_sampleShape->interceptSurface(particleTrack) != 1) { - throw std::runtime_error("CalculateMSVesuvio::calculateCounts - " - "Sample shape appears to have a hole in it?. Unable to continue"); + return false; } // Find distance inside object and compute probability of scattering const auto & link = particleTrack.begin(); @@ -1024,12 +1051,12 @@ namespace Mantid const double dist = -log(1.0 - m_randgen->flat()*scatterProb)/m_sampleProps->mu; // From start point advance in direction of travel by computed distance to find scatter point // Track is defined as set of links and exit point of first link is entry to sample! - V3D scatterPt = link->entryPoint; + scatterPt = link->entryPoint; scatterPt += direc*dist; // Update weight weight *= scatterProb; - return scatterPt; + return true; } /** @@ -1087,7 +1114,7 @@ namespace Mantid { const double jstddev = atoms[i].profile; const double mass = atoms[i].mass; - const double y = 0.5*mass*w/(4.18036*q) - q; + const double y = mass*w/(4.18036*q) - 0.5*q; const double jy = exp(-0.5*y*y/(jstddev*jstddev))/(jstddev*rt2pi); const double sqw = mass*jy/(4.18036*q); diff --git a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h index bca1678e785d..dae6b089a0c6 100644 --- a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h +++ b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h @@ -129,7 +129,7 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite void test_input_workspace_with_detector_that_has_no_shape_throws_exception() { - auto alg = createTestAlgorithm(createFlatPlateSampleWS()); + auto alg = createTestAlgorithm(createFlatPlateSampleWS(false)); TS_ASSERT_THROWS(alg->execute(), std::invalid_argument); } @@ -165,10 +165,10 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite const double halfHeight(0.05), halfWidth(0.05), halfThick(0.0025); std::ostringstream sampleShapeXML; sampleShapeXML << " " - << " " - << " " - << " " - << " " + << " " + << " " + << " " + << " " << ""; auto sampleShape = Mantid::Geometry::ShapeFactory().createShape(sampleShapeXML.str()); testWS->mutableSample().setShape(*sampleShape); @@ -193,10 +193,10 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite // replace instrument with one that has a detector with a shape const std::string shapeXML = \ "" - "" - "" - "" - "" + "" + "" + "" + "" "" ""; const auto pos = ws2d->getDetector(0)->getPos(); diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/PhysicalConstants.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/PhysicalConstants.h index c4c59f7bad30..fa19d947a330 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/PhysicalConstants.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/PhysicalConstants.h @@ -16,7 +16,7 @@ namespace Mantid @author Russell Taylor, Tessella Support Services plc @date 30/10/2007 - + Copyright © 2007-2010 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory This file is part of Mantid. @@ -34,7 +34,7 @@ namespace Mantid 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: . + File change history is stored at: . Code Documentation is available at: */ namespace PhysicalConstants @@ -52,16 +52,19 @@ namespace PhysicalConstants /** Standard acceleration due to gravity. Precise value in ms-2. */ static const double g = 9.80665; - + /** Mass of the neutron in kg. Taken from on 30/10/2007. */ static const double NeutronMass = 1.674927211e-27; /** Mass of the neutron in AMU. Taken from on 02/01/2013. */ static const double NeutronMassAMU = 1.008664916; + /** AMU in kg. Taken from on 10/09/2014. */ + static const double AtomicMassUnit = 1.660538921e-27; + /** 1 meV in Joules. Taken from on 28/03/2008. */ static const double meV = 1.602176487e-22; - + /** 1 meV in wavenumber (cm-1). Taken from on 02/04/2008. */ static const double meVtoWavenumber = 8.06554465; @@ -74,10 +77,10 @@ namespace PhysicalConstants /** Muon lifetime. Taken from MuLan experiment on 08/12/2008. */ static const double MuonLifetime = 2.197019e-6; - /** Standard atmospheric pressure in kPa. + /** Standard atmospheric pressure in kPa. * Taken from on 01/12/2010 **/ static const double StandardAtmosphere = 101.325; - + /** Boltzmann Constant in meV/K * Taken from on 10/07/2012 */ From a3083f7128437d53ed1425844999737f9a5289c7 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Mon, 22 Sep 2014 16:10:12 +0100 Subject: [PATCH 10/37] Add error in tof to Vesuvio resolution parameters Refs #10169 --- .../MantidCurveFitting/VesuvioResolution.h | 11 ++-- .../CurveFitting/src/VesuvioResolution.cpp | 5 +- Code/Mantid/instrument/VESUVIO_Parameters.xml | 59 ++++++++++--------- 3 files changed, 41 insertions(+), 34 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/VesuvioResolution.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/VesuvioResolution.h index f03970f60223..fd9f28d1437b 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/VesuvioResolution.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/VesuvioResolution.h @@ -20,11 +20,12 @@ namespace CurveFitting /// It avoids some functions taking a huge number of arguments struct ResolutionParams { - double dl1; ///< spread in source-sample distance - double dl2; ///< spread in sample-detector distance - double dthe; ///< spread in scattering angle - double dEnLorentz; ///< lorentz width in energy - double dEnGauss; ///< gaussian width in energy + double dl1; ///< spread in source-sample distance (m) + double dl2; ///< spread in sample-detector distance (m) + double dtof; ///< spread in tof measurement (us) + double dthe; ///< spread in scattering angle (radians) + double dEnLorentz; ///< lorentz width in energy (meV) + double dEnGauss; ///< gaussian width in energy (meV }; /** diff --git a/Code/Mantid/Framework/CurveFitting/src/VesuvioResolution.cpp b/Code/Mantid/Framework/CurveFitting/src/VesuvioResolution.cpp index c0adfc1b6a56..7e0a734546ac 100644 --- a/Code/Mantid/Framework/CurveFitting/src/VesuvioResolution.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/VesuvioResolution.cpp @@ -87,6 +87,7 @@ namespace CurveFitting ResolutionParams respar; respar.dl1 = ConvertToYSpace::getComponentParameter(det, pmap, "sigma_l1"); respar.dl2 = ConvertToYSpace::getComponentParameter(det, pmap, "sigma_l2"); + respar.dtof = ConvertToYSpace::getComponentParameter(det, pmap, "sigma_tof"); respar.dthe = ConvertToYSpace::getComponentParameter(det, pmap, "sigma_theta"); //radians respar.dEnLorentz = ConvertToYSpace::getComponentParameter(det, pmap, "hwhm_lorentz"); respar.dEnGauss = ConvertToYSpace::getComponentParameter(det, pmap, "sigma_gauss"); @@ -156,7 +157,7 @@ namespace CurveFitting double wl2 = 2.0*STDDEV_TO_HWHM*std::abs((std::pow(k0y0,3)/(k1*qy0*detpar.l1))*common)*respar.dl2; m_resolutionSigma = std::sqrt(std::pow(wgauss,2) + std::pow(wtheta,2) + std::pow(wl1,2) + std::pow(wl2,2)); - + m_log.notice() << "--------------------- Mass=" << m_mass << " -----------------------" << std::endl; m_log.notice() << "w_l1 (FWHM)=" << wl2 << std::endl; m_log.notice() << "w_l0 (FWHM)=" << wl1 << std::endl; @@ -203,7 +204,7 @@ namespace CurveFitting { voigtApprox(voigt, xValues, lorentzPos, lorentzAmp, m_lorentzFWHM, m_resolutionSigma); } - + /** * Transforms the input y coordinates using the Voigt function approximation. The area is normalized to lorentzAmp * @param voigt [Out] Output values (vector is expected to be of the correct size diff --git a/Code/Mantid/instrument/VESUVIO_Parameters.xml b/Code/Mantid/instrument/VESUVIO_Parameters.xml index d1fcdc31ad6a..867fc7bdb68e 100644 --- a/Code/Mantid/instrument/VESUVIO_Parameters.xml +++ b/Code/Mantid/instrument/VESUVIO_Parameters.xml @@ -7,106 +7,111 @@ - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + - + + + + + + - + @@ -117,7 +122,7 @@ - @@ -132,7 +137,7 @@ - + @@ -143,7 +148,7 @@ - @@ -156,8 +161,8 @@ - - From 65d01b5d6497d120bcedfcf092712767ee23c7fc Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Mon, 22 Sep 2014 16:11:40 +0100 Subject: [PATCH 11/37] Fix a few more bugs in calculation. 1. Use resolution parameter for generating initial tof 2. Use correct coordinate system for generating random scatter direction. Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 2 +- .../CurveFitting/src/CalculateMSVesuvio.cpp | 22 ++++++++++++------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index 6e8d0e45579d..110b218c70ce 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -160,7 +160,7 @@ namespace Mantid // single-event helpers Kernel::V3D generateSrcPos(const double l1) const; double generateE0(const double l1, const double t2, double &weight) const; - double generateTOF(const double gaussTOF, const double en0, const double dl1) const; + double generateTOF(const double gaussTOF, const double dtof, const double dl1) const; bool generateScatter(const Kernel::V3D &startPos, const Kernel::V3D &direc, double &weight, Kernel::V3D &scatterPt) const; std::pair calculateE1Range(const double theta, const double en0) const; diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index da609f862280..568e0eefda57 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -559,10 +559,9 @@ namespace Mantid // Setup progress const int64_t nhist = static_cast(m_inputWS->getNumberHistograms()); - m_progress = new API::Progress(this, 0.0, 1.0, nhist); + m_progress = new API::Progress(this, 0.0, 1.0, nhist*NSIMULATIONS*2); for(int64_t i = 0; i < nhist; ++i) { - m_progress->report("Calculating corrections"); // Copy over the X-values const MantidVec & xValues = m_inputWS->readX(i); @@ -727,7 +726,7 @@ namespace Mantid { // Detector information DetectorParams detpar = ConvertToYSpace::getDetectorParameters(m_inputWS, wsIndex); - // t0 is stored in seconds here, whereas here we want microseconds + // t0 is stored in seconds by default, whereas here we want microseconds detpar.t0 *= 1e6; const Geometry::IDetector_const_sptr detector = m_inputWS->getDetector(wsIndex); @@ -736,6 +735,7 @@ namespace Mantid ResolutionParams respar; respar.dl1 = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_l1"); respar.dl2 = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_l2"); + respar.dtof = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_tof"); respar.dthe = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_theta"); //radians respar.dEnLorentz = ConvertToYSpace::getComponentParameter(detector, pmap, "hwhm_lorentz"); respar.dEnGauss = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_gauss"); @@ -745,8 +745,12 @@ namespace Mantid SimulationAggregator accumulator(nruns); for(size_t i = 0; i < nruns; ++i) { + m_progress->report("MS calculation: idx=" + boost::lexical_cast(wsIndex) + + ", run=" + boost::lexical_cast(i)); simulate(nevents, nscatters, detpar, respar, accumulator.newSimulation(nscatters, m_inputWS->blocksize())); + m_progress->report("MS calculation: idx=" + boost::lexical_cast(wsIndex) + + ", run=" + boost::lexical_cast(i)); } SimulationWithErrors avgCounts = accumulator.average(); @@ -827,7 +831,7 @@ namespace Mantid const double vel2 = sqrt(detpar.efixed/MASS_TO_MEV); const double t2 = detpar.l2/vel2; en1[0] = generateE0(detpar.l1, t2, weights[0]); - tofs[0] = generateTOF(en1[0], detpar.t0, respar.dl1); // correction for resolution in l1 + tofs[0] = generateTOF(en1[0], respar.dtof, respar.dl1); // correction for resolution in l1 // Neutron path // Algorithm has initial direction pointing to origin @@ -856,9 +860,11 @@ namespace Mantid { double randth = acos(2.0*m_randgen->flat() - 1.0); double randphi = 2.0*M_PI*m_randgen->flat(); - particleDir.spherical_rad(1.0, randth, randphi); + + particleDir.azimuth_polar_SNS(1.0, randth, randphi); particleDir.normalize(); scAngs[i-1] = particleDir.angle(oldDir); + // Update weight const double wgt = weights[i]; if(generateScatter(scatterPts[i-1], particleDir, weights[i], scatterPts[i])) @@ -986,12 +992,12 @@ namespace Mantid * Generate an initial tof from this distribution: * 1-(0.5*X**2/T0**2+X/T0+1)*EXP(-X/T0), where x is the time and t0 * is the src-sample time. - * @param dt0 Error in time resolution (us) + * @param dtof Error in time resolution (us) * @param en0 Value of the incident energy * @param dl1 S.d of moderator to sample distance * @return tof Guass TOF modified for asymmetric pulse */ - double CalculateMSVesuvio::generateTOF(const double en0, const double dt0, + double CalculateMSVesuvio::generateTOF(const double en0, const double dtof, const double dl1) const { const double vel1 = sqrt(en0/MASS_TO_MEV); @@ -1002,7 +1008,7 @@ namespace Mantid const double yv = m_randgen->flat(); double xt(xmin); - double tof = m_randgen->gaussian(0.0, dt0); + double tof = m_randgen->gaussian(0.0, dtof); while(true) { xt += dx; From 23b58b02e6c7d4e3d1c643238c1a45f30527e6d2 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Tue, 23 Sep 2014 11:47:01 +0100 Subject: [PATCH 12/37] Fix error in operator precedance when calculating scatter length Refs #10169 --- Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 568e0eefda57..16f42dd5d637 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -665,7 +665,7 @@ namespace Mantid totalMass += comptonAtom.mass*PhysicalConstants::AtomicMassUnit*1000; const double xsec = sampleInfo[nExptdAtomProp*i + 1]; - comptonAtom.sclength = sqrt(xsec/4.0*M_PI); + comptonAtom.sclength = sqrt(xsec/(4.0*M_PI)); const double factor = 1.0 + (PhysicalConstants::NeutronMassAMU/comptonAtom.mass); m_sampleProps->totalxsec += (xsec/(factor*factor)); From 472d2edb5dbdad9b7a95a88c76cbcb7fd00762da Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Mon, 22 Sep 2014 16:42:14 +0100 Subject: [PATCH 13/37] Fix bug in e1 range determination. Refs #10169 --- Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 16f42dd5d637..0f8f8ffca8bd 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -1090,7 +1090,7 @@ namespace Mantid const double e1a = en0 - wr - 10.0*width; const double e1b = en0 - wr + 10.0*width; if(e1a < e1min) e1min = e1a; - if(e1b > e1min) e1max = e1b; + if(e1b > e1max) e1max = e1b; } if(e1min < 0.0) e1min = 0.0; return std::make_pair(e1min, e1max); From 4ef012d9d851a490505e44bf008bffdd5e3a9af4 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Tue, 23 Sep 2014 14:28:46 +0100 Subject: [PATCH 14/37] Fix bug in binning data to final workspace. Refs #10169 --- .../CurveFitting/src/CalculateMSVesuvio.cpp | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 0f8f8ffca8bd..d2d59f15b058 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -638,7 +638,7 @@ namespace Mantid const auto & inX = m_inputWS->readX(0); m_tmin = inX.front()*1e-06; m_tmax = inX.back()*1e-06; - m_delt = (inX[1] - m_tmin)*1e-06; + m_delt = (inX[1] - inX.front()); // -- Sample -- int nmasses = getProperty("NoOfMasses"); @@ -753,13 +753,14 @@ namespace Mantid + ", run=" + boost::lexical_cast(i)); } + // Average over all runs and assign to output workspaces SimulationWithErrors avgCounts = accumulator.average(); avgCounts.normalise(); - // assign to output spectrum + // Sum up all multiple scatter events auto & msscatY = multsc.dataY(); auto & msscatE = multsc.dataE(); - for(size_t i = 0; i < nscatters; ++i) + for(size_t i = 1; i < nscatters; ++i) //(i >= 1 for multiple scatters) { const auto & counts = avgCounts.sim.counts[i]; // equivalent to msscatY[j] += counts[j] @@ -927,15 +928,16 @@ namespace Mantid // "Bin" weight into appropriate place std::vector &counts = simulation.counts[i]; const double finalTOF = tofs[i]; - auto uppIter = std::upper_bound(inX.begin(), inX.end(), finalTOF); - if(uppIter != inX.begin()) + + for (size_t it = 0; it < inX.size(); ++it) { - // See which side of line between us and previous value it should fall - auto prevIter = uppIter - 1; - if(finalTOF < *uppIter - 0.5*(*uppIter - *prevIter)) --uppIter; + if (inX[it] - 0.5*m_delt < finalTOF && finalTOF < inX[it] + 0.5*m_delt) + { + counts[it] += weights[i]; + break; + } } - size_t idx = std::distance(inX.begin(), uppIter); - counts[idx] += weights[i]; + } return weightSum; From 9a9fd6dc1449472209d687add4fd29173c8a4a26 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Tue, 30 Sep 2014 14:18:19 +0100 Subject: [PATCH 15/37] Convert umbra and penumbra values to metres internally. Refs #10169 --- .../CurveFitting/src/CalculateMSVesuvio.cpp | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index d2d59f15b058..af602567ec64 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -34,7 +34,7 @@ namespace Mantid namespace { const size_t NSIMULATIONS = 10; - const size_t NEVENTS = 500000; + const size_t NEVENTS = 50000; const size_t NSCATTERS = 3; const size_t MAX_SCATTER_PT_TRIES = 25; /// Conversion constant @@ -313,7 +313,6 @@ namespace Mantid //------------------------------------------------------------------------- // RandomNumberGenerator helper //------------------------------------------------------------------------- - /** * Produces random numbers with various probability distributions */ @@ -618,6 +617,9 @@ namespace Mantid << m_srcR1; throw std::invalid_argument(os.str()); } + // Convert to metres + m_srcR1 /= 100.0; + m_srcR2 /= 100.0; // Sample rotation specified by a goniometer m_goniometer = &(m_inputWS->run().getGoniometerMatrix()); @@ -820,8 +822,11 @@ namespace Mantid V3D srcPos = generateSrcPos(detpar.l1); // transform to sample frame srcPos.rotate(*m_goniometer); - if(fabs(srcPos[0]) > m_halfSampleWidth || - fabs(srcPos[1]) > m_halfSampleHeight) return 0.0; // misses sample + if(fabs(srcPos[m_acrossIdx]) > m_halfSampleWidth || + fabs(srcPos[m_upIdx]) > m_halfSampleHeight) + { + return 0.0; // misses sample + } // track various variables during calculation std::vector weights(nscatters, 1.0), // start at 1.0 From 19bbbd325d2182815b138e8e0916058bc09a44cc Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Thu, 16 Oct 2014 16:20:28 +0100 Subject: [PATCH 16/37] Fix scattering angle calculation on final detector event. Refs #10169 --- Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index af602567ec64..fa8b4f358f01 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -917,6 +917,8 @@ namespace Mantid "Logical error. No intersection with sample, despite track " "originating from with sample."); } + // Calculate final scattering angle + scAngs[i] = detDirection.angle(scatterPts[i]); const auto & link = scatterToDet.begin(); double distToExit = link->distInsideObject; // Weight by probability neutron leaves sample From 303294c4a2aaaa7ad239fa5958bc52826ac7e3af Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Thu, 16 Oct 2014 16:22:13 +0100 Subject: [PATCH 17/37] Fix loop terminator for uranium final energy. Refs #10169 --- .../Framework/CurveFitting/src/CalculateMSVesuvio.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index fa8b4f358f01..0aea4ca37dfc 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -249,8 +249,8 @@ namespace Mantid } /** - * Generate the final energy of a neutron for gold foil analyser at 293K - * with number density of 7.35E19 atoms/cm^2 in double-difference mode. + * Generate the final energy of a neutron for uranium foil analyser at 293K + * with number density of 1.456E20 atoms/cm^2 in double-difference mode. * @param randv A random number between 0.0 & 1.0, sample from a flat distribution * @return A value to use for the final energy */ @@ -296,7 +296,7 @@ namespace Mantid 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000 }; - for(size_t i = 0; i < 299; ++i) + for(size_t i = 0; i < 200; ++i) { if(XVALUES[i] < randv && XVALUES[i+1] > randv) { From 2cebd8705ad339f47fc2f925fd9254211f34cdbe Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Fri, 17 Oct 2014 13:36:19 +0100 Subject: [PATCH 18/37] Update the Vesuvio detector shape to match reality. Refs #10169 --- Code/Mantid/instrument/VESUVIO_Definition.xml | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Code/Mantid/instrument/VESUVIO_Definition.xml b/Code/Mantid/instrument/VESUVIO_Definition.xml index f760a8f2faa8..4bda845b804c 100644 --- a/Code/Mantid/instrument/VESUVIO_Definition.xml +++ b/Code/Mantid/instrument/VESUVIO_Definition.xml @@ -320,22 +320,22 @@ - - - - + + + + - - - - - + + + + + - + @@ -355,4 +355,4 @@ - \ No newline at end of file + From 7a84ddf84fdf8b7609d77eea5c8caaf487181172 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Tue, 28 Oct 2014 11:33:00 +0000 Subject: [PATCH 19/37] Fix problems with computing the final detector position. Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 2 +- .../CurveFitting/src/CalculateMSVesuvio.cpp | 118 +++++++++--------- 2 files changed, 61 insertions(+), 59 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index 110b218c70ce..53f0fe78d23b 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -165,7 +165,7 @@ namespace Mantid Kernel::V3D &scatterPt) const; std::pair calculateE1Range(const double theta, const double en0) const; double partialDiffXSec(const double en0, const double en1, const double theta) const; - Kernel::V3D generateDetectorPos(const double l2, const double angle, const double energy) const; + Kernel::V3D generateDetectorPos(const Kernel::V3D & nominalPos, const double energy) const; double generateE1(const double angle, const double e1nom, const double e1res) const; // Member Variables diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 0aea4ca37dfc..719b9dba52d4 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -601,6 +601,7 @@ namespace Mantid // -- Geometry -- const auto instrument = m_inputWS->getInstrument(); m_beamDir = instrument->getSample()->getPos() - instrument->getSource()->getPos(); + m_beamDir.normalize(); const auto rframe = instrument->getReferenceFrame(); m_acrossIdx = rframe->pointingHorizontal(); @@ -830,8 +831,7 @@ namespace Mantid // track various variables during calculation std::vector weights(nscatters, 1.0), // start at 1.0 - tofs(nscatters, 0.0), - scAngs(nscatters, 0.0), // scattering angles between each order + tofs(nscatters, 0.0), // tof accumulates for each piece of the calculation en1(nscatters, 0.0); const double vel2 = sqrt(detpar.efixed/MASS_TO_MEV); @@ -840,14 +840,12 @@ namespace Mantid tofs[0] = generateTOF(en1[0], respar.dtof, respar.dl1); // correction for resolution in l1 // Neutron path - // Algorithm has initial direction pointing to origin - V3D particleDir = V3D() - srcPos; - particleDir.normalize(); - - // first scatter - std::vector scatterPts(nscatters); // track origin of each scatter + std::vector scatterPts(nscatters), // track origin of each scatter + neutronDirs(nscatters); // neutron directions V3D startPos(srcPos); - generateScatter(startPos, particleDir, weights[0], scatterPts[0]); + neutronDirs[0] = m_beamDir; + + generateScatter(startPos, neutronDirs[0], weights[0], scatterPts[0]); double distFromStart = startPos.distance(scatterPts[0]); // Compute TOF for first scatter event const double vel0 = sqrt(en1[0]/MASS_TO_MEV); @@ -860,20 +858,20 @@ namespace Mantid tofs[i] = tofs[i-1]; // Generate a new direction of travel - V3D oldDir = particleDir; + const V3D & prevSc = scatterPts[i-1]; + V3D & curSc = scatterPts[i-1]; + const V3D & oldDir = neutronDirs[i-1]; + V3D & newDir = neutronDirs[i]; size_t ntries(0); do { - double randth = acos(2.0*m_randgen->flat() - 1.0); - double randphi = 2.0*M_PI*m_randgen->flat(); - - particleDir.azimuth_polar_SNS(1.0, randth, randphi); - particleDir.normalize(); - scAngs[i-1] = particleDir.angle(oldDir); + const double randth = acos(2.0*m_randgen->flat() - 1.0); + const double randphi = 2.0*M_PI*m_randgen->flat(); + newDir.azimuth_polar_SNS(1.0, randphi, randth); // Update weight const double wgt = weights[i]; - if(generateScatter(scatterPts[i-1], particleDir, weights[i], scatterPts[i])) + if(generateScatter(prevSc, newDir, weights[i], curSc)) break; else { @@ -887,9 +885,10 @@ namespace Mantid throw std::runtime_error("Unable to generate scatter point in sample. Check sample shape."); } - auto e1range = calculateE1Range(scAngs[i-1], en1[i-1]); + const double scang = newDir.angle(oldDir); + auto e1range = calculateE1Range(scang, en1[i-1]); en1[i] = e1range.first + m_randgen->flat()*(e1range.second - e1range.first); - const double d2sig = partialDiffXSec(en1[i-1], en1[i], scAngs[i-1]); + const double d2sig = partialDiffXSec(en1[i-1], en1[i], scang); double weight = d2sig*4.0*M_PI*(e1range.second - e1range.first)/m_sampleProps->totalxsec; // accumulate total weight weightSum += weight; @@ -897,41 +896,49 @@ namespace Mantid // Increment time of flight... const double veli = sqrt(en1[i]/MASS_TO_MEV); - tofs[i] += (scatterPts[i].distance(scatterPts[i-1])*1e6/veli); + tofs[i] += (curSc.distance(prevSc)*1e6/veli); } // force all orders in to current detector const auto & inX = m_inputWS->readX(0); for(size_t i = 0; i < nscatters; ++i) { - V3D detPos = generateDetectorPos(detpar.l2, detpar.theta, en1[i]); - // transform to sample frame - detPos.rotate(*m_goniometer); - // Distance to exit the sample for this order - V3D detDirection = detPos - scatterPts[i]; - detDirection.normalize(); - Geometry::Track scatterToDet(scatterPts[i], detDirection); - if(m_sampleShape->interceptSurface(scatterToDet) == 0) + V3D detPos; + double scang(0.0), distToExit(0.0); + size_t ntries(0); + do + { + V3D detPos = generateDetectorPos(detpar.pos, en1[i]); + // transform to sample frame + detPos.rotate(*m_goniometer); + // Distance to exit the sample for this order + V3D scToDet = detPos - scatterPts[i]; + scToDet.normalize(); + Geometry::Track scatterToDet(scatterPts[i], scToDet); + if(m_sampleShape->interceptSurface(scatterToDet) > 0) + { + scang = neutronDirs[i].angle(scToDet); + const auto & link = scatterToDet.begin(); + distToExit = link->distInsideObject; + break; + } + // if point is very close surface then there may be no valid intercept so try again + } + while(ntries < MAX_SCATTER_PT_TRIES); + if(ntries == MAX_SCATTER_PT_TRIES) { - throw std::logic_error("CalculateMSVesuvio::calculateCounts() - " - "Logical error. No intersection with sample, despite track " - "originating from with sample."); + throw std::runtime_error("Unable to create track from sample to detector. " + "Detector shape may be too small."); } - // Calculate final scattering angle - scAngs[i] = detDirection.angle(scatterPts[i]); - const auto & link = scatterToDet.begin(); - double distToExit = link->distInsideObject; + // Weight by probability neutron leaves sample weights[i] *= exp(-m_sampleProps->mu*distToExit); - // Weight by cross-section for the final energy const double efinal = generateE1(detpar.theta, detpar.efixed, m_foilRes); - weights[i] *= partialDiffXSec(en1[i], efinal, scAngs[i])/m_sampleProps->totalxsec; - + weights[i] *= partialDiffXSec(en1[i], efinal, scang)/m_sampleProps->totalxsec; // final TOF const double veli = sqrt(efinal/MASS_TO_MEV); tofs[i] += detpar.t0 + (scatterPts[i].distance(detPos)*1e6)/veli; - // "Bin" weight into appropriate place std::vector &counts = simulation.counts[i]; const double finalTOF = tofs[i]; @@ -944,7 +951,6 @@ namespace Mantid break; } } - } return weightSum; @@ -1064,13 +1070,13 @@ namespace Mantid // Select a random point on the track that is the actual scatter point // from the scattering probability distribution const double dist = -log(1.0 - m_randgen->flat()*scatterProb)/m_sampleProps->mu; - // From start point advance in direction of travel by computed distance to find scatter point - // Track is defined as set of links and exit point of first link is entry to sample! + const double fraction = dist/totalObjectDist; + // Scatter point is then entry point + fraction of width in each direction scatterPt = link->entryPoint; - scatterPt += direc*dist; + V3D edgeDistances = (link->exitPoint - link->entryPoint); + scatterPt += edgeDistances*fraction; // Update weight weight *= scatterProb; - return true; } @@ -1151,26 +1157,22 @@ namespace Mantid /** * Generate a random position within the final detector in the lab frame - * @param l2 The nominal distance from sample to detector - * @param angle The The scattering angle from the sample + * @param nominalPos The poisiton of the centre point of the detector * @param energy The final energy of the neutron * @return A new position in the detector */ - V3D CalculateMSVesuvio::generateDetectorPos(const double l2, const double angle, const double energy) const + V3D CalculateMSVesuvio::generateDetectorPos(const V3D &nominalPos, const double energy) const { const double mu = 7430.0/sqrt(energy); // Inverse attenuation length (m-1) for vesuvio det. - const double ps = 1.0 - exp(-mu*m_detThick); // Probability of detection in path length YD. - - const double width = -0.5*m_detWidth + m_detWidth*m_randgen->flat(); - const double beam = -log(1.0 - m_randgen->flat()*ps)/mu; - const double height = -0.5*m_detHeight + m_detHeight*m_randgen->flat(); - const double widthLab = (l2 + beam)*sin(angle) + width*cos(angle); - const double beamLab = (l2 + beam)*cos(angle) - width*sin(angle); + const double ps = 1.0 - exp(-mu*m_detThick); // Probability of detection in path thickness. V3D detPos; - detPos[m_beamIdx] = beamLab; - detPos[m_acrossIdx] = widthLab; - detPos[m_upIdx] = height; - + // Beam direction by moving to front of "box"define by detector dimensions and then + // computing expected distance travelled based on probability + detPos[m_beamIdx] = (nominalPos[m_beamIdx] - 0.5*m_detThick) - \ + (log(1.0 - m_randgen->flat()*ps)/mu); + // perturb away from nominal position + detPos[m_acrossIdx] = nominalPos[m_acrossIdx] + (m_randgen->flat() - 0.5)*m_detWidth; + detPos[m_upIdx] = nominalPos[m_upIdx] + (m_randgen->flat() - 0.5)*m_detHeight; return detPos; } From 8a6602d338ff565c93f23b9149ac776b60a9fe9a Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Tue, 28 Oct 2014 13:16:50 +0000 Subject: [PATCH 20/37] Fix indexing bug with scatter points. Refs #10169 --- Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 719b9dba52d4..1839c17bba11 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -859,7 +859,7 @@ namespace Mantid // Generate a new direction of travel const V3D & prevSc = scatterPts[i-1]; - V3D & curSc = scatterPts[i-1]; + V3D & curSc = scatterPts[i]; const V3D & oldDir = neutronDirs[i-1]; V3D & newDir = neutronDirs[i]; size_t ntries(0); From d60b642293208727e2b468841ba62898566929fc Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Wed, 29 Oct 2014 13:39:15 +0000 Subject: [PATCH 21/37] Fix scope problem with setting detector position. Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 5 +- .../CurveFitting/src/CalculateMSVesuvio.cpp | 83 ++++++++++--------- 2 files changed, 49 insertions(+), 39 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index 53f0fe78d23b..d3cc71ed3739 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -165,7 +165,10 @@ namespace Mantid Kernel::V3D &scatterPt) const; std::pair calculateE1Range(const double theta, const double en0) const; double partialDiffXSec(const double en0, const double en1, const double theta) const; - Kernel::V3D generateDetectorPos(const Kernel::V3D & nominalPos, const double energy) const; + Kernel::V3D generateDetectorPos(const Kernel::V3D & nominalPos, const double energy, + const Kernel::V3D &scatterPt, + const Kernel::V3D & direcBeforeSc, + double &scang, double &distToExit) const; double generateE1(const double angle, const double e1nom, const double e1res) const; // Member Variables diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 1839c17bba11..5823cd4c4be9 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -903,39 +903,15 @@ namespace Mantid const auto & inX = m_inputWS->readX(0); for(size_t i = 0; i < nscatters; ++i) { - V3D detPos; double scang(0.0), distToExit(0.0); - size_t ntries(0); - do - { - V3D detPos = generateDetectorPos(detpar.pos, en1[i]); - // transform to sample frame - detPos.rotate(*m_goniometer); - // Distance to exit the sample for this order - V3D scToDet = detPos - scatterPts[i]; - scToDet.normalize(); - Geometry::Track scatterToDet(scatterPts[i], scToDet); - if(m_sampleShape->interceptSurface(scatterToDet) > 0) - { - scang = neutronDirs[i].angle(scToDet); - const auto & link = scatterToDet.begin(); - distToExit = link->distInsideObject; - break; - } - // if point is very close surface then there may be no valid intercept so try again - } - while(ntries < MAX_SCATTER_PT_TRIES); - if(ntries == MAX_SCATTER_PT_TRIES) - { - throw std::runtime_error("Unable to create track from sample to detector. " - "Detector shape may be too small."); - } - + V3D detPos = generateDetectorPos(detpar.pos, en1[i], scatterPts[i], neutronDirs[i], + scang, distToExit); // Weight by probability neutron leaves sample - weights[i] *= exp(-m_sampleProps->mu*distToExit); + double & curWgt = weights[i]; + curWgt *= exp(-m_sampleProps->mu*distToExit); // Weight by cross-section for the final energy const double efinal = generateE1(detpar.theta, detpar.efixed, m_foilRes); - weights[i] *= partialDiffXSec(en1[i], efinal, scang)/m_sampleProps->totalxsec; + curWgt *= partialDiffXSec(en1[i], efinal, scang)/m_sampleProps->totalxsec; // final TOF const double veli = sqrt(efinal/MASS_TO_MEV); tofs[i] += detpar.t0 + (scatterPts[i].distance(detPos)*1e6)/veli; @@ -947,7 +923,7 @@ namespace Mantid { if (inX[it] - 0.5*m_delt < finalTOF && finalTOF < inX[it] + 0.5*m_delt) { - counts[it] += weights[i]; + counts[it] += curWgt; break; } } @@ -1159,20 +1135,51 @@ namespace Mantid * Generate a random position within the final detector in the lab frame * @param nominalPos The poisiton of the centre point of the detector * @param energy The final energy of the neutron + * @param scatterPt The position of the scatter event that lead to this detector + * @param direcBeforeSc Directional vector that lead to scatter point that hit this detector + * @param scang [Output] The value of the scattering angle for the generated point + * @param distToExit [Output] The distance covered within the object from scatter to exit * @return A new position in the detector */ - V3D CalculateMSVesuvio::generateDetectorPos(const V3D &nominalPos, const double energy) const + V3D CalculateMSVesuvio::generateDetectorPos(const V3D &nominalPos, const double energy, + const V3D &scatterPt, const V3D &direcBeforeSc, + double &scang, double &distToExit) const { const double mu = 7430.0/sqrt(energy); // Inverse attenuation length (m-1) for vesuvio det. const double ps = 1.0 - exp(-mu*m_detThick); // Probability of detection in path thickness. V3D detPos; - // Beam direction by moving to front of "box"define by detector dimensions and then - // computing expected distance travelled based on probability - detPos[m_beamIdx] = (nominalPos[m_beamIdx] - 0.5*m_detThick) - \ - (log(1.0 - m_randgen->flat()*ps)/mu); - // perturb away from nominal position - detPos[m_acrossIdx] = nominalPos[m_acrossIdx] + (m_randgen->flat() - 0.5)*m_detWidth; - detPos[m_upIdx] = nominalPos[m_upIdx] + (m_randgen->flat() - 0.5)*m_detHeight; + scang = 0.0; distToExit = 0.0; + size_t ntries(0); + do + { + // Beam direction by moving to front of "box"define by detector dimensions and then + // computing expected distance travelled based on probability + detPos[m_beamIdx] = (nominalPos[m_beamIdx] - 0.5*m_detThick) - \ + (log(1.0 - m_randgen->flat()*ps)/mu); + // perturb away from nominal position + detPos[m_acrossIdx] = nominalPos[m_acrossIdx] + (m_randgen->flat() - 0.5)*m_detWidth; + detPos[m_upIdx] = nominalPos[m_upIdx] + (m_randgen->flat() - 0.5)*m_detHeight; + detPos.rotate(*m_goniometer); // to sample frame + + // Distance to exit the sample for this order + V3D scToDet = detPos - scatterPt; + scToDet.normalize(); + Geometry::Track scatterToDet(scatterPt, scToDet); + if(m_sampleShape->interceptSurface(scatterToDet) > 0) + { + scang = direcBeforeSc.angle(scToDet); + const auto & link = scatterToDet.begin(); + distToExit = link->distInsideObject; + break; + } + // if point is very close surface then there may be no valid intercept so try again + } + while(ntries < MAX_SCATTER_PT_TRIES); + if(ntries == MAX_SCATTER_PT_TRIES) + { + throw std::runtime_error("Unable to create track from sample to detector. " + "Detector shape may be too small."); + } return detPos; } From 8b08ecb737c0005c2cb4973f7865c9f9195d3998 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Wed, 29 Oct 2014 15:07:11 +0000 Subject: [PATCH 22/37] Implement value check for unit test. Fixed problem with setting initial detector position & shape uncovered here. Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 1 - .../CurveFitting/src/CalculateMSVesuvio.cpp | 10 +--- .../test/CalculateMSVesuvioTest.h | 52 +++++++++++-------- .../test/ComptonProfileTestHelpers.h | 5 +- 4 files changed, 35 insertions(+), 33 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index d3cc71ed3739..560c0ea5d76b 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -179,7 +179,6 @@ namespace Mantid double m_srcR1, m_srcR2; // beam umbra, penumbra radius (m) double m_halfSampleHeight, m_halfSampleWidth, m_halfSampleThick; // (m) double m_maxWidthSampleFrame; // Maximum width in sample frame (m) - Kernel::DblMatrix const *m_goniometer; // sample rotation Geometry::Object const *m_sampleShape; // sample shape SampleComptonProperties *m_sampleProps; // description of sample properties double m_detHeight, m_detWidth, m_detThick; // (m) diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 5823cd4c4be9..581eb0278af6 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -471,8 +471,7 @@ namespace Mantid m_randgen(NULL), m_acrossIdx(0), m_upIdx(1), m_beamIdx(3), m_beamDir(), m_srcR1(0.0), m_srcR2(0.0), m_halfSampleHeight(0.0), m_halfSampleWidth(0.0), m_halfSampleThick(0.0), - m_maxWidthSampleFrame(0.0), m_goniometer(NULL), m_sampleShape(NULL), - m_sampleProps(NULL), + m_maxWidthSampleFrame(0.0), m_sampleShape(NULL), m_sampleProps(NULL), m_detHeight(-1.0), m_detWidth(-1.0), m_detThick(-1.0), m_tmin(-1.0), m_tmax(-1.0), m_delt(-1.0), m_foilRes(-1.0), m_progress(NULL), m_inputWS() @@ -591,7 +590,7 @@ namespace Mantid setProperty("TotalScatteringWS", totalsc); setProperty("MultipleScatteringWS", multsc); - } + } /** * Caches inputs insuitable form for speed in later calculations @@ -622,8 +621,6 @@ namespace Mantid m_srcR1 /= 100.0; m_srcR2 /= 100.0; - // Sample rotation specified by a goniometer - m_goniometer = &(m_inputWS->run().getGoniometerMatrix()); // Sample shape m_sampleShape = &(m_inputWS->sample().getShape()); // We know the shape is valid from the property validator @@ -821,8 +818,6 @@ namespace Mantid // moderator coord in lab frame V3D srcPos = generateSrcPos(detpar.l1); - // transform to sample frame - srcPos.rotate(*m_goniometer); if(fabs(srcPos[m_acrossIdx]) > m_halfSampleWidth || fabs(srcPos[m_upIdx]) > m_halfSampleHeight) { @@ -1159,7 +1154,6 @@ namespace Mantid // perturb away from nominal position detPos[m_acrossIdx] = nominalPos[m_acrossIdx] + (m_randgen->flat() - 0.5)*m_detWidth; detPos[m_upIdx] = nominalPos[m_upIdx] + (m_randgen->flat() - 0.5)*m_detHeight; - detPos.rotate(*m_goniometer); // to sample frame // Distance to exit the sample for this order V3D scToDet = detPos - scatterPt; diff --git a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h index dae6b089a0c6..b167139e336f 100644 --- a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h +++ b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h @@ -31,28 +31,13 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite TS_ASSERT(alg.isInitialized()); } - void test_exec_with_flat_plate_sample_and_no_goniometer() + void test_exec_with_flat_plate_sample() { auto alg = createTestAlgorithm(createFlatPlateSampleWS()); - - feenableexcept(FE_INVALID | FE_OVERFLOW); - - TS_ASSERT_THROWS_NOTHING(alg->execute()); - TS_ASSERT(alg->isExecuted()); - } - - void test_exec_with_flat_plate_sample_and_goniometer() - { - auto testWS = createFlatPlateSampleWS(); - Mantid::Geometry::Goniometer sampleRot; - // 45.0 deg rotation around Y - sampleRot.pushAxis("phi", 0.0, 1.0, 0.0, 45.0, - Mantid::Geometry::CW, Mantid::Geometry::angDegrees); - testWS->mutableRun().setGoniometer(sampleRot, false); - auto alg = createTestAlgorithm(testWS); - TS_ASSERT_THROWS_NOTHING(alg->execute()); TS_ASSERT(alg->isExecuted()); + + checkOutputValuesAsExpected(alg, 0.0113021908, 0.0028218125); } // ------------------------ Failure Cases ----------------------------------------- @@ -193,10 +178,10 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite // replace instrument with one that has a detector with a shape const std::string shapeXML = \ "" - "" - "" - "" - "" + "" + "" + "" + "" "" ""; const auto pos = ws2d->getDetector(0)->getPos(); @@ -209,6 +194,29 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite return ws2d; } + void checkOutputValuesAsExpected(const Mantid::API::IAlgorithm_sptr & alg, + const double expectedTotal, const double expectedMS) + { + using Mantid::API::MatrixWorkspace_sptr; + const size_t checkIdx = 100; + const double tolerance(1e-8); + + // Values for total scattering + MatrixWorkspace_sptr totScatter = alg->getProperty("TotalScatteringWS"); + TS_ASSERT(totScatter); + const auto & totY = totScatter->readY(0); + TS_ASSERT_DELTA(expectedTotal, totY[checkIdx], tolerance); + const auto & totX = totScatter->readX(0); + TS_ASSERT_DELTA(150.0, totX[checkIdx], tolerance); // based on workspace setup + + // Values for multiple scatters + MatrixWorkspace_sptr multScatter = alg->getProperty("MultipleScatteringWS"); + TS_ASSERT(multScatter); + const auto & msY = multScatter->readY(0); + TS_ASSERT_DELTA(expectedMS, msY[checkIdx], tolerance); + const auto & msX = multScatter->readX(0); + TS_ASSERT_DELTA(150.0, msX[checkIdx], tolerance); // based on workspace setup + } }; diff --git a/Code/Mantid/Framework/CurveFitting/test/ComptonProfileTestHelpers.h b/Code/Mantid/Framework/CurveFitting/test/ComptonProfileTestHelpers.h index a7b923df3757..83c29bd6fb0c 100644 --- a/Code/Mantid/Framework/CurveFitting/test/ComptonProfileTestHelpers.h +++ b/Code/Mantid/Framework/CurveFitting/test/ComptonProfileTestHelpers.h @@ -64,14 +64,14 @@ namespace ComptonProfileTestHelpers { double r(0.553), theta(66.5993), phi(138.6); Mantid::Kernel::V3D detPos; - detPos.spherical(r, theta, phi); + detPos.spherical_rad(r, theta*M_PI/180.0, phi*M_PI/180.0); ws2d->setInstrument(createTestInstrumentWithFoilChanger(id,detPos)); } else { double r(0.55), theta(66.5993), phi(0.0); Mantid::Kernel::V3D detPos; - detPos.spherical(r, theta, phi); + detPos.spherical_rad(r, theta*M_PI/180.0, phi*M_PI/180.0); ws2d->setInstrument(createTestInstrumentWithNoFoilChanger(id,detPos)); } @@ -182,6 +182,7 @@ namespace ComptonProfileTestHelpers pmap.addDouble(compID, "t0", -0.32); pmap.addDouble(compID, "hwhm_lorentz", 24); pmap.addDouble(compID, "sigma_gauss", 73); + pmap.addDouble(compID, "sigma_tof", 0.3); } static void addFoilResolution(const Mantid::API::MatrixWorkspace_sptr & ws, From 91644b76858608238f497c1e2b19cf603c8a976d Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Wed, 29 Oct 2014 15:14:27 +0000 Subject: [PATCH 23/37] Remove some unused properties. Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 2 +- .../CurveFitting/src/CalculateMSVesuvio.cpp | 22 ++++--------------- .../test/CalculateMSVesuvioTest.h | 20 +++-------------- 3 files changed, 8 insertions(+), 36 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index 560c0ea5d76b..ad29ad09c1da 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -176,7 +176,7 @@ namespace Mantid size_t m_acrossIdx, m_upIdx, m_beamIdx; // indices of each direction Kernel::V3D m_beamDir; // Directional vector for beam - double m_srcR1, m_srcR2; // beam umbra, penumbra radius (m) + double m_srcR2; // beam penumbra radius (m) double m_halfSampleHeight, m_halfSampleWidth, m_halfSampleThick; // (m) double m_maxWidthSampleFrame; // Maximum width in sample frame (m) Geometry::Object const *m_sampleShape; // sample shape diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 581eb0278af6..2a4869291550 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -469,7 +469,7 @@ namespace Mantid /// Constructor CalculateMSVesuvio::CalculateMSVesuvio() : Algorithm(), m_randgen(NULL), - m_acrossIdx(0), m_upIdx(1), m_beamIdx(3), m_beamDir(), m_srcR1(0.0), m_srcR2(0.0), + m_acrossIdx(0), m_upIdx(1), m_beamIdx(3), m_beamDir(), m_srcR2(0.0), m_halfSampleHeight(0.0), m_halfSampleWidth(0.0), m_halfSampleThick(0.0), m_maxWidthSampleFrame(0.0), m_sampleShape(NULL), m_sampleProps(NULL), m_detHeight(-1.0), m_detWidth(-1.0), m_detThick(-1.0), @@ -522,12 +522,8 @@ namespace Mantid setPropertyGroup("AtomicProperties", "Sample"); // -- Beam -- - declareProperty("BeamUmbraRadius", -1.0, positiveNonZero, - "Radius, in cm, of part in total shadow."); - declareProperty("BeamPenumbraRadius", -1.0, positiveNonZero, - "Radius, in cm, of part in partial shadow."); - setPropertyGroup("BeamUmbraRadius", "Beam"); - setPropertyGroup("BeamPenumbraRadius", "Beam"); + declareProperty("BeamRadius", -1.0, positiveNonZero, + "Radius, in cm, of beam"); // -- Algorithm -- declareProperty("Seed", 123456789, positiveInt, @@ -607,18 +603,8 @@ namespace Mantid m_upIdx = rframe->pointingUp(); m_beamIdx = rframe->pointingAlongBeam(); - m_srcR1 = getProperty("BeamUmbraRadius"); - m_srcR2 = getProperty("BeamPenumbraRadius"); - if(m_srcR2 < m_srcR1) - { - std::ostringstream os; - os << "Invalid beam radius parameters. Penumbra value=" - << m_srcR2 << " < Umbra value=" - << m_srcR1; - throw std::invalid_argument(os.str()); - } + m_srcR2 = getProperty("BeamRadius"); // Convert to metres - m_srcR1 /= 100.0; m_srcR2 /= 100.0; // Sample shape diff --git a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h index b167139e336f..0fd918d229bc 100644 --- a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h +++ b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h @@ -95,21 +95,8 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite CalculateMSVesuvio alg; alg.initialize(); - TS_ASSERT_THROWS(alg.setProperty("BeamUmbraRadius", -1.5), std::invalid_argument); - TS_ASSERT_THROWS(alg.setProperty("BeamUmbraRadius", 0.0), std::invalid_argument); - TS_ASSERT_THROWS(alg.setProperty("BeamPenumbraRadius", -1.5), std::invalid_argument); - TS_ASSERT_THROWS(alg.setProperty("BeamPenumbraRadius", 0.0), std::invalid_argument); - } - - void test_setting_umbra_less_than_penumbra_throws_invalid_argument() - { - auto testWS = createFlatPlateSampleWS(); - auto alg = createTestAlgorithm(testWS); - - alg->setProperty("BeamUmbraRadius", 2.5); - alg->setProperty("BeamPenumbraRadius", 1.5); - - TS_ASSERT_THROWS(alg->execute(), std::invalid_argument); + TS_ASSERT_THROWS(alg.setProperty("BeamRadius", -1.5), std::invalid_argument); + TS_ASSERT_THROWS(alg.setProperty("BeamRadius", 0.0), std::invalid_argument); } void test_input_workspace_with_detector_that_has_no_shape_throws_exception() @@ -134,8 +121,7 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite const double sampleProps[9] = {1.007900, 0.9272392, 5.003738, 16.00000, 3.2587662E-02, 13.92299, 27.50000, 4.0172841E-02, 15.07701}; alg->setProperty("AtomicProperties", std::vector(sampleProps, sampleProps + 9)); - alg->setProperty("BeamUmbraRadius", 1.5); - alg->setProperty("BeamPenumbraRadius", 2.5); + alg->setProperty("BeamRadius", 2.5); // outputs alg->setPropertyValue("TotalScatteringWS", "__unused_for_child"); alg->setPropertyValue("MultipleScatteringWS", "__unused_for_child"); From 8afc45c4be650466fad1edf18f1b72e5a9682e80 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Wed, 29 Oct 2014 17:20:11 +0000 Subject: [PATCH 24/37] Add static function to retrieve resolution parameters Follows on from how ConvertToYSpace fills the detector parameters Refs #10169 --- .../MantidCurveFitting/VesuvioResolution.h | 5 ++ .../CurveFitting/src/CalculateMSVesuvio.cpp | 15 +---- .../CurveFitting/src/VesuvioResolution.cpp | 66 +++++++++++-------- 3 files changed, 45 insertions(+), 41 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/VesuvioResolution.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/VesuvioResolution.h index fd9f28d1437b..543363c119e9 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/VesuvioResolution.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/VesuvioResolution.h @@ -55,6 +55,11 @@ namespace CurveFitting class MANTID_CURVEFITTING_DLL VesuvioResolution : public virtual API::ParamFunction, public virtual API::IFunction1D { public: + + /// Creates a POD struct containing the required resolution parameters for this spectrum + static ResolutionParams getResolutionParameters(const API::MatrixWorkspace_const_sptr & ws, + const size_t index); + /// Default constructor required for factory VesuvioResolution(); diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 2a4869291550..217fdf801c73 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -712,19 +712,8 @@ namespace Mantid { // Detector information DetectorParams detpar = ConvertToYSpace::getDetectorParameters(m_inputWS, wsIndex); - // t0 is stored in seconds by default, whereas here we want microseconds - detpar.t0 *= 1e6; - - const Geometry::IDetector_const_sptr detector = m_inputWS->getDetector(wsIndex); - const auto & pmap = m_inputWS->instrumentParameters(); - // Resolution information - ResolutionParams respar; - respar.dl1 = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_l1"); - respar.dl2 = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_l2"); - respar.dtof = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_tof"); - respar.dthe = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_theta"); //radians - respar.dEnLorentz = ConvertToYSpace::getComponentParameter(detector, pmap, "hwhm_lorentz"); - respar.dEnGauss = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_gauss"); + detpar.t0 *= 1e6; // t0 in microseconds here + ResolutionParams respar = VesuvioResolution::getResolutionParameters(m_inputWS, wsIndex); // Final counts averaged over all simulations const size_t nruns(NSIMULATIONS), nscatters(NSCATTERS), nevents(NEVENTS); diff --git a/Code/Mantid/Framework/CurveFitting/src/VesuvioResolution.cpp b/Code/Mantid/Framework/CurveFitting/src/VesuvioResolution.cpp index 7e0a734546ac..6bcf01387e11 100644 --- a/Code/Mantid/Framework/CurveFitting/src/VesuvioResolution.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/VesuvioResolution.cpp @@ -22,8 +22,44 @@ namespace CurveFitting // Register into factory DECLARE_FUNCTION(VesuvioResolution); + //--------------------------------------------------------------------------- + // Static functions + //--------------------------------------------------------------------------- + /** - */ + * @param ws The workspace with attached instrument + * @param index Index of the spectrum + * @return DetectorParams structure containing the relevant parameters + */ + ResolutionParams VesuvioResolution::getResolutionParameters(const API::MatrixWorkspace_const_sptr & ws, + const size_t index) + { + Geometry::IDetector_const_sptr detector; + try + { + detector = ws->getDetector(index); + } + catch (Kernel::Exception::NotFoundError &) + { + throw std::invalid_argument("VesuvioResolution - Workspace has no detector attached to histogram at index " + \ + boost::lexical_cast(index)); + } + + ResolutionParams respar; + const auto & pmap = ws->constInstrumentParameters(); + respar.dl1 = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_l1"); + respar.dl2 = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_l2"); + respar.dtof = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_tof"); + respar.dthe = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_theta"); //radians + respar.dEnLorentz = ConvertToYSpace::getComponentParameter(detector, pmap, "hwhm_lorentz"); + respar.dEnGauss = ConvertToYSpace::getComponentParameter(detector, pmap, "sigma_gauss"); + return respar; + } + + //--------------------------------------------------------------------------- + // Member functions + //--------------------------------------------------------------------------- + VesuvioResolution::VesuvioResolution() : API::ParamFunction(), API::IFunction1D(), m_log("VesuvioResolution"), m_wsIndex(0), m_mass(0.0), m_voigt(), @@ -63,35 +99,9 @@ namespace CurveFitting UNUSED_ARG(startX); UNUSED_ARG(endX); - auto inst = workspace->getInstrument(); - auto sample = inst->getSample(); - auto source = inst->getSource(); - if(!sample || !source) - { - throw std::invalid_argument("VesuvioResolution - Workspace has no source/sample."); - } m_wsIndex = wsIndex; - Geometry::IDetector_const_sptr det; - try - { - det = workspace->getDetector(m_wsIndex); - } - catch (Kernel::Exception::NotFoundError &) - { - throw std::invalid_argument("VesuvioResolution - Workspace has no detector attached to histogram at index " + boost::lexical_cast(m_wsIndex)); - } - DetectorParams detpar = ConvertToYSpace::getDetectorParameters(workspace, m_wsIndex); - const auto & pmap = workspace->constInstrumentParameters(); - - ResolutionParams respar; - respar.dl1 = ConvertToYSpace::getComponentParameter(det, pmap, "sigma_l1"); - respar.dl2 = ConvertToYSpace::getComponentParameter(det, pmap, "sigma_l2"); - respar.dtof = ConvertToYSpace::getComponentParameter(det, pmap, "sigma_tof"); - respar.dthe = ConvertToYSpace::getComponentParameter(det, pmap, "sigma_theta"); //radians - respar.dEnLorentz = ConvertToYSpace::getComponentParameter(det, pmap, "hwhm_lorentz"); - respar.dEnGauss = ConvertToYSpace::getComponentParameter(det, pmap, "sigma_gauss"); - + ResolutionParams respar = getResolutionParameters(workspace, m_wsIndex); this->cacheResolutionComponents(detpar, respar); } From e1ab08d600271303ed283e2303125598ba180228 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Wed, 29 Oct 2014 17:28:08 +0000 Subject: [PATCH 25/37] Refactoring to improve readability Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 3 ++ .../CurveFitting/src/CalculateMSVesuvio.cpp | 54 ++++++++++++------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index ad29ad09c1da..9d2339681b6a 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -152,6 +152,9 @@ namespace Mantid const DetectorParams & detpar, const ResolutionParams &respar, Simulation & simulCounts) const; + void assignToOutput(const size_t nscatters, + const SimulationWithErrors & avgCounts, API::ISpectrum & totalsc, + API::ISpectrum & multsc) const; double calculateCounts(const size_t nscatters, const DetectorParams & detpar, const ResolutionParams &respar, diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 217fdf801c73..781cf435fc39 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -731,7 +731,41 @@ namespace Mantid // Average over all runs and assign to output workspaces SimulationWithErrors avgCounts = accumulator.average(); avgCounts.normalise(); + assignToOutput(nscatters, avgCounts, totalsc, multsc); + } + + /** + * Perform a single simulation of a given number of events for up to a maximum number of + * scatterings on a chosen detector + * @param nevents The number of neutron events to simulate + * @param nscatters Maximum order of scattering that should be simulated + * @param detpar Detector information describing the final detector position + * @param respar Resolution information on the intrument as a whole + * @param simulCounts Simulation object used to storing the calculated number of counts + */ + void CalculateMSVesuvio::simulate(const size_t nevents, const size_t nscatters, + const DetectorParams & detpar, + const ResolutionParams &respar, + Simulation & simulCounts) const + { + for(size_t i = 0; i < nevents; ++i) + { + calculateCounts(nscatters, detpar, respar, simulCounts); + } + } + /** + * Assign the averaged counts to the correct workspaces + * @param nscatters The highest order of scattering + * @param avgCounts Counts & errors separated for each scattering order + * @param totalsc A non-const reference to the spectrum for the total scattering calculation + * @param multsc A non-const reference to the spectrum for the multiple scattering contribution + */ + void + CalculateMSVesuvio::assignToOutput(const size_t nscatters, + const CalculateMSVesuvio::SimulationWithErrors &avgCounts, + API::ISpectrum & totalsc, API::ISpectrum & multsc) const + { // Sum up all multiple scatter events auto & msscatY = multsc.dataY(); auto & msscatE = multsc.dataE(); @@ -757,26 +791,6 @@ namespace Mantid VectorHelper::SumGaussError()); } - /** - * Perform a single simulation of a given number of events for up to a maximum number of - * scatterings on a chosen detector - * @param nevents The number of neutron events to simulate - * @param nscatters Maximum order of scattering that should be simulated - * @param detpar Detector information describing the final detector position - * @param respar Resolution information on the intrument as a whole - * @param simulCounts Simulation object used to storing the calculated number of counts - */ - void CalculateMSVesuvio::simulate(const size_t nevents, const size_t nscatters, - const DetectorParams & detpar, - const ResolutionParams &respar, - Simulation & simulCounts) const - { - for(size_t i = 0; i < nevents; ++i) - { - calculateCounts(nscatters, detpar, respar, simulCounts); - } - } - /** * * @param nscatters Maximum order of scattering that should be simulated From 3365277a0c6885bdef53d65b936007e79f9af065 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Wed, 29 Oct 2014 17:38:41 +0000 Subject: [PATCH 26/37] Add properties to control the number of scatterings, runs & events Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 13 ++-- .../CurveFitting/src/CalculateMSVesuvio.cpp | 70 +++++++++++-------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index 9d2339681b6a..3965ce40bf65 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -148,15 +148,12 @@ namespace Mantid void cacheInputs(); void calculateMS(const size_t wsIndex, API::ISpectrum & totalsc, API::ISpectrum & multsc) const; - void simulate(const size_t nevents, const size_t nscatters, - const DetectorParams & detpar, + void simulate(const DetectorParams & detpar, const ResolutionParams &respar, Simulation & simulCounts) const; - void assignToOutput(const size_t nscatters, - const SimulationWithErrors & avgCounts, API::ISpectrum & totalsc, + void assignToOutput(const SimulationWithErrors & avgCounts, API::ISpectrum & totalsc, API::ISpectrum & multsc) const; - double calculateCounts(const size_t nscatters, - const DetectorParams & detpar, + double calculateCounts(const DetectorParams & detpar, const ResolutionParams &respar, Simulation & simulation) const; @@ -188,6 +185,10 @@ namespace Mantid double m_tmin, m_tmax, m_delt; // min, max & dt TOF value double m_foilRes; // resolution in energy of foil + size_t m_nscatters; // highest order of scattering to generate + size_t m_nruns; // number of runs per spectrum + size_t m_nevents; // number of single events per run + API::Progress *m_progress; API::MatrixWorkspace_sptr m_inputWS; }; diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 781cf435fc39..3ff69122d50b 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -33,9 +33,6 @@ namespace Mantid namespace { - const size_t NSIMULATIONS = 10; - const size_t NEVENTS = 50000; - const size_t NSCATTERS = 3; const size_t MAX_SCATTER_PT_TRIES = 25; /// Conversion constant const double MASS_TO_MEV = 0.5*PhysicalConstants::NeutronMass/PhysicalConstants::meV; @@ -474,6 +471,7 @@ namespace Mantid m_maxWidthSampleFrame(0.0), m_sampleShape(NULL), m_sampleProps(NULL), m_detHeight(-1.0), m_detWidth(-1.0), m_detThick(-1.0), m_tmin(-1.0), m_tmax(-1.0), m_delt(-1.0), m_foilRes(-1.0), + m_nscatters(0), m_nruns(0), m_nevents(0), m_progress(NULL), m_inputWS() { } @@ -528,6 +526,16 @@ namespace Mantid // -- Algorithm -- declareProperty("Seed", 123456789, positiveInt, "Seed the random number generator with this value"); + declareProperty("NumScatters", 3, positiveInt, + "Number of scattering orders to calculate"); + declareProperty("NumRuns", 10, positiveInt, + "Number of simulated runs per spectrum"); + declareProperty("NumEventsPerRun", 50000, positiveInt, + "Number of events per run"); + setPropertyGroup("Seed", "Algorithm"); + setPropertyGroup("NumScatters", "Algorithm"); + setPropertyGroup("NumRuns", "Algorithm"); + setPropertyGroup("NumEventsPerRun", "Algorithm"); // Outputs declareProperty(new WorkspaceProperty<>("TotalScatteringWS","", Direction::Output), @@ -553,7 +561,7 @@ namespace Mantid // Setup progress const int64_t nhist = static_cast(m_inputWS->getNumberHistograms()); - m_progress = new API::Progress(this, 0.0, 1.0, nhist*NSIMULATIONS*2); + m_progress = new API::Progress(this, 0.0, 1.0, nhist*m_nruns*2); for(int64_t i = 0; i < nhist; ++i) { @@ -593,6 +601,14 @@ namespace Mantid */ void CalculateMSVesuvio::cacheInputs() { + // Algorithm + int nscatters = getProperty("NumScatters"); + m_nscatters = static_cast(nscatters); + int nruns = getProperty("NumRuns"); + m_nruns = static_cast(nruns); + int nevents = getProperty("NumEventsPerRun"); + m_nevents = static_cast(nevents); + // -- Geometry -- const auto instrument = m_inputWS->getInstrument(); m_beamDir = instrument->getSample()->getPos() - instrument->getSource()->getPos(); @@ -716,14 +732,15 @@ namespace Mantid ResolutionParams respar = VesuvioResolution::getResolutionParameters(m_inputWS, wsIndex); // Final counts averaged over all simulations - const size_t nruns(NSIMULATIONS), nscatters(NSCATTERS), nevents(NEVENTS); - SimulationAggregator accumulator(nruns); - for(size_t i = 0; i < nruns; ++i) + SimulationAggregator accumulator(m_nruns); + for(size_t i = 0; i < m_nruns; ++i) { m_progress->report("MS calculation: idx=" + boost::lexical_cast(wsIndex) + ", run=" + boost::lexical_cast(i)); - simulate(nevents, nscatters, detpar, respar, - accumulator.newSimulation(nscatters, m_inputWS->blocksize())); + + simulate(detpar, respar, + accumulator.newSimulation(m_nscatters, m_inputWS->blocksize())); + m_progress->report("MS calculation: idx=" + boost::lexical_cast(wsIndex) + ", run=" + boost::lexical_cast(i)); } @@ -731,45 +748,40 @@ namespace Mantid // Average over all runs and assign to output workspaces SimulationWithErrors avgCounts = accumulator.average(); avgCounts.normalise(); - assignToOutput(nscatters, avgCounts, totalsc, multsc); + assignToOutput(avgCounts, totalsc, multsc); } /** * Perform a single simulation of a given number of events for up to a maximum number of * scatterings on a chosen detector - * @param nevents The number of neutron events to simulate - * @param nscatters Maximum order of scattering that should be simulated * @param detpar Detector information describing the final detector position * @param respar Resolution information on the intrument as a whole * @param simulCounts Simulation object used to storing the calculated number of counts */ - void CalculateMSVesuvio::simulate(const size_t nevents, const size_t nscatters, - const DetectorParams & detpar, + void CalculateMSVesuvio::simulate(const DetectorParams & detpar, const ResolutionParams &respar, Simulation & simulCounts) const { - for(size_t i = 0; i < nevents; ++i) + for(size_t i = 0; i < m_nevents; ++i) { - calculateCounts(nscatters, detpar, respar, simulCounts); + calculateCounts(detpar, respar, simulCounts); } } /** * Assign the averaged counts to the correct workspaces - * @param nscatters The highest order of scattering * @param avgCounts Counts & errors separated for each scattering order * @param totalsc A non-const reference to the spectrum for the total scattering calculation * @param multsc A non-const reference to the spectrum for the multiple scattering contribution */ void - CalculateMSVesuvio::assignToOutput(const size_t nscatters, - const CalculateMSVesuvio::SimulationWithErrors &avgCounts, + CalculateMSVesuvio::assignToOutput(const CalculateMSVesuvio::SimulationWithErrors &avgCounts, API::ISpectrum & totalsc, API::ISpectrum & multsc) const { // Sum up all multiple scatter events auto & msscatY = multsc.dataY(); auto & msscatE = multsc.dataE(); - for(size_t i = 1; i < nscatters; ++i) //(i >= 1 for multiple scatters) + for(size_t i = 1; i < m_nscatters; ++i) //(i >= 1 for multiple scatters) { const auto & counts = avgCounts.sim.counts[i]; // equivalent to msscatY[j] += counts[j] @@ -792,14 +804,12 @@ namespace Mantid } /** - * - * @param nscatters Maximum order of scattering that should be simulated * @param detpar Detector information describing the final detector position * @param respar Resolution information on the intrument as a whole * @param simulation [Output] Store the calculated counts here * @return The sum of the weights for all scatters */ - double CalculateMSVesuvio::calculateCounts(const size_t nscatters, const DetectorParams &detpar, + double CalculateMSVesuvio::calculateCounts(const DetectorParams &detpar, const ResolutionParams &respar, Simulation &simulation) const { @@ -814,9 +824,9 @@ namespace Mantid } // track various variables during calculation - std::vector weights(nscatters, 1.0), // start at 1.0 - tofs(nscatters, 0.0), // tof accumulates for each piece of the calculation - en1(nscatters, 0.0); + std::vector weights(m_nscatters, 1.0), // start at 1.0 + tofs(m_nscatters, 0.0), // tof accumulates for each piece of the calculation + en1(m_nscatters, 0.0); const double vel2 = sqrt(detpar.efixed/MASS_TO_MEV); const double t2 = detpar.l2/vel2; @@ -824,8 +834,8 @@ namespace Mantid tofs[0] = generateTOF(en1[0], respar.dtof, respar.dl1); // correction for resolution in l1 // Neutron path - std::vector scatterPts(nscatters), // track origin of each scatter - neutronDirs(nscatters); // neutron directions + std::vector scatterPts(m_nscatters), // track origin of each scatter + neutronDirs(m_nscatters); // neutron directions V3D startPos(srcPos); neutronDirs[0] = m_beamDir; @@ -836,7 +846,7 @@ namespace Mantid tofs[0] += (distFromStart*1e6/vel0); // multiple scatter events within sample, i.e not including zeroth - for(size_t i = 1; i < nscatters; ++i) + for(size_t i = 1; i < m_nscatters; ++i) { weights[i] = weights[i-1]; tofs[i] = tofs[i-1]; @@ -885,7 +895,7 @@ namespace Mantid // force all orders in to current detector const auto & inX = m_inputWS->readX(0); - for(size_t i = 0; i < nscatters; ++i) + for(size_t i = 0; i < m_nscatters; ++i) { double scang(0.0), distToExit(0.0); V3D detPos = generateDetectorPos(detpar.pos, en1[i], scatterPts[i], neutronDirs[i], From 6deac0b8390a9a693cf65b9780d711a043b20dbd Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Wed, 29 Oct 2014 17:43:39 +0000 Subject: [PATCH 27/37] Separate helpers into different file The main algorithm code is easier to read this way. Refs #10169 --- .../Framework/CurveFitting/CMakeLists.txt | 2 + .../MantidCurveFitting/CalculateMSVesuvio.h | 73 +-- .../inc/MantidCurveFitting/MSVesuvioHelpers.h | 76 +++ .../CurveFitting/src/CalculateMSVesuvio.cpp | 439 +----------------- .../CurveFitting/src/MSVesuvioHelpers.cpp | 420 +++++++++++++++++ 5 files changed, 520 insertions(+), 490 deletions(-) create mode 100644 Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/MSVesuvioHelpers.h create mode 100644 Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp diff --git a/Code/Mantid/Framework/CurveFitting/CMakeLists.txt b/Code/Mantid/Framework/CurveFitting/CMakeLists.txt index 403c02e38b34..6fd76d691ebf 100644 --- a/Code/Mantid/Framework/CurveFitting/CMakeLists.txt +++ b/Code/Mantid/Framework/CurveFitting/CMakeLists.txt @@ -57,6 +57,7 @@ set ( SRC_FILES src/LogNormal.cpp src/Lorentzian.cpp src/Lorentzian1D.cpp + src/MSVesuvioHelpers.cpp src/MultiDomainCreator.cpp src/MuonFInteraction.cpp src/NeutronBk2BkExpConvPVoigt.cpp @@ -167,6 +168,7 @@ set ( INC_FILES inc/MantidCurveFitting/LogNormal.h inc/MantidCurveFitting/Lorentzian.h inc/MantidCurveFitting/Lorentzian1D.h + inc/MantidCurveFitting/MSVesuvioHelpers.h inc/MantidCurveFitting/MultiDomainCreator.h inc/MantidCurveFitting/MuonFInteraction.h inc/MantidCurveFitting/NeutronBk2BkExpConvPVoigt.h diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index 3965ce40bf65..89080ea516ec 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -7,10 +7,6 @@ #include "MantidGeometry/Instrument/ReferenceFrame.h" #include "MantidKernel/V3D.h" -#include -#include -#include - namespace Mantid { namespace CurveFitting @@ -20,6 +16,12 @@ namespace Mantid //----------------------------------------------------------------------------- struct DetectorParams; struct ResolutionParams; + namespace MSVesuvioHelper + { + class RandomNumberGenerator; + struct Simulation; + struct SimulationWithErrors; + } /** Calculates the multiple scattering & total scattering contributions @@ -49,7 +51,6 @@ namespace Mantid class DLLExport CalculateMSVesuvio : public API::Algorithm { private: - // Holds date on the compton scattering properties of an atom struct ComptonNeutronAtom { @@ -59,7 +60,6 @@ namespace Mantid double sclength; // 4pi/xsec double profile; // s.d of J(y) }; - // Holds data about sample as a whole. struct SampleComptonProperties { @@ -73,57 +73,6 @@ namespace Mantid double mu; // attenuation factor (1/m) }; - // Produces random numbers with various probability distributions - class RandomNumberGenerator - { - typedef boost::uniform_real uniform_double; - typedef boost::normal_distribution gaussian_double; - public: - RandomNumberGenerator(const int seed); - /// Returns a flat random number between 0.0 & 1.0 - double flat(); - /// Returns a random number distributed by a normal distribution - double gaussian(const double mean, const double sigma); - - private: - RandomNumberGenerator(); - boost::mt19937 m_generator; - }; - - // Stores counts for each scatter order - // for a "run" of a given number of events - struct Simulation - { - Simulation(const size_t order, const size_t ntimes); - - std::vector> counts; - size_t maxorder; - }; - // Stores counts for each scatter order with errors - struct SimulationWithErrors - { - SimulationWithErrors(const size_t order, const size_t ntimes) : - sim(order, ntimes), errors(order, std::vector(ntimes)) {} - - void normalise(); - Simulation sim; - std::vector> errors; - - }; - - // Accumulates and averages the results of a set of simulations - struct SimulationAggregator - { - SimulationAggregator(const size_t nruns); - - // Creates a placeholder for a new simulation - Simulation & newSimulation(const size_t order, - const size_t ntimes); - SimulationWithErrors average() const; - - std::vector results; - }; - public: CalculateMSVesuvio(); ~CalculateMSVesuvio(); @@ -150,12 +99,12 @@ namespace Mantid API::ISpectrum & multsc) const; void simulate(const DetectorParams & detpar, const ResolutionParams &respar, - Simulation & simulCounts) const; - void assignToOutput(const SimulationWithErrors & avgCounts, API::ISpectrum & totalsc, - API::ISpectrum & multsc) const; + MSVesuvioHelper::Simulation & simulCounts) const; + void assignToOutput(const MSVesuvioHelper::SimulationWithErrors & avgCounts, + API::ISpectrum & totalsc, API::ISpectrum & multsc) const; double calculateCounts(const DetectorParams & detpar, const ResolutionParams &respar, - Simulation & simulation) const; + MSVesuvioHelper::Simulation & simulation) const; // single-event helpers Kernel::V3D generateSrcPos(const double l1) const; @@ -172,7 +121,7 @@ namespace Mantid double generateE1(const double angle, const double e1nom, const double e1res) const; // Member Variables - RandomNumberGenerator *m_randgen; // random number generator + MSVesuvioHelper::RandomNumberGenerator *m_randgen; // random number generator size_t m_acrossIdx, m_upIdx, m_beamIdx; // indices of each direction Kernel::V3D m_beamDir; // Directional vector for beam diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/MSVesuvioHelpers.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/MSVesuvioHelpers.h new file mode 100644 index 000000000000..89cfe9088d31 --- /dev/null +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/MSVesuvioHelpers.h @@ -0,0 +1,76 @@ +#ifndef MANTID_CURVEFITTING_MSVESUVIOHELPERS_H +#define MANTID_CURVEFITTING_MSVESUVIOHELPERS_H + +#include + +#include +#include +#include + + +namespace Mantid { namespace CurveFitting +{ + namespace MSVesuvioHelper + { + // Absorption energy for double-difference gold foil + double finalEnergyAuDD(const double randv); + // Absorption energy for gold foil YAP + double finalEnergyAuYap(const double randv); + // Absorption energy for uranium + double finalEnergyUranium(const double randv); + + // Ties together random numbers with various probability distributions + // @todo: Should move to Kernel + class RandomNumberGenerator + { + typedef boost::uniform_real uniform_double; + typedef boost::normal_distribution gaussian_double; + public: + RandomNumberGenerator(const int seed); + /// Returns a flat random number between 0.0 & 1.0 + double flat(); + /// Returns a random number distributed by a normal distribution + double gaussian(const double mean, const double sigma); + + private: + RandomNumberGenerator(); + boost::mt19937 m_generator; + }; + + // Stores counts for each scatter order + // for a "run" of a given number of events + struct Simulation + { + Simulation(const size_t order, const size_t ntimes); + + std::vector> counts; + size_t maxorder; + }; + // Stores counts for each scatter order with errors + struct SimulationWithErrors + { + SimulationWithErrors(const size_t order, const size_t ntimes) : + sim(order, ntimes), errors(order, std::vector(ntimes)) {} + + void normalise(); + Simulation sim; + std::vector> errors; + }; + + // Accumulates and averages the results of a set of simulations + struct SimulationAggregator + { + SimulationAggregator(const size_t nruns); + + // Creates a placeholder for a new simulation + Simulation & newSimulation(const size_t order, + const size_t ntimes); + SimulationWithErrors average() const; + + std::vector results; + }; + + } +}} + +#endif // MANTID_CURVEFITTING_MSVESUVIOHELPERS_H diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index 3ff69122d50b..ef42ec2f43a3 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -4,6 +4,7 @@ #include "MantidCurveFitting/CalculateMSVesuvio.h" // Use helpers for storing detector/resolution parameters #include "MantidCurveFitting/ConvertToYSpace.h" +#include "MantidCurveFitting/MSVesuvioHelpers.h" #include "MantidCurveFitting/VesuvioResolution.h" #include "MantidAPI/SampleShapeValidator.h" @@ -36,428 +37,10 @@ namespace Mantid const size_t MAX_SCATTER_PT_TRIES = 25; /// Conversion constant const double MASS_TO_MEV = 0.5*PhysicalConstants::NeutronMass/PhysicalConstants::meV; - - /** - * Generate the final energy of a neutron for gold foil analyser at 293K - * in double-difference mode: - * - THIN FOIL NUMBER DENSITY = 1.456E20 ATOMS/SQ CM. - * - THICK FOIL NUMBER DENSITY = 3.0* 1.456E20 ATOMS/SQ CM. - * @param randv A random number between 0.0 & 1.0, sample from a flat distribution - * @return A value to use for the final energy - */ - double finalEnergyAuDD(const double randv) - { - // Tabulated values of absoprtion energies from S.F. Mughabghab, Neutron Cross Sections, Academic - // Press, Orlando, Florida, 1984. - const double ENERGIES[300] = {\ - 2000.0, 2020.7, 2041.5, 2062.2, 2082.9, 2103.7, 2124.4, 2145.2, 2165.9, 2186.6, 2207.4, 2228.1, - 2248.8, 2269.6, 2290.3, 2311.0, 2331.8, 2352.5, 2373.2, 2394.0, 2414.7, 2435.5, 2456.2, 2476.9, - 2497.7, 2518.4, 2539.1, 2559.9, 2580.6, 2601.3, 2622.1, 2642.8, 2663.5, 2684.3, 2705.0, 2725.8, - 2746.5, 2767.2, 2788.0, 2808.7, 2829.4, 2850.2, 2870.9, 2891.6, 2912.4, 2933.1, 2953.8, 2974.6, - 2995.3, 3016.1, 3036.8, 3057.5, 3078.3, 3099.0, 3119.7, 3140.5, 3161.2, 3181.9, 3202.7, 3223.4, - 3244.1, 3264.9, 3285.6, 3306.4, 3327.1, 3347.8, 3368.6, 3389.3, 3410.0, 3430.8, 3451.5, 3472.2, - 3493.0, 3513.7, 3534.4, 3555.2, 3575.9, 3596.7, 3617.4, 3638.1, 3658.9, 3679.6, 3700.3, 3721.1, - 3741.8, 3762.5, 3783.3, 3804.0, 3824.7, 3845.5, 3866.2, 3887.0, 3907.7, 3928.4, 3949.2, 3969.9, - 3990.6, 4011.4, 4032.1, 4052.8, 4073.6, 4094.3, 4115.1, 4135.8, 4156.5, 4177.3, 4198.0, 4218.7, - 4239.5, 4260.2, 4280.9, 4301.7, 4322.4, 4343.1, 4363.9, 4384.6, 4405.4, 4426.1, 4446.8, 4467.6, - 4488.3, 4509.0, 4529.8, 4550.5, 4571.2, 4592.0, 4612.7, 4633.4, 4654.2, 4674.9, 4695.7, 4716.4, - 4737.1, 4757.9, 4778.6, 4799.3, 4820.1, 4840.8, 4861.5, 4882.3, 4903.0, 4923.7, 4944.5, 4965.2, - 4986.0, 5006.7, 5027.4, 5048.2, 5068.9, 5089.6, 5110.4, 5131.1, 5151.8, 5172.6, 5193.3, 5214.0, - 5234.8, 5255.5, 5276.3, 5297.0, 5317.7, 5338.5, 5359.2, 5379.9, 5400.7, 5421.4, 5442.1, 5462.9, - 5483.6, 5504.3, 5525.1, 5545.8, 5566.6, 5587.3, 5608.0, 5628.8, 5649.5, 5670.2, 5691.0, 5711.7, - 5732.4, 5753.2, 5773.9, 5794.6, 5815.4, 5836.1, 5856.9, 5877.6, 5898.3, 5919.1, 5939.8, 5960.5, - 5981.3, 6002.0, 6022.7, 6043.5, 6064.2, 6085.0, 6105.7, 6126.4, 6147.2, 6167.9, 6188.6, 6209.4, - 6230.1, 6250.8, 6271.6, 6292.3, 6313.0, 6333.8, 6354.5, 6375.3, 6396.0, 6416.7, 6437.5, 6458.2, - 6478.9, 6499.7, 6520.4, 6541.1, 6561.9, 6582.6, 6603.3, 6624.1, 6644.8, 6665.6, 6686.3, 6707.0, - 6727.8, 6748.5, 6769.2, 6790.0, 6810.7, 6831.4, 6852.2, 6872.9, 6893.6, 6914.4, 6935.1, 6955.9, - 6976.6, 6997.3, 7018.1, 7038.8, 7059.5, 7080.3, 7101.0, 7121.7, 7142.5, 7163.2, 7183.9, 7204.7, - 7225.4, 7246.2, 7266.9, 7287.6, 7308.4, 7329.1, 7349.8, 7370.6, 7391.3, 7412.0, 7432.8, 7453.5, - 7474.2, 7495.0, 7515.7, 7536.5, 7557.2, 7577.9, 7598.7, 7619.4, 7640.1, 7660.9, 7681.6, 7702.3, - 7723.1, 7743.8, 7764.5, 7785.3, 7806.0, 7826.8, 7847.5, 7868.2, 7889.0, 7909.7, 7930.4, 7951.2, - 7971.9, 7992.6, 8013.4, 8034.1, 8054.8, 8075.6, 8096.3, 8117.1, 8137.8, 8158.5, 8179.3, 8200.0 - }; - - const double XVALUES[300] = {\ - 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, - 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, - 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00010, 0.00010, - 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, - 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, - 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, - 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, 0.00030, 0.00030, 0.00030, 0.00030, 0.00030, 0.00030, - 0.00030, 0.00030, 0.00040, 0.00040, 0.00040, 0.00040, 0.00040, 0.00050, 0.00050, 0.00050, 0.00050, 0.00060, - 0.00060, 0.00070, 0.00070, 0.00070, 0.00080, 0.00090, 0.00090, 0.00100, 0.00110, 0.00110, 0.00120, 0.00130, - 0.00150, 0.00160, 0.00170, 0.00190, 0.00210, 0.00230, 0.00260, 0.00290, 0.00320, 0.00360, 0.00410, 0.00470, - 0.00540, 0.00620, 0.00720, 0.00840, 0.00990, 0.01180, 0.01420, 0.01740, 0.02140, 0.02680, 0.03410, 0.04400, - 0.05770, 0.07680, 0.10360, 0.14050, 0.18960, 0.25110, 0.32310, 0.40240, 0.48540, 0.56870, 0.64930, 0.72370, - 0.78850, 0.84150, 0.88240, 0.91260, 0.93440, 0.95000, 0.96130, 0.96960, 0.97570, 0.98030, 0.98380, 0.98650, - 0.98870, 0.99040, 0.99180, 0.99290, 0.99380, 0.99460, 0.99520, 0.99580, 0.99620, 0.99660, 0.99700, 0.99730, - 0.99750, 0.99770, 0.99790, 0.99810, 0.99830, 0.99840, 0.99850, 0.99860, 0.99870, 0.99880, 0.99890, 0.99900, - 0.99900, 0.99910, 0.99910, 0.99920, 0.99920, 0.99930, 0.99930, 0.99940, 0.99940, 0.99940, 0.99940, 0.99950, - 0.99950, 0.99950, 0.99950, 0.99960, 0.99960, 0.99960, 0.99960, 0.99960, 0.99960, 0.99970, 0.99970, 0.99970, - 0.99970, 0.99970, 0.99970, 0.99970, 0.99970, 0.99970, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, - 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99990, 0.99990, - 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, - 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, - 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, - 0.99990, 0.99990, 0.99990, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, - 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, - 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000 - }; - - for(size_t i = 0; i < 299; ++i) - { - if(XVALUES[i] < randv && XVALUES[i+1] > randv) - { - double ef = ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); - if(ef < 100.0) ef = 0.0; - return ef; - } - } - return 0.0; - - - } - - /** - * Generate the final energy of a neutron for gold foil analyser at 293K - * with number density of 7.35E19 atoms/cm^2 in yap difference mode. - * @param randv A random number between 0.0 & 1.0, sample from a flat distribution - * @return A value to use for the final energy - */ - double finalEnergyAuYap(const double randv) - { - // Tabulated values of absoprtion energies from S.F. Mughabghab, Neutron Cross Sections, Academic - // Press, Orlando, Florida, 1984. - const double ENERGIES[600] = {\ - 4000.0, 4003.3, 4006.7, 4010.0, 4013.4, 4016.7, 4020.0, 4023.4, 4026.7, 4030.1, 4033.4, 4036.7, - 4040.1, 4043.4, 4046.7, 4050.1, 4053.4, 4056.8, 4060.1, 4063.4, 4066.8, 4070.1, 4073.5, 4076.8, - 4080.1, 4083.5, 4086.8, 4090.2, 4093.5, 4096.8, 4100.2, 4103.5, 4106.8, 4110.2, 4113.5, 4116.9, - 4120.2, 4123.5, 4126.9, 4130.2, 4133.6, 4136.9, 4140.2, 4143.6, 4146.9, 4150.3, 4153.6, 4156.9, - 4160.3, 4163.6, 4166.9, 4170.3, 4173.6, 4177.0, 4180.3, 4183.6, 4187.0, 4190.3, 4193.7, 4197.0, - 4200.3, 4203.7, 4207.0, 4210.4, 4213.7, 4217.0, 4220.4, 4223.7, 4227.0, 4230.4, 4233.7, 4237.1, - 4240.4, 4243.7, 4247.1, 4250.4, 4253.8, 4257.1, 4260.4, 4263.8, 4267.1, 4270.5, 4273.8, 4277.1, - 4280.5, 4283.8, 4287.1, 4290.5, 4293.8, 4297.2, 4300.5, 4303.8, 4307.2, 4310.5, 4313.9, 4317.2, - 4320.5, 4323.9, 4327.2, 4330.6, 4333.9, 4337.2, 4340.6, 4343.9, 4347.2, 4350.6, 4353.9, 4357.3, - 4360.6, 4363.9, 4367.3, 4370.6, 4374.0, 4377.3, 4380.6, 4384.0, 4387.3, 4390.7, 4394.0, 4397.3, - 4400.7, 4404.0, 4407.3, 4410.7, 4414.0, 4417.4, 4420.7, 4424.0, 4427.4, 4430.7, 4434.1, 4437.4, - 4440.7, 4444.1, 4447.4, 4450.8, 4454.1, 4457.4, 4460.8, 4464.1, 4467.4, 4470.8, 4474.1, 4477.5, - 4480.8, 4484.1, 4487.5, 4490.8, 4494.2, 4497.5, 4500.8, 4504.2, 4507.5, 4510.9, 4514.2, 4517.5, - 4520.9, 4524.2, 4527.5, 4530.9, 4534.2, 4537.6, 4540.9, 4544.2, 4547.6, 4550.9, 4554.3, 4557.6, - 4560.9, 4564.3, 4567.6, 4571.0, 4574.3, 4577.6, 4581.0, 4584.3, 4587.6, 4591.0, 4594.3, 4597.7, - 4601.0, 4604.3, 4607.7, 4611.0, 4614.4, 4617.7, 4621.0, 4624.4, 4627.7, 4631.1, 4634.4, 4637.7, - 4641.1, 4644.4, 4647.7, 4651.1, 4654.4, 4657.8, 4661.1, 4664.4, 4667.8, 4671.1, 4674.5, 4677.8, - 4681.1, 4684.5, 4687.8, 4691.2, 4694.5, 4697.8, 4701.2, 4704.5, 4707.8, 4711.2, 4714.5, 4717.9, - 4721.2, 4724.5, 4727.9, 4731.2, 4734.6, 4737.9, 4741.2, 4744.6, 4747.9, 4751.3, 4754.6, 4757.9, - 4761.3, 4764.6, 4767.9, 4771.3, 4774.6, 4778.0, 4781.3, 4784.6, 4788.0, 4791.3, 4794.7, 4798.0, - 4801.3, 4804.7, 4808.0, 4811.4, 4814.7, 4818.0, 4821.4, 4824.7, 4828.0, 4831.4, 4834.7, 4838.1, - 4841.4, 4844.7, 4848.1, 4851.4, 4854.8, 4858.1, 4861.4, 4864.8, 4868.1, 4871.5, 4874.8, 4878.1, - 4881.5, 4884.8, 4888.1, 4891.5, 4894.8, 4898.2, 4901.5, 4904.8, 4908.2, 4911.5, 4914.9, 4918.2, - 4921.5, 4924.9, 4928.2, 4931.6, 4934.9, 4938.2, 4941.6, 4944.9, 4948.2, 4951.6, 4954.9, 4958.3, - 4961.6, 4964.9, 4968.3, 4971.6, 4975.0, 4978.3, 4981.6, 4985.0, 4988.3, 4991.7, 4995.0, 4998.3, - 5001.7, 5005.0, 5008.3, 5011.7, 5015.0, 5018.4, 5021.7, 5025.0, 5028.4, 5031.7, 5035.1, 5038.4, - 5041.7, 5045.1, 5048.4, 5051.8, 5055.1, 5058.4, 5061.8, 5065.1, 5068.4, 5071.8, 5075.1, 5078.5, - 5081.8, 5085.1, 5088.5, 5091.8, 5095.2, 5098.5, 5101.8, 5105.2, 5108.5, 5111.9, 5115.2, 5118.5, - 5121.9, 5125.2, 5128.5, 5131.9, 5135.2, 5138.6, 5141.9, 5145.2, 5148.6, 5151.9, 5155.3, 5158.6, - 5161.9, 5165.3, 5168.6, 5172.0, 5175.3, 5178.6, 5182.0, 5185.3, 5188.6, 5192.0, 5195.3, 5198.7, - 5202.0, 5205.3, 5208.7, 5212.0, 5215.4, 5218.7, 5222.0, 5225.4, 5228.7, 5232.1, 5235.4, 5238.7, - 5242.1, 5245.4, 5248.7, 5252.1, 5255.4, 5258.8, 5262.1, 5265.4, 5268.8, 5272.1, 5275.5, 5278.8, - 5282.1, 5285.5, 5288.8, 5292.2, 5295.5, 5298.8, 5302.2, 5305.5, 5308.8, 5312.2, 5315.5, 5318.9, - 5322.2, 5325.5, 5328.9, 5332.2, 5335.6, 5338.9, 5342.2, 5345.6, 5348.9, 5352.3, 5355.6, 5358.9, - 5362.3, 5365.6, 5368.9, 5372.3, 5375.6, 5379.0, 5382.3, 5385.6, 5389.0, 5392.3, 5395.7, 5399.0, - 5402.3, 5405.7, 5409.0, 5412.4, 5415.7, 5419.0, 5422.4, 5425.7, 5429.0, 5432.4, 5435.7, 5439.1, - 5442.4, 5445.7, 5449.1, 5452.4, 5455.8, 5459.1, 5462.4, 5465.8, 5469.1, 5472.5, 5475.8, 5479.1, - 5482.5, 5485.8, 5489.1, 5492.5, 5495.8, 5499.2, 5502.5, 5505.8, 5509.2, 5512.5, 5515.9, 5519.2, - 5522.5, 5525.9, 5529.2, 5532.6, 5535.9, 5539.2, 5542.6, 5545.9, 5549.2, 5552.6, 5555.9, 5559.3, - 5562.6, 5565.9, 5569.3, 5572.6, 5576.0, 5579.3, 5582.6, 5586.0, 5589.3, 5592.7, 5596.0, 5599.3, - 5602.7, 5606.0, 5609.3, 5612.7, 5616.0, 5619.4, 5622.7, 5626.0, 5629.4, 5632.7, 5636.1, 5639.4, - 5642.7, 5646.1, 5649.4, 5652.8, 5656.1, 5659.4, 5662.8, 5666.1, 5669.4, 5672.8, 5676.1, 5679.5, - 5682.8, 5686.1, 5689.5, 5692.8, 5696.2, 5699.5, 5702.8, 5706.2, 5709.5, 5712.9, 5716.2, 5719.5, - 5722.9, 5726.2, 5729.5, 5732.9, 5736.2, 5739.6, 5742.9, 5746.2, 5749.6, 5752.9, 5756.3, 5759.6, - 5762.9, 5766.3, 5769.6, 5773.0, 5776.3, 5779.6, 5783.0, 5786.3, 5789.6, 5793.0, 5796.3, 5799.7, - 5803.0, 5806.3, 5809.7, 5813.0, 5816.4, 5819.7, 5823.0, 5826.4, 5829.7, 5833.1, 5836.4, 5839.7, - 5843.1, 5846.4, 5849.7, 5853.1, 5856.4, 5859.8, 5863.1, 5866.4, 5869.8, 5873.1, 5876.5, 5879.8, - 5883.1, 5886.5, 5889.8, 5893.2, 5896.5, 5899.8, 5903.2, 5906.5, 5909.8, 5913.2, 5916.5, 5919.9, - 5923.2, 5926.5, 5929.9, 5933.2, 5936.6, 5939.9, 5943.2, 5946.6, 5949.9, 5953.3, 5956.6, 5959.9, - 5963.3, 5966.6, 5970.0, 5973.3, 5976.6, 5980.0, 5983.3, 5986.6, 5990.0, 5993.3, 5996.7, 6000.0 - }; - - const double XVALUES[600] = {\ - 0.00000, 0.00000, 0.00000, 0.00002, 0.00003, 0.00003, 0.00004, 0.00005, 0.00005, 0.00006, 0.00007, 0.00007, - 0.00008, 0.00009, 0.00010, 0.00010, 0.00011, 0.00012, 0.00013, 0.00014, 0.00015, 0.00015, 0.00016, 0.00017, - 0.00018, 0.00019, 0.00020, 0.00021, 0.00022, 0.00023, 0.00024, 0.00025, 0.00026, 0.00027, 0.00028, 0.00029, - 0.00030, 0.00031, 0.00032, 0.00033, 0.00034, 0.00035, 0.00037, 0.00038, 0.00039, 0.00040, 0.00041, 0.00043, - 0.00044, 0.00045, 0.00047, 0.00048, 0.00049, 0.00051, 0.00052, 0.00054, 0.00055, 0.00057, 0.00058, 0.00060, - 0.00061, 0.00063, 0.00065, 0.00066, 0.00068, 0.00070, 0.00072, 0.00074, 0.00075, 0.00077, 0.00079, 0.00081, - 0.00083, 0.00085, 0.00087, 0.00089, 0.00092, 0.00094, 0.00096, 0.00098, 0.00101, 0.00103, 0.00106, 0.00108, - 0.00111, 0.00113, 0.00116, 0.00118, 0.00121, 0.00124, 0.00127, 0.00130, 0.00133, 0.00136, 0.00139, 0.00142, - 0.00146, 0.00149, 0.00152, 0.00156, 0.00159, 0.00163, 0.00167, 0.00171, 0.00174, 0.00178, 0.00182, 0.00187, - 0.00191, 0.00195, 0.00200, 0.00204, 0.00209, 0.00214, 0.00219, 0.00224, 0.00229, 0.00235, 0.00240, 0.00246, - 0.00251, 0.00257, 0.00263, 0.00269, 0.00276, 0.00282, 0.00289, 0.00296, 0.00303, 0.00310, 0.00318, 0.00325, - 0.00333, 0.00341, 0.00349, 0.00358, 0.00367, 0.00376, 0.00385, 0.00394, 0.00404, 0.00414, 0.00425, 0.00435, - 0.00446, 0.00458, 0.00469, 0.00481, 0.00494, 0.00507, 0.00520, 0.00533, 0.00548, 0.00562, 0.00577, 0.00592, - 0.00608, 0.00625, 0.00642, 0.00659, 0.00677, 0.00696, 0.00716, 0.00736, 0.00757, 0.00778, 0.00800, 0.00823, - 0.00847, 0.00872, 0.00898, 0.00924, 0.00952, 0.00980, 0.01010, 0.01041, 0.01073, 0.01106, 0.01141, 0.01177, - 0.01214, 0.01253, 0.01293, 0.01335, 0.01379, 0.01425, 0.01472, 0.01522, 0.01573, 0.01627, 0.01683, 0.01742, - 0.01803, 0.01867, 0.01934, 0.02004, 0.02077, 0.02154, 0.02234, 0.02317, 0.02405, 0.02497, 0.02594, 0.02695, - 0.02801, 0.02913, 0.03030, 0.03153, 0.03282, 0.03419, 0.03561, 0.03712, 0.03871, 0.04037, 0.04213, 0.04398, - 0.04594, 0.04799, 0.05017, 0.05246, 0.05488, 0.05743, 0.06013, 0.06297, 0.06598, 0.06915, 0.07251, 0.07605, - 0.07979, 0.08374, 0.08791, 0.09230, 0.09694, 0.10183, 0.10698, 0.11241, 0.11812, 0.12411, 0.13041, 0.13703, - 0.14395, 0.15119, 0.15877, 0.16667, 0.17490, 0.18347, 0.19237, 0.20159, 0.21114, 0.22100, 0.23117, 0.24164, - 0.25240, 0.26344, 0.27473, 0.28628, 0.29807, 0.31007, 0.32228, 0.33468, 0.34725, 0.35999, 0.37286, 0.38586, - 0.39898, 0.41219, 0.42549, 0.43886, 0.45228, 0.46575, 0.47925, 0.49277, 0.50628, 0.51980, 0.53329, 0.54674, - 0.56016, 0.57350, 0.58677, 0.59996, 0.61304, 0.62600, 0.63883, 0.65152, 0.66403, 0.67638, 0.68853, 0.70048, - 0.71220, 0.72369, 0.73492, 0.74590, 0.75659, 0.76700, 0.77711, 0.78691, 0.79640, 0.80557, 0.81442, 0.82294, - 0.83113, 0.83900, 0.84654, 0.85376, 0.86067, 0.86726, 0.87355, 0.87954, 0.88525, 0.89067, 0.89583, 0.90073, - 0.90537, 0.90979, 0.91398, 0.91794, 0.92170, 0.92527, 0.92865, 0.93185, 0.93489, 0.93776, 0.94049, 0.94307, - 0.94552, 0.94784, 0.95005, 0.95213, 0.95412, 0.95600, 0.95779, 0.95949, 0.96110, 0.96264, 0.96410, 0.96549, - 0.96681, 0.96807, 0.96927, 0.97041, 0.97150, 0.97254, 0.97353, 0.97447, 0.97538, 0.97624, 0.97706, 0.97785, - 0.97860, 0.97933, 0.98002, 0.98068, 0.98131, 0.98192, 0.98250, 0.98306, 0.98359, 0.98411, 0.98460, 0.98507, - 0.98553, 0.98596, 0.98638, 0.98679, 0.98718, 0.98755, 0.98791, 0.98826, 0.98859, 0.98892, 0.98923, 0.98953, - 0.98981, 0.99009, 0.99036, 0.99062, 0.99087, 0.99111, 0.99135, 0.99158, 0.99179, 0.99201, 0.99221, 0.99241, - 0.99260, 0.99279, 0.99296, 0.99314, 0.99331, 0.99347, 0.99363, 0.99378, 0.99393, 0.99408, 0.99422, 0.99435, - 0.99448, 0.99461, 0.99473, 0.99486, 0.99497, 0.99509, 0.99520, 0.99530, 0.99541, 0.99551, 0.99561, 0.99571, - 0.99580, 0.99589, 0.99598, 0.99607, 0.99615, 0.99623, 0.99631, 0.99639, 0.99647, 0.99654, 0.99661, 0.99668, - 0.99675, 0.99682, 0.99688, 0.99694, 0.99701, 0.99707, 0.99713, 0.99718, 0.99724, 0.99729, 0.99735, 0.99740, - 0.99745, 0.99750, 0.99755, 0.99760, 0.99764, 0.99769, 0.99773, 0.99778, 0.99782, 0.99786, 0.99790, 0.99794, - 0.99798, 0.99802, 0.99806, 0.99809, 0.99813, 0.99816, 0.99820, 0.99823, 0.99827, 0.99830, 0.99833, 0.99836, - 0.99839, 0.99842, 0.99845, 0.99848, 0.99850, 0.99853, 0.99856, 0.99859, 0.99861, 0.99864, 0.99866, 0.99869, - 0.99871, 0.99873, 0.99876, 0.99878, 0.99880, 0.99882, 0.99884, 0.99886, 0.99889, 0.99891, 0.99893, 0.99895, - 0.99896, 0.99898, 0.99900, 0.99902, 0.99904, 0.99906, 0.99907, 0.99909, 0.99911, 0.99912, 0.99914, 0.99915, - 0.99917, 0.99919, 0.99920, 0.99922, 0.99923, 0.99924, 0.99926, 0.99927, 0.99929, 0.99930, 0.99931, 0.99933, - 0.99934, 0.99935, 0.99936, 0.99938, 0.99939, 0.99940, 0.99941, 0.99942, 0.99943, 0.99944, 0.99946, 0.99947, - 0.99948, 0.99949, 0.99950, 0.99951, 0.99952, 0.99953, 0.99954, 0.99955, 0.99956, 0.99956, 0.99957, 0.99958, - 0.99959, 0.99960, 0.99961, 0.99962, 0.99963, 0.99963, 0.99964, 0.99965, 0.99966, 0.99967, 0.99967, 0.99968, - 0.99969, 0.99970, 0.99970, 0.99971, 0.99972, 0.99973, 0.99973, 0.99974, 0.99975, 0.99975, 0.99976, 0.99977, - 0.99977, 0.99978, 0.99979, 0.99979, 0.99980, 0.99980, 0.99981, 0.99982, 0.99982, 0.99983, 0.99983, 0.99984, - 0.99984, 0.99985, 0.99985, 0.99986, 0.99986, 0.99987, 0.99988, 0.99988, 0.99989, 0.99989, 0.99990, 0.99990, - 0.99990, 0.99991, 0.99991, 0.99992, 0.99992, 0.99993, 0.99993, 0.99994, 0.99994, 0.99994, 0.99995, 0.99995, - 0.99996, 0.99996, 0.99997, 0.99997, 0.99997, 0.99998, 0.99998, 0.99998, 0.99999, 0.99999, 1.00000, 1.00000 - }; - - for(size_t i = 0; i < 599; ++i) - { - if(XVALUES[i] < randv && XVALUES[i+1] > randv) - { - return ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); - } - } - return 0.0; - } - - /** - * Generate the final energy of a neutron for uranium foil analyser at 293K - * with number density of 1.456E20 atoms/cm^2 in double-difference mode. - * @param randv A random number between 0.0 & 1.0, sample from a flat distribution - * @return A value to use for the final energy - */ - double finalEnergyUranium(const double randv) - { - const double ENERGIES[201] = {\ - 5959.0, 5967.7, 5976.4, 5985.1, 5993.8, 6002.5, 6011.2, 6019.9, 6028.6, 6037.3, 6046.0, 6054.8, - 6063.5, 6072.2, 6080.9, 6089.6, 6098.3, 6107.0, 6115.7, 6124.4, 6133.1, 6141.8, 6150.5, 6159.2, - 6167.9, 6176.6, 6185.3, 6194.0, 6202.7, 6211.4, 6220.1, 6228.9, 6237.6, 6246.3, 6255.0, 6263.7, - 6272.4, 6281.1, 6289.8, 6298.5, 6307.2, 6315.9, 6324.6, 6333.3, 6342.0, 6350.7, 6359.4, 6368.1, - 6376.8, 6385.5, 6394.3, 6403.0, 6411.7, 6420.4, 6429.1, 6437.8, 6446.5, 6455.2, 6463.9, 6472.6, - 6481.3, 6490.0, 6498.7, 6507.4, 6516.1, 6524.8, 6533.5, 6542.2, 6550.9, 6559.6, 6568.4, 6577.1, - 6585.8, 6594.5, 6603.2, 6611.9, 6620.6, 6629.3, 6638.0, 6646.7, 6655.4, 6664.1, 6672.8, 6681.5, - 6690.2, 6698.9, 6707.6, 6716.3, 6725.0, 6733.7, 6742.5, 6751.2, 6759.9, 6768.6, 6777.3, 6786.0, - 6794.7, 6803.4, 6812.1, 6820.8, 6829.5, 6838.2, 6846.9, 6855.6, 6864.3, 6873.0, 6881.7, 6890.4, - 6899.1, 6907.8, 6916.5, 6925.3, 6934.0, 6942.7, 6951.4, 6960.1, 6968.8, 6977.5, 6986.2, 6994.9, - 7003.6, 7012.3, 7021.0, 7029.7, 7038.4, 7047.1, 7055.8, 7064.5, 7073.2, 7081.9, 7090.6, 7099.4, - 7108.1, 7116.8, 7125.5, 7134.2, 7142.9, 7151.6, 7160.3, 7169.0, 7177.7, 7186.4, 7195.1, 7203.8, - 7212.5, 7221.2, 7229.9, 7238.6, 7247.3, 7256.0, 7264.8, 7273.5, 7282.2, 7290.9, 7299.6, 7308.3, - 7317.0, 7325.7, 7334.4, 7343.1, 7351.8, 7360.5, 7369.2, 7377.9, 7386.6, 7395.3, 7404.0, 7412.7, - 7421.4, 7430.1, 7438.9, 7447.6, 7456.3, 7465.0, 7473.7, 7482.4, 7491.1, 7499.8, 7508.5, 7517.2, - 7525.9, 7534.6, 7543.3, 7552.0, 7560.7, 7569.4, 7578.1, 7586.8, 7595.5, 7604.2, 7613.0, 7621.7, - 7630.4, 7639.1, 7647.8, 7656.5, 7665.2, 7673.9, 7682.6, 7691.3, 7700.0 - }; - - const double XVALUES[201] = {\ - 0.00000, 0.00000, 0.00000, 0.00020, 0.00030, 0.00040, 0.00050, 0.00060, 0.00070, 0.00080, 0.00090, 0.00110, - 0.00120, 0.00140, 0.00150, 0.00170, 0.00190, 0.00210, 0.00230, 0.00250, 0.00270, 0.00290, 0.00310, 0.00340, - 0.00360, 0.00390, 0.00410, 0.00440, 0.00470, 0.00500, 0.00530, 0.00560, 0.00590, 0.00620, 0.00650, 0.00690, - 0.00720, 0.00760, 0.00800, 0.00840, 0.00880, 0.00920, 0.00960, 0.01010, 0.01050, 0.01100, 0.01150, 0.01210, - 0.01270, 0.01330, 0.01390, 0.01460, 0.01530, 0.01610, 0.01690, 0.01780, 0.01870, 0.01970, 0.02090, 0.02210, - 0.02350, 0.02500, 0.02660, 0.02850, 0.03070, 0.03320, 0.03620, 0.03990, 0.04440, 0.05020, 0.05780, 0.06790, - 0.08120, 0.09880, 0.12150, 0.15020, 0.18520, 0.22640, 0.27340, 0.32510, 0.38050, 0.43830, 0.49720, 0.55580, - 0.61290, 0.66710, 0.71740, 0.76250, 0.80190, 0.83510, 0.86220, 0.88380, 0.90050, 0.91340, 0.92340, 0.93100, - 0.93710, 0.94200, 0.94600, 0.94940, 0.95230, 0.95490, 0.95710, 0.95920, 0.96100, 0.96270, 0.96430, 0.96580, - 0.96710, 0.96840, 0.96950, 0.97060, 0.97170, 0.97270, 0.97360, 0.97450, 0.97540, 0.97620, 0.97700, 0.97770, - 0.97840, 0.97910, 0.97980, 0.98040, 0.98100, 0.98160, 0.98220, 0.98280, 0.98330, 0.98390, 0.98440, 0.98490, - 0.98540, 0.98590, 0.98630, 0.98680, 0.98720, 0.98770, 0.98810, 0.98850, 0.98890, 0.98930, 0.98970, 0.99010, - 0.99050, 0.99090, 0.99130, 0.99160, 0.99200, 0.99230, 0.99270, 0.99300, 0.99330, 0.99360, 0.99400, 0.99430, - 0.99460, 0.99480, 0.99510, 0.99540, 0.99560, 0.99590, 0.99610, 0.99640, 0.99660, 0.99680, 0.99710, 0.99730, - 0.99750, 0.99770, 0.99780, 0.99800, 0.99820, 0.99840, 0.99850, 0.99870, 0.99880, 0.99890, 0.99910, 0.99920, - 0.99930, 0.99940, 0.99950, 0.99960, 0.99960, 0.99970, 0.99980, 0.99980, 0.99990, 0.99990, 0.99990, 1.00000, - 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000 - }; - - for(size_t i = 0; i < 200; ++i) - { - if(XVALUES[i] < randv && XVALUES[i+1] > randv) - { - return ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); - } - } - - return 0.0; - } - - } // end anonymous namespace //------------------------------------------------------------------------- - // RandomNumberGenerator helper - //------------------------------------------------------------------------- - /** - * Produces random numbers with various probability distributions - */ - CalculateMSVesuvio:: - RandomNumberGenerator::RandomNumberGenerator(const int seed) : m_generator() - { - m_generator.seed(static_cast(seed)); - } - /// Returns a flat random number between 0.0 & 1.0 - double CalculateMSVesuvio:: - RandomNumberGenerator::flat() - { - return uniform_double()(m_generator, - uniform_double::param_type(0.0, 1.0)); - } - /// Returns a random number distributed by a normal distribution - double CalculateMSVesuvio:: - RandomNumberGenerator::gaussian(const double mean, const double sigma) - { - return gaussian_double()(m_generator, - gaussian_double::param_type(mean, sigma)); - } - - //------------------------------------------------------------------------- - // Simulation helper - //------------------------------------------------------------------------- - /** - * Stores counts for each scatter order - * for a "run" of a given number of events - */ - CalculateMSVesuvio::Simulation:: - Simulation(const size_t order, const size_t ntimes) : - counts(order, std::vector(ntimes)), - maxorder(order) - {} - //------------------------------------------------------------------------- - // SimulationAggreator - //------------------------------------------------------------------------- - /** - * Accumulates and averages the results - * of each simulation - * @param nruns The number of runs that will be computed - */ - CalculateMSVesuvio::SimulationAggregator:: - SimulationAggregator(const size_t nruns) - { - results.reserve(nruns); - } - - /** - * @param order The number of requested scatterings - * @param ntimes The number of times on input workspace - * @return A reference to a new Simulation object - */ - CalculateMSVesuvio::Simulation & - CalculateMSVesuvio::SimulationAggregator:: - newSimulation(const size_t order, const size_t ntimes) - { - results.push_back(Simulation(order, ntimes)); - return results.back(); - } - - /** - * @return The mean and standard deviation of the current set of simulations - */ - CalculateMSVesuvio::SimulationWithErrors - CalculateMSVesuvio::SimulationAggregator::average() const - { - const size_t maxorder(results[0].maxorder), ntimes(results[0].counts[0].size()), - nruns(results.size()); - SimulationWithErrors retval(maxorder, ntimes); - - for(size_t i = 0; i < maxorder; ++i) - { - auto & orderCounts = retval.sim.counts[i]; - auto & orderErrors = retval.errors[i]; - for(size_t j = 0; j < ntimes; ++j) - { - double mean(0.0); - size_t npoints(0); - for(size_t k = 0; k < nruns; ++k) - { - const double val = results[k].counts[i][j]; - if(val > 0.0) - { - mean += val; - npoints +=1; - } - } - if(npoints < 2) - { - orderCounts[j] = 0.0; - orderErrors[j] = 0.0; - } - else - { - const double dblPts = static_cast(npoints); - orderCounts[j] = mean/dblPts; - // error is std dev - double sumsq(0.0); - for(size_t k = 0; k < nruns; ++k) - { - const double val = results[k].counts[i][j]; - if(val > 0.0) - { - const double diff = (val - mean); - sumsq += diff*diff; - } - } - orderErrors[j] = sqrt(sumsq/(dblPts*(dblPts-1))); - } - } - } - - return retval; - } - //------------------------------------------------------------------------- - // SimulationWithErrors - //------------------------------------------------------------------------- - /** - * Normalise the counts so that the integral over the single-scatter - * events is 1. - */ - void CalculateMSVesuvio::SimulationWithErrors::normalise() - { - const double sumSingle = std::accumulate(sim.counts.front().begin(), - sim.counts.front().end(), 0.0); - if(sumSingle > 0.0) - { - const double invSum = 1.0/sumSingle; // multiply is faster - // Divide everything by the sum - const size_t nscatters = sim.counts.size(); - for(size_t i = 0; i < nscatters; ++i) - { - auto & counts = sim.counts[i]; - auto & scerrors = this->errors[i]; - for(auto cit = counts.begin(), eit = scerrors.begin(); cit != counts.end(); - ++cit, ++eit) - { - (*cit) *= invSum; - (*eit) *= invSum; - } - } - } - } - - //------------------------------------------------------------------------- - // Algorithm definitions + // Member functions //------------------------------------------------------------------------- // Register the algorithm into the AlgorithmFactory @@ -557,7 +140,7 @@ namespace Mantid MatrixWorkspace_sptr multsc = WorkspaceFactory::Instance().create(m_inputWS); // Initialize random number generator - m_randgen = new RandomNumberGenerator(getProperty("Seed")); + m_randgen = new MSVesuvioHelper::RandomNumberGenerator(getProperty("Seed")); // Setup progress const int64_t nhist = static_cast(m_inputWS->getNumberHistograms()); @@ -732,7 +315,7 @@ namespace Mantid ResolutionParams respar = VesuvioResolution::getResolutionParameters(m_inputWS, wsIndex); // Final counts averaged over all simulations - SimulationAggregator accumulator(m_nruns); + MSVesuvioHelper::SimulationAggregator accumulator(m_nruns); for(size_t i = 0; i < m_nruns; ++i) { m_progress->report("MS calculation: idx=" + boost::lexical_cast(wsIndex) @@ -746,7 +329,7 @@ namespace Mantid } // Average over all runs and assign to output workspaces - SimulationWithErrors avgCounts = accumulator.average(); + MSVesuvioHelper::SimulationWithErrors avgCounts = accumulator.average(); avgCounts.normalise(); assignToOutput(avgCounts, totalsc, multsc); } @@ -760,7 +343,7 @@ namespace Mantid */ void CalculateMSVesuvio::simulate(const DetectorParams & detpar, const ResolutionParams &respar, - Simulation & simulCounts) const + MSVesuvioHelper::Simulation & simulCounts) const { for(size_t i = 0; i < m_nevents; ++i) { @@ -775,7 +358,7 @@ namespace Mantid * @param multsc A non-const reference to the spectrum for the multiple scattering contribution */ void - CalculateMSVesuvio::assignToOutput(const CalculateMSVesuvio::SimulationWithErrors &avgCounts, + CalculateMSVesuvio::assignToOutput(const MSVesuvioHelper::SimulationWithErrors &avgCounts, API::ISpectrum & totalsc, API::ISpectrum & multsc) const { // Sum up all multiple scatter events @@ -811,7 +394,7 @@ namespace Mantid */ double CalculateMSVesuvio::calculateCounts(const DetectorParams &detpar, const ResolutionParams &respar, - Simulation &simulation) const + MSVesuvioHelper::Simulation &simulation) const { double weightSum(0.0); @@ -1191,12 +774,12 @@ namespace Mantid const double randv = m_randgen->flat(); if(e1nom < 5000.0) { - if(angle > 90.0) return finalEnergyAuDD(randv); - else return finalEnergyAuYap(randv); + if(angle > 90.0) return MSVesuvioHelper::finalEnergyAuDD(randv); + else return MSVesuvioHelper::finalEnergyAuYap(randv); } else { - return finalEnergyUranium(randv); + return MSVesuvioHelper::finalEnergyUranium(randv); } } diff --git a/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp b/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp new file mode 100644 index 000000000000..5e2269c1914e --- /dev/null +++ b/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp @@ -0,0 +1,420 @@ +//----------------------------------------------------------------------------- +// Includes +//----------------------------------------------------------------------------- +#include "MantidCurveFitting/MSVesuvioHelpers.h" + +#include + +namespace Mantid { namespace CurveFitting +{ + namespace MSVesuvioHelper + { + + /** + * Generate the final energy of a neutron for gold foil analyser at 293K + * in double-difference mode: + * - THIN FOIL NUMBER DENSITY = 1.456E20 ATOMS/SQ CM. + * - THICK FOIL NUMBER DENSITY = 3.0* 1.456E20 ATOMS/SQ CM. + * @param randv A random number between 0.0 & 1.0, sample from a flat distribution + * @return A value to use for the final energy + */ + double finalEnergyAuDD(const double randv) + { + // Tabulated values of absoprtion energies from S.F. Mughabghab, Neutron Cross Sections, Academic + // Press, Orlando, Florida, 1984. + const double ENERGIES[300] = {\ + 2000.0, 2020.7, 2041.5, 2062.2, 2082.9, 2103.7, 2124.4, 2145.2, 2165.9, 2186.6, 2207.4, 2228.1, + 2248.8, 2269.6, 2290.3, 2311.0, 2331.8, 2352.5, 2373.2, 2394.0, 2414.7, 2435.5, 2456.2, 2476.9, + 2497.7, 2518.4, 2539.1, 2559.9, 2580.6, 2601.3, 2622.1, 2642.8, 2663.5, 2684.3, 2705.0, 2725.8, + 2746.5, 2767.2, 2788.0, 2808.7, 2829.4, 2850.2, 2870.9, 2891.6, 2912.4, 2933.1, 2953.8, 2974.6, + 2995.3, 3016.1, 3036.8, 3057.5, 3078.3, 3099.0, 3119.7, 3140.5, 3161.2, 3181.9, 3202.7, 3223.4, + 3244.1, 3264.9, 3285.6, 3306.4, 3327.1, 3347.8, 3368.6, 3389.3, 3410.0, 3430.8, 3451.5, 3472.2, + 3493.0, 3513.7, 3534.4, 3555.2, 3575.9, 3596.7, 3617.4, 3638.1, 3658.9, 3679.6, 3700.3, 3721.1, + 3741.8, 3762.5, 3783.3, 3804.0, 3824.7, 3845.5, 3866.2, 3887.0, 3907.7, 3928.4, 3949.2, 3969.9, + 3990.6, 4011.4, 4032.1, 4052.8, 4073.6, 4094.3, 4115.1, 4135.8, 4156.5, 4177.3, 4198.0, 4218.7, + 4239.5, 4260.2, 4280.9, 4301.7, 4322.4, 4343.1, 4363.9, 4384.6, 4405.4, 4426.1, 4446.8, 4467.6, + 4488.3, 4509.0, 4529.8, 4550.5, 4571.2, 4592.0, 4612.7, 4633.4, 4654.2, 4674.9, 4695.7, 4716.4, + 4737.1, 4757.9, 4778.6, 4799.3, 4820.1, 4840.8, 4861.5, 4882.3, 4903.0, 4923.7, 4944.5, 4965.2, + 4986.0, 5006.7, 5027.4, 5048.2, 5068.9, 5089.6, 5110.4, 5131.1, 5151.8, 5172.6, 5193.3, 5214.0, + 5234.8, 5255.5, 5276.3, 5297.0, 5317.7, 5338.5, 5359.2, 5379.9, 5400.7, 5421.4, 5442.1, 5462.9, + 5483.6, 5504.3, 5525.1, 5545.8, 5566.6, 5587.3, 5608.0, 5628.8, 5649.5, 5670.2, 5691.0, 5711.7, + 5732.4, 5753.2, 5773.9, 5794.6, 5815.4, 5836.1, 5856.9, 5877.6, 5898.3, 5919.1, 5939.8, 5960.5, + 5981.3, 6002.0, 6022.7, 6043.5, 6064.2, 6085.0, 6105.7, 6126.4, 6147.2, 6167.9, 6188.6, 6209.4, + 6230.1, 6250.8, 6271.6, 6292.3, 6313.0, 6333.8, 6354.5, 6375.3, 6396.0, 6416.7, 6437.5, 6458.2, + 6478.9, 6499.7, 6520.4, 6541.1, 6561.9, 6582.6, 6603.3, 6624.1, 6644.8, 6665.6, 6686.3, 6707.0, + 6727.8, 6748.5, 6769.2, 6790.0, 6810.7, 6831.4, 6852.2, 6872.9, 6893.6, 6914.4, 6935.1, 6955.9, + 6976.6, 6997.3, 7018.1, 7038.8, 7059.5, 7080.3, 7101.0, 7121.7, 7142.5, 7163.2, 7183.9, 7204.7, + 7225.4, 7246.2, 7266.9, 7287.6, 7308.4, 7329.1, 7349.8, 7370.6, 7391.3, 7412.0, 7432.8, 7453.5, + 7474.2, 7495.0, 7515.7, 7536.5, 7557.2, 7577.9, 7598.7, 7619.4, 7640.1, 7660.9, 7681.6, 7702.3, + 7723.1, 7743.8, 7764.5, 7785.3, 7806.0, 7826.8, 7847.5, 7868.2, 7889.0, 7909.7, 7930.4, 7951.2, + 7971.9, 7992.6, 8013.4, 8034.1, 8054.8, 8075.6, 8096.3, 8117.1, 8137.8, 8158.5, 8179.3, 8200.0 + }; + const double XVALUES[300] = {\ + 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, + 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, + 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00010, 0.00010, + 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, + 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, + 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00010, 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, + 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, 0.00020, 0.00030, 0.00030, 0.00030, 0.00030, 0.00030, 0.00030, + 0.00030, 0.00030, 0.00040, 0.00040, 0.00040, 0.00040, 0.00040, 0.00050, 0.00050, 0.00050, 0.00050, 0.00060, + 0.00060, 0.00070, 0.00070, 0.00070, 0.00080, 0.00090, 0.00090, 0.00100, 0.00110, 0.00110, 0.00120, 0.00130, + 0.00150, 0.00160, 0.00170, 0.00190, 0.00210, 0.00230, 0.00260, 0.00290, 0.00320, 0.00360, 0.00410, 0.00470, + 0.00540, 0.00620, 0.00720, 0.00840, 0.00990, 0.01180, 0.01420, 0.01740, 0.02140, 0.02680, 0.03410, 0.04400, + 0.05770, 0.07680, 0.10360, 0.14050, 0.18960, 0.25110, 0.32310, 0.40240, 0.48540, 0.56870, 0.64930, 0.72370, + 0.78850, 0.84150, 0.88240, 0.91260, 0.93440, 0.95000, 0.96130, 0.96960, 0.97570, 0.98030, 0.98380, 0.98650, + 0.98870, 0.99040, 0.99180, 0.99290, 0.99380, 0.99460, 0.99520, 0.99580, 0.99620, 0.99660, 0.99700, 0.99730, + 0.99750, 0.99770, 0.99790, 0.99810, 0.99830, 0.99840, 0.99850, 0.99860, 0.99870, 0.99880, 0.99890, 0.99900, + 0.99900, 0.99910, 0.99910, 0.99920, 0.99920, 0.99930, 0.99930, 0.99940, 0.99940, 0.99940, 0.99940, 0.99950, + 0.99950, 0.99950, 0.99950, 0.99960, 0.99960, 0.99960, 0.99960, 0.99960, 0.99960, 0.99970, 0.99970, 0.99970, + 0.99970, 0.99970, 0.99970, 0.99970, 0.99970, 0.99970, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, + 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99980, 0.99990, 0.99990, + 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, + 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, + 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, 0.99990, + 0.99990, 0.99990, 0.99990, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, + 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, + 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000 + }; + + for(size_t i = 0; i < 299; ++i) + { + if(XVALUES[i] < randv && XVALUES[i+1] > randv) + { + double ef = ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); + if(ef < 100.0) ef = 0.0; + return ef; + } + } + return 0.0; + + + } + + /** + * Generate the final energy of a neutron for gold foil analyser at 293K + * with number density of 7.35E19 atoms/cm^2 in yap difference mode. + * @param randv A random number between 0.0 & 1.0, sample from a flat distribution + * @return A value to use for the final energy + */ + double finalEnergyAuYap(const double randv) + { + // Tabulated values of absoprtion energies from S.F. Mughabghab, Neutron Cross Sections, Academic + // Press, Orlando, Florida, 1984. + const double ENERGIES[600] = {\ + 4000.0, 4003.3, 4006.7, 4010.0, 4013.4, 4016.7, 4020.0, 4023.4, 4026.7, 4030.1, 4033.4, 4036.7, + 4040.1, 4043.4, 4046.7, 4050.1, 4053.4, 4056.8, 4060.1, 4063.4, 4066.8, 4070.1, 4073.5, 4076.8, + 4080.1, 4083.5, 4086.8, 4090.2, 4093.5, 4096.8, 4100.2, 4103.5, 4106.8, 4110.2, 4113.5, 4116.9, + 4120.2, 4123.5, 4126.9, 4130.2, 4133.6, 4136.9, 4140.2, 4143.6, 4146.9, 4150.3, 4153.6, 4156.9, + 4160.3, 4163.6, 4166.9, 4170.3, 4173.6, 4177.0, 4180.3, 4183.6, 4187.0, 4190.3, 4193.7, 4197.0, + 4200.3, 4203.7, 4207.0, 4210.4, 4213.7, 4217.0, 4220.4, 4223.7, 4227.0, 4230.4, 4233.7, 4237.1, + 4240.4, 4243.7, 4247.1, 4250.4, 4253.8, 4257.1, 4260.4, 4263.8, 4267.1, 4270.5, 4273.8, 4277.1, + 4280.5, 4283.8, 4287.1, 4290.5, 4293.8, 4297.2, 4300.5, 4303.8, 4307.2, 4310.5, 4313.9, 4317.2, + 4320.5, 4323.9, 4327.2, 4330.6, 4333.9, 4337.2, 4340.6, 4343.9, 4347.2, 4350.6, 4353.9, 4357.3, + 4360.6, 4363.9, 4367.3, 4370.6, 4374.0, 4377.3, 4380.6, 4384.0, 4387.3, 4390.7, 4394.0, 4397.3, + 4400.7, 4404.0, 4407.3, 4410.7, 4414.0, 4417.4, 4420.7, 4424.0, 4427.4, 4430.7, 4434.1, 4437.4, + 4440.7, 4444.1, 4447.4, 4450.8, 4454.1, 4457.4, 4460.8, 4464.1, 4467.4, 4470.8, 4474.1, 4477.5, + 4480.8, 4484.1, 4487.5, 4490.8, 4494.2, 4497.5, 4500.8, 4504.2, 4507.5, 4510.9, 4514.2, 4517.5, + 4520.9, 4524.2, 4527.5, 4530.9, 4534.2, 4537.6, 4540.9, 4544.2, 4547.6, 4550.9, 4554.3, 4557.6, + 4560.9, 4564.3, 4567.6, 4571.0, 4574.3, 4577.6, 4581.0, 4584.3, 4587.6, 4591.0, 4594.3, 4597.7, + 4601.0, 4604.3, 4607.7, 4611.0, 4614.4, 4617.7, 4621.0, 4624.4, 4627.7, 4631.1, 4634.4, 4637.7, + 4641.1, 4644.4, 4647.7, 4651.1, 4654.4, 4657.8, 4661.1, 4664.4, 4667.8, 4671.1, 4674.5, 4677.8, + 4681.1, 4684.5, 4687.8, 4691.2, 4694.5, 4697.8, 4701.2, 4704.5, 4707.8, 4711.2, 4714.5, 4717.9, + 4721.2, 4724.5, 4727.9, 4731.2, 4734.6, 4737.9, 4741.2, 4744.6, 4747.9, 4751.3, 4754.6, 4757.9, + 4761.3, 4764.6, 4767.9, 4771.3, 4774.6, 4778.0, 4781.3, 4784.6, 4788.0, 4791.3, 4794.7, 4798.0, + 4801.3, 4804.7, 4808.0, 4811.4, 4814.7, 4818.0, 4821.4, 4824.7, 4828.0, 4831.4, 4834.7, 4838.1, + 4841.4, 4844.7, 4848.1, 4851.4, 4854.8, 4858.1, 4861.4, 4864.8, 4868.1, 4871.5, 4874.8, 4878.1, + 4881.5, 4884.8, 4888.1, 4891.5, 4894.8, 4898.2, 4901.5, 4904.8, 4908.2, 4911.5, 4914.9, 4918.2, + 4921.5, 4924.9, 4928.2, 4931.6, 4934.9, 4938.2, 4941.6, 4944.9, 4948.2, 4951.6, 4954.9, 4958.3, + 4961.6, 4964.9, 4968.3, 4971.6, 4975.0, 4978.3, 4981.6, 4985.0, 4988.3, 4991.7, 4995.0, 4998.3, + 5001.7, 5005.0, 5008.3, 5011.7, 5015.0, 5018.4, 5021.7, 5025.0, 5028.4, 5031.7, 5035.1, 5038.4, + 5041.7, 5045.1, 5048.4, 5051.8, 5055.1, 5058.4, 5061.8, 5065.1, 5068.4, 5071.8, 5075.1, 5078.5, + 5081.8, 5085.1, 5088.5, 5091.8, 5095.2, 5098.5, 5101.8, 5105.2, 5108.5, 5111.9, 5115.2, 5118.5, + 5121.9, 5125.2, 5128.5, 5131.9, 5135.2, 5138.6, 5141.9, 5145.2, 5148.6, 5151.9, 5155.3, 5158.6, + 5161.9, 5165.3, 5168.6, 5172.0, 5175.3, 5178.6, 5182.0, 5185.3, 5188.6, 5192.0, 5195.3, 5198.7, + 5202.0, 5205.3, 5208.7, 5212.0, 5215.4, 5218.7, 5222.0, 5225.4, 5228.7, 5232.1, 5235.4, 5238.7, + 5242.1, 5245.4, 5248.7, 5252.1, 5255.4, 5258.8, 5262.1, 5265.4, 5268.8, 5272.1, 5275.5, 5278.8, + 5282.1, 5285.5, 5288.8, 5292.2, 5295.5, 5298.8, 5302.2, 5305.5, 5308.8, 5312.2, 5315.5, 5318.9, + 5322.2, 5325.5, 5328.9, 5332.2, 5335.6, 5338.9, 5342.2, 5345.6, 5348.9, 5352.3, 5355.6, 5358.9, + 5362.3, 5365.6, 5368.9, 5372.3, 5375.6, 5379.0, 5382.3, 5385.6, 5389.0, 5392.3, 5395.7, 5399.0, + 5402.3, 5405.7, 5409.0, 5412.4, 5415.7, 5419.0, 5422.4, 5425.7, 5429.0, 5432.4, 5435.7, 5439.1, + 5442.4, 5445.7, 5449.1, 5452.4, 5455.8, 5459.1, 5462.4, 5465.8, 5469.1, 5472.5, 5475.8, 5479.1, + 5482.5, 5485.8, 5489.1, 5492.5, 5495.8, 5499.2, 5502.5, 5505.8, 5509.2, 5512.5, 5515.9, 5519.2, + 5522.5, 5525.9, 5529.2, 5532.6, 5535.9, 5539.2, 5542.6, 5545.9, 5549.2, 5552.6, 5555.9, 5559.3, + 5562.6, 5565.9, 5569.3, 5572.6, 5576.0, 5579.3, 5582.6, 5586.0, 5589.3, 5592.7, 5596.0, 5599.3, + 5602.7, 5606.0, 5609.3, 5612.7, 5616.0, 5619.4, 5622.7, 5626.0, 5629.4, 5632.7, 5636.1, 5639.4, + 5642.7, 5646.1, 5649.4, 5652.8, 5656.1, 5659.4, 5662.8, 5666.1, 5669.4, 5672.8, 5676.1, 5679.5, + 5682.8, 5686.1, 5689.5, 5692.8, 5696.2, 5699.5, 5702.8, 5706.2, 5709.5, 5712.9, 5716.2, 5719.5, + 5722.9, 5726.2, 5729.5, 5732.9, 5736.2, 5739.6, 5742.9, 5746.2, 5749.6, 5752.9, 5756.3, 5759.6, + 5762.9, 5766.3, 5769.6, 5773.0, 5776.3, 5779.6, 5783.0, 5786.3, 5789.6, 5793.0, 5796.3, 5799.7, + 5803.0, 5806.3, 5809.7, 5813.0, 5816.4, 5819.7, 5823.0, 5826.4, 5829.7, 5833.1, 5836.4, 5839.7, + 5843.1, 5846.4, 5849.7, 5853.1, 5856.4, 5859.8, 5863.1, 5866.4, 5869.8, 5873.1, 5876.5, 5879.8, + 5883.1, 5886.5, 5889.8, 5893.2, 5896.5, 5899.8, 5903.2, 5906.5, 5909.8, 5913.2, 5916.5, 5919.9, + 5923.2, 5926.5, 5929.9, 5933.2, 5936.6, 5939.9, 5943.2, 5946.6, 5949.9, 5953.3, 5956.6, 5959.9, + 5963.3, 5966.6, 5970.0, 5973.3, 5976.6, 5980.0, 5983.3, 5986.6, 5990.0, 5993.3, 5996.7, 6000.0 + }; + const double XVALUES[600] = {\ + 0.00000, 0.00000, 0.00000, 0.00002, 0.00003, 0.00003, 0.00004, 0.00005, 0.00005, 0.00006, 0.00007, 0.00007, + 0.00008, 0.00009, 0.00010, 0.00010, 0.00011, 0.00012, 0.00013, 0.00014, 0.00015, 0.00015, 0.00016, 0.00017, + 0.00018, 0.00019, 0.00020, 0.00021, 0.00022, 0.00023, 0.00024, 0.00025, 0.00026, 0.00027, 0.00028, 0.00029, + 0.00030, 0.00031, 0.00032, 0.00033, 0.00034, 0.00035, 0.00037, 0.00038, 0.00039, 0.00040, 0.00041, 0.00043, + 0.00044, 0.00045, 0.00047, 0.00048, 0.00049, 0.00051, 0.00052, 0.00054, 0.00055, 0.00057, 0.00058, 0.00060, + 0.00061, 0.00063, 0.00065, 0.00066, 0.00068, 0.00070, 0.00072, 0.00074, 0.00075, 0.00077, 0.00079, 0.00081, + 0.00083, 0.00085, 0.00087, 0.00089, 0.00092, 0.00094, 0.00096, 0.00098, 0.00101, 0.00103, 0.00106, 0.00108, + 0.00111, 0.00113, 0.00116, 0.00118, 0.00121, 0.00124, 0.00127, 0.00130, 0.00133, 0.00136, 0.00139, 0.00142, + 0.00146, 0.00149, 0.00152, 0.00156, 0.00159, 0.00163, 0.00167, 0.00171, 0.00174, 0.00178, 0.00182, 0.00187, + 0.00191, 0.00195, 0.00200, 0.00204, 0.00209, 0.00214, 0.00219, 0.00224, 0.00229, 0.00235, 0.00240, 0.00246, + 0.00251, 0.00257, 0.00263, 0.00269, 0.00276, 0.00282, 0.00289, 0.00296, 0.00303, 0.00310, 0.00318, 0.00325, + 0.00333, 0.00341, 0.00349, 0.00358, 0.00367, 0.00376, 0.00385, 0.00394, 0.00404, 0.00414, 0.00425, 0.00435, + 0.00446, 0.00458, 0.00469, 0.00481, 0.00494, 0.00507, 0.00520, 0.00533, 0.00548, 0.00562, 0.00577, 0.00592, + 0.00608, 0.00625, 0.00642, 0.00659, 0.00677, 0.00696, 0.00716, 0.00736, 0.00757, 0.00778, 0.00800, 0.00823, + 0.00847, 0.00872, 0.00898, 0.00924, 0.00952, 0.00980, 0.01010, 0.01041, 0.01073, 0.01106, 0.01141, 0.01177, + 0.01214, 0.01253, 0.01293, 0.01335, 0.01379, 0.01425, 0.01472, 0.01522, 0.01573, 0.01627, 0.01683, 0.01742, + 0.01803, 0.01867, 0.01934, 0.02004, 0.02077, 0.02154, 0.02234, 0.02317, 0.02405, 0.02497, 0.02594, 0.02695, + 0.02801, 0.02913, 0.03030, 0.03153, 0.03282, 0.03419, 0.03561, 0.03712, 0.03871, 0.04037, 0.04213, 0.04398, + 0.04594, 0.04799, 0.05017, 0.05246, 0.05488, 0.05743, 0.06013, 0.06297, 0.06598, 0.06915, 0.07251, 0.07605, + 0.07979, 0.08374, 0.08791, 0.09230, 0.09694, 0.10183, 0.10698, 0.11241, 0.11812, 0.12411, 0.13041, 0.13703, + 0.14395, 0.15119, 0.15877, 0.16667, 0.17490, 0.18347, 0.19237, 0.20159, 0.21114, 0.22100, 0.23117, 0.24164, + 0.25240, 0.26344, 0.27473, 0.28628, 0.29807, 0.31007, 0.32228, 0.33468, 0.34725, 0.35999, 0.37286, 0.38586, + 0.39898, 0.41219, 0.42549, 0.43886, 0.45228, 0.46575, 0.47925, 0.49277, 0.50628, 0.51980, 0.53329, 0.54674, + 0.56016, 0.57350, 0.58677, 0.59996, 0.61304, 0.62600, 0.63883, 0.65152, 0.66403, 0.67638, 0.68853, 0.70048, + 0.71220, 0.72369, 0.73492, 0.74590, 0.75659, 0.76700, 0.77711, 0.78691, 0.79640, 0.80557, 0.81442, 0.82294, + 0.83113, 0.83900, 0.84654, 0.85376, 0.86067, 0.86726, 0.87355, 0.87954, 0.88525, 0.89067, 0.89583, 0.90073, + 0.90537, 0.90979, 0.91398, 0.91794, 0.92170, 0.92527, 0.92865, 0.93185, 0.93489, 0.93776, 0.94049, 0.94307, + 0.94552, 0.94784, 0.95005, 0.95213, 0.95412, 0.95600, 0.95779, 0.95949, 0.96110, 0.96264, 0.96410, 0.96549, + 0.96681, 0.96807, 0.96927, 0.97041, 0.97150, 0.97254, 0.97353, 0.97447, 0.97538, 0.97624, 0.97706, 0.97785, + 0.97860, 0.97933, 0.98002, 0.98068, 0.98131, 0.98192, 0.98250, 0.98306, 0.98359, 0.98411, 0.98460, 0.98507, + 0.98553, 0.98596, 0.98638, 0.98679, 0.98718, 0.98755, 0.98791, 0.98826, 0.98859, 0.98892, 0.98923, 0.98953, + 0.98981, 0.99009, 0.99036, 0.99062, 0.99087, 0.99111, 0.99135, 0.99158, 0.99179, 0.99201, 0.99221, 0.99241, + 0.99260, 0.99279, 0.99296, 0.99314, 0.99331, 0.99347, 0.99363, 0.99378, 0.99393, 0.99408, 0.99422, 0.99435, + 0.99448, 0.99461, 0.99473, 0.99486, 0.99497, 0.99509, 0.99520, 0.99530, 0.99541, 0.99551, 0.99561, 0.99571, + 0.99580, 0.99589, 0.99598, 0.99607, 0.99615, 0.99623, 0.99631, 0.99639, 0.99647, 0.99654, 0.99661, 0.99668, + 0.99675, 0.99682, 0.99688, 0.99694, 0.99701, 0.99707, 0.99713, 0.99718, 0.99724, 0.99729, 0.99735, 0.99740, + 0.99745, 0.99750, 0.99755, 0.99760, 0.99764, 0.99769, 0.99773, 0.99778, 0.99782, 0.99786, 0.99790, 0.99794, + 0.99798, 0.99802, 0.99806, 0.99809, 0.99813, 0.99816, 0.99820, 0.99823, 0.99827, 0.99830, 0.99833, 0.99836, + 0.99839, 0.99842, 0.99845, 0.99848, 0.99850, 0.99853, 0.99856, 0.99859, 0.99861, 0.99864, 0.99866, 0.99869, + 0.99871, 0.99873, 0.99876, 0.99878, 0.99880, 0.99882, 0.99884, 0.99886, 0.99889, 0.99891, 0.99893, 0.99895, + 0.99896, 0.99898, 0.99900, 0.99902, 0.99904, 0.99906, 0.99907, 0.99909, 0.99911, 0.99912, 0.99914, 0.99915, + 0.99917, 0.99919, 0.99920, 0.99922, 0.99923, 0.99924, 0.99926, 0.99927, 0.99929, 0.99930, 0.99931, 0.99933, + 0.99934, 0.99935, 0.99936, 0.99938, 0.99939, 0.99940, 0.99941, 0.99942, 0.99943, 0.99944, 0.99946, 0.99947, + 0.99948, 0.99949, 0.99950, 0.99951, 0.99952, 0.99953, 0.99954, 0.99955, 0.99956, 0.99956, 0.99957, 0.99958, + 0.99959, 0.99960, 0.99961, 0.99962, 0.99963, 0.99963, 0.99964, 0.99965, 0.99966, 0.99967, 0.99967, 0.99968, + 0.99969, 0.99970, 0.99970, 0.99971, 0.99972, 0.99973, 0.99973, 0.99974, 0.99975, 0.99975, 0.99976, 0.99977, + 0.99977, 0.99978, 0.99979, 0.99979, 0.99980, 0.99980, 0.99981, 0.99982, 0.99982, 0.99983, 0.99983, 0.99984, + 0.99984, 0.99985, 0.99985, 0.99986, 0.99986, 0.99987, 0.99988, 0.99988, 0.99989, 0.99989, 0.99990, 0.99990, + 0.99990, 0.99991, 0.99991, 0.99992, 0.99992, 0.99993, 0.99993, 0.99994, 0.99994, 0.99994, 0.99995, 0.99995, + 0.99996, 0.99996, 0.99997, 0.99997, 0.99997, 0.99998, 0.99998, 0.99998, 0.99999, 0.99999, 1.00000, 1.00000 + }; + for(size_t i = 0; i < 599; ++i) + { + if(XVALUES[i] < randv && XVALUES[i+1] > randv) + { + return ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); + } + } + return 0.0; + } + + /** + * Generate the final energy of a neutron for uranium foil analyser at 293K + * with number density of 1.456E20 atoms/cm^2 in double-difference mode. + * @param randv A random number between 0.0 & 1.0, sample from a flat distribution + * @return A value to use for the final energy + */ + double finalEnergyUranium(const double randv) + { + const double ENERGIES[201] = {\ + 5959.0, 5967.7, 5976.4, 5985.1, 5993.8, 6002.5, 6011.2, 6019.9, 6028.6, 6037.3, 6046.0, 6054.8, + 6063.5, 6072.2, 6080.9, 6089.6, 6098.3, 6107.0, 6115.7, 6124.4, 6133.1, 6141.8, 6150.5, 6159.2, + 6167.9, 6176.6, 6185.3, 6194.0, 6202.7, 6211.4, 6220.1, 6228.9, 6237.6, 6246.3, 6255.0, 6263.7, + 6272.4, 6281.1, 6289.8, 6298.5, 6307.2, 6315.9, 6324.6, 6333.3, 6342.0, 6350.7, 6359.4, 6368.1, + 6376.8, 6385.5, 6394.3, 6403.0, 6411.7, 6420.4, 6429.1, 6437.8, 6446.5, 6455.2, 6463.9, 6472.6, + 6481.3, 6490.0, 6498.7, 6507.4, 6516.1, 6524.8, 6533.5, 6542.2, 6550.9, 6559.6, 6568.4, 6577.1, + 6585.8, 6594.5, 6603.2, 6611.9, 6620.6, 6629.3, 6638.0, 6646.7, 6655.4, 6664.1, 6672.8, 6681.5, + 6690.2, 6698.9, 6707.6, 6716.3, 6725.0, 6733.7, 6742.5, 6751.2, 6759.9, 6768.6, 6777.3, 6786.0, + 6794.7, 6803.4, 6812.1, 6820.8, 6829.5, 6838.2, 6846.9, 6855.6, 6864.3, 6873.0, 6881.7, 6890.4, + 6899.1, 6907.8, 6916.5, 6925.3, 6934.0, 6942.7, 6951.4, 6960.1, 6968.8, 6977.5, 6986.2, 6994.9, + 7003.6, 7012.3, 7021.0, 7029.7, 7038.4, 7047.1, 7055.8, 7064.5, 7073.2, 7081.9, 7090.6, 7099.4, + 7108.1, 7116.8, 7125.5, 7134.2, 7142.9, 7151.6, 7160.3, 7169.0, 7177.7, 7186.4, 7195.1, 7203.8, + 7212.5, 7221.2, 7229.9, 7238.6, 7247.3, 7256.0, 7264.8, 7273.5, 7282.2, 7290.9, 7299.6, 7308.3, + 7317.0, 7325.7, 7334.4, 7343.1, 7351.8, 7360.5, 7369.2, 7377.9, 7386.6, 7395.3, 7404.0, 7412.7, + 7421.4, 7430.1, 7438.9, 7447.6, 7456.3, 7465.0, 7473.7, 7482.4, 7491.1, 7499.8, 7508.5, 7517.2, + 7525.9, 7534.6, 7543.3, 7552.0, 7560.7, 7569.4, 7578.1, 7586.8, 7595.5, 7604.2, 7613.0, 7621.7, + 7630.4, 7639.1, 7647.8, 7656.5, 7665.2, 7673.9, 7682.6, 7691.3, 7700.0 + }; + + const double XVALUES[201] = {\ + 0.00000, 0.00000, 0.00000, 0.00020, 0.00030, 0.00040, 0.00050, 0.00060, 0.00070, 0.00080, 0.00090, 0.00110, + 0.00120, 0.00140, 0.00150, 0.00170, 0.00190, 0.00210, 0.00230, 0.00250, 0.00270, 0.00290, 0.00310, 0.00340, + 0.00360, 0.00390, 0.00410, 0.00440, 0.00470, 0.00500, 0.00530, 0.00560, 0.00590, 0.00620, 0.00650, 0.00690, + 0.00720, 0.00760, 0.00800, 0.00840, 0.00880, 0.00920, 0.00960, 0.01010, 0.01050, 0.01100, 0.01150, 0.01210, + 0.01270, 0.01330, 0.01390, 0.01460, 0.01530, 0.01610, 0.01690, 0.01780, 0.01870, 0.01970, 0.02090, 0.02210, + 0.02350, 0.02500, 0.02660, 0.02850, 0.03070, 0.03320, 0.03620, 0.03990, 0.04440, 0.05020, 0.05780, 0.06790, + 0.08120, 0.09880, 0.12150, 0.15020, 0.18520, 0.22640, 0.27340, 0.32510, 0.38050, 0.43830, 0.49720, 0.55580, + 0.61290, 0.66710, 0.71740, 0.76250, 0.80190, 0.83510, 0.86220, 0.88380, 0.90050, 0.91340, 0.92340, 0.93100, + 0.93710, 0.94200, 0.94600, 0.94940, 0.95230, 0.95490, 0.95710, 0.95920, 0.96100, 0.96270, 0.96430, 0.96580, + 0.96710, 0.96840, 0.96950, 0.97060, 0.97170, 0.97270, 0.97360, 0.97450, 0.97540, 0.97620, 0.97700, 0.97770, + 0.97840, 0.97910, 0.97980, 0.98040, 0.98100, 0.98160, 0.98220, 0.98280, 0.98330, 0.98390, 0.98440, 0.98490, + 0.98540, 0.98590, 0.98630, 0.98680, 0.98720, 0.98770, 0.98810, 0.98850, 0.98890, 0.98930, 0.98970, 0.99010, + 0.99050, 0.99090, 0.99130, 0.99160, 0.99200, 0.99230, 0.99270, 0.99300, 0.99330, 0.99360, 0.99400, 0.99430, + 0.99460, 0.99480, 0.99510, 0.99540, 0.99560, 0.99590, 0.99610, 0.99640, 0.99660, 0.99680, 0.99710, 0.99730, + 0.99750, 0.99770, 0.99780, 0.99800, 0.99820, 0.99840, 0.99850, 0.99870, 0.99880, 0.99890, 0.99910, 0.99920, + 0.99930, 0.99940, 0.99950, 0.99960, 0.99960, 0.99970, 0.99980, 0.99980, 0.99990, 0.99990, 0.99990, 1.00000, + 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000 + }; + + for(size_t i = 0; i < 200; ++i) + { + if(XVALUES[i] < randv && XVALUES[i+1] > randv) + { + return ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); + } + } + return 0.0; + } + + //------------------------------------------------------------------------- + // RandomNumberGenerator + //------------------------------------------------------------------------- + /** + * Produces random numbers with various probability distributions + */ + RandomNumberGenerator::RandomNumberGenerator(const int seed) : m_generator() + { + m_generator.seed(static_cast(seed)); + } + /// Returns a flat random number between 0.0 & 1.0 + double RandomNumberGenerator::flat() + { + return uniform_double()(m_generator, + uniform_double::param_type(0.0, 1.0)); + } + /// Returns a random number distributed by a normal distribution + double RandomNumberGenerator::gaussian(const double mean, const double sigma) + { + return gaussian_double()(m_generator, + gaussian_double::param_type(mean, sigma)); + } + + //------------------------------------------------------------------------- + // Simulation + //------------------------------------------------------------------------- + /** + * Stores counts for each scatter order + * for a "run" of a given number of events + */ + Simulation::Simulation(const size_t order, const size_t ntimes) : + counts(order, std::vector(ntimes)), + maxorder(order) + {} + + //------------------------------------------------------------------------- + // SimulationAggreator + //------------------------------------------------------------------------- + /** + * Accumulates and averages the results + * of each simulation + * @param nruns The number of runs that will be computed + */ + SimulationAggregator::SimulationAggregator(const size_t nruns) + { + results.reserve(nruns); + } + + /** + * @param order The number of requested scatterings + * @param ntimes The number of times on input workspace + * @return A reference to a new Simulation object + */ + Simulation & SimulationAggregator::newSimulation(const size_t order, const size_t ntimes) + { + results.push_back(Simulation(order, ntimes)); + return results.back(); + } + + /** + * @return The mean and standard deviation of the current set of simulations + */ + SimulationWithErrors + SimulationAggregator::average() const + { + const size_t maxorder(results[0].maxorder), ntimes(results[0].counts[0].size()), + nruns(results.size()); + SimulationWithErrors retval(maxorder, ntimes); + + for(size_t i = 0; i < maxorder; ++i) + { + auto & orderCounts = retval.sim.counts[i]; + auto & orderErrors = retval.errors[i]; + for(size_t j = 0; j < ntimes; ++j) + { + double mean(0.0); + size_t npoints(0); + for(size_t k = 0; k < nruns; ++k) + { + const double val = results[k].counts[i][j]; + if(val > 0.0) + { + mean += val; + npoints +=1; + } + } + if(npoints < 2) + { + orderCounts[j] = 0.0; + orderErrors[j] = 0.0; + } + else + { + const double dblPts = static_cast(npoints); + orderCounts[j] = mean/dblPts; + // error is std dev + double sumsq(0.0); + for(size_t k = 0; k < nruns; ++k) + { + const double val = results[k].counts[i][j]; + if(val > 0.0) + { + const double diff = (val - mean); + sumsq += diff*diff; + } + } + orderErrors[j] = sqrt(sumsq/(dblPts*(dblPts-1))); + } + } + } + + return retval; + } + //------------------------------------------------------------------------- + // SimulationWithErrors + //------------------------------------------------------------------------- + /** + * Normalise the counts so that the integral over the single-scatter + * events is 1. + */ + void SimulationWithErrors::normalise() + { + const double sumSingle = std::accumulate(sim.counts.front().begin(), + sim.counts.front().end(), 0.0); + if(sumSingle > 0.0) + { + const double invSum = 1.0/sumSingle; // multiply is faster + // Divide everything by the sum + const size_t nscatters = sim.counts.size(); + for(size_t i = 0; i < nscatters; ++i) + { + auto & counts = sim.counts[i]; + auto & scerrors = this->errors[i]; + for(auto cit = counts.begin(), eit = scerrors.begin(); cit != counts.end(); + ++cit, ++eit) + { + (*cit) *= invSum; + (*eit) *= invSum; + } + } + } + } + + } // namespace MSVesuvioHelper +}} From 6c990d0e9904e8abfc1230e1fc3933c92fe4f2ce Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Thu, 30 Oct 2014 10:44:19 +0000 Subject: [PATCH 28/37] First performance enhancement Use STL algorithms for value searching and static storage for constant arrays. Refs #10169 --- .../CurveFitting/src/MSVesuvioHelpers.cpp | 64 ++++++++----------- 1 file changed, 28 insertions(+), 36 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp b/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp index 5e2269c1914e..9b51dfac49f4 100644 --- a/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp @@ -22,7 +22,7 @@ namespace Mantid { namespace CurveFitting { // Tabulated values of absoprtion energies from S.F. Mughabghab, Neutron Cross Sections, Academic // Press, Orlando, Florida, 1984. - const double ENERGIES[300] = {\ + static const double ENERGIES[300] = {\ 2000.0, 2020.7, 2041.5, 2062.2, 2082.9, 2103.7, 2124.4, 2145.2, 2165.9, 2186.6, 2207.4, 2228.1, 2248.8, 2269.6, 2290.3, 2311.0, 2331.8, 2352.5, 2373.2, 2394.0, 2414.7, 2435.5, 2456.2, 2476.9, 2497.7, 2518.4, 2539.1, 2559.9, 2580.6, 2601.3, 2622.1, 2642.8, 2663.5, 2684.3, 2705.0, 2725.8, @@ -49,7 +49,7 @@ namespace Mantid { namespace CurveFitting 7723.1, 7743.8, 7764.5, 7785.3, 7806.0, 7826.8, 7847.5, 7868.2, 7889.0, 7909.7, 7930.4, 7951.2, 7971.9, 7992.6, 8013.4, 8034.1, 8054.8, 8075.6, 8096.3, 8117.1, 8137.8, 8158.5, 8179.3, 8200.0 }; - const double XVALUES[300] = {\ + static const double XVALUES[300] = {\ 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00010, 0.00010, @@ -77,18 +77,14 @@ namespace Mantid { namespace CurveFitting 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000 }; - for(size_t i = 0; i < 299; ++i) - { - if(XVALUES[i] < randv && XVALUES[i+1] > randv) - { - double ef = ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); - if(ef < 100.0) ef = 0.0; - return ef; - } - } - return 0.0; - - + const double *xp1 = std::lower_bound(XVALUES, XVALUES+300, randv); + if(xp1 == XVALUES+300) return 0.0; + const double *xm1 = xp1 - 1; + const double *ep1 = ENERGIES + (xp1 - XVALUES); + const double *em1 = ep1 - 1; + const double ef = (*em1) + (randv - *xm1)*(*ep1 - *em1)/(*xp1 - *xm1); + if( ef < 100.0 ) return 0.0; + else return ef; } /** @@ -101,7 +97,7 @@ namespace Mantid { namespace CurveFitting { // Tabulated values of absoprtion energies from S.F. Mughabghab, Neutron Cross Sections, Academic // Press, Orlando, Florida, 1984. - const double ENERGIES[600] = {\ + static const double ENERGIES[600] = {\ 4000.0, 4003.3, 4006.7, 4010.0, 4013.4, 4016.7, 4020.0, 4023.4, 4026.7, 4030.1, 4033.4, 4036.7, 4040.1, 4043.4, 4046.7, 4050.1, 4053.4, 4056.8, 4060.1, 4063.4, 4066.8, 4070.1, 4073.5, 4076.8, 4080.1, 4083.5, 4086.8, 4090.2, 4093.5, 4096.8, 4100.2, 4103.5, 4106.8, 4110.2, 4113.5, 4116.9, @@ -153,7 +149,7 @@ namespace Mantid { namespace CurveFitting 5923.2, 5926.5, 5929.9, 5933.2, 5936.6, 5939.9, 5943.2, 5946.6, 5949.9, 5953.3, 5956.6, 5959.9, 5963.3, 5966.6, 5970.0, 5973.3, 5976.6, 5980.0, 5983.3, 5986.6, 5990.0, 5993.3, 5996.7, 6000.0 }; - const double XVALUES[600] = {\ + static const double XVALUES[600] = {\ 0.00000, 0.00000, 0.00000, 0.00002, 0.00003, 0.00003, 0.00004, 0.00005, 0.00005, 0.00006, 0.00007, 0.00007, 0.00008, 0.00009, 0.00010, 0.00010, 0.00011, 0.00012, 0.00013, 0.00014, 0.00015, 0.00015, 0.00016, 0.00017, 0.00018, 0.00019, 0.00020, 0.00021, 0.00022, 0.00023, 0.00024, 0.00025, 0.00026, 0.00027, 0.00028, 0.00029, @@ -205,14 +201,13 @@ namespace Mantid { namespace CurveFitting 0.99990, 0.99991, 0.99991, 0.99992, 0.99992, 0.99993, 0.99993, 0.99994, 0.99994, 0.99994, 0.99995, 0.99995, 0.99996, 0.99996, 0.99997, 0.99997, 0.99997, 0.99998, 0.99998, 0.99998, 0.99999, 0.99999, 1.00000, 1.00000 }; - for(size_t i = 0; i < 599; ++i) - { - if(XVALUES[i] < randv && XVALUES[i+1] > randv) - { - return ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); - } - } - return 0.0; + + const double *xp1 = std::lower_bound(XVALUES, XVALUES+600, randv); + if(xp1 == XVALUES+600) return 0.0; + const double *xm1 = xp1 - 1; + const double *ep1 = ENERGIES + (xp1 - XVALUES); + const double *em1 = ep1 - 1; + return (*em1) + (randv - *xm1)*(*ep1 - *em1)/(*xp1 - *xm1); } /** @@ -223,7 +218,7 @@ namespace Mantid { namespace CurveFitting */ double finalEnergyUranium(const double randv) { - const double ENERGIES[201] = {\ + static const double ENERGIES[201] = {\ 5959.0, 5967.7, 5976.4, 5985.1, 5993.8, 6002.5, 6011.2, 6019.9, 6028.6, 6037.3, 6046.0, 6054.8, 6063.5, 6072.2, 6080.9, 6089.6, 6098.3, 6107.0, 6115.7, 6124.4, 6133.1, 6141.8, 6150.5, 6159.2, 6167.9, 6176.6, 6185.3, 6194.0, 6202.7, 6211.4, 6220.1, 6228.9, 6237.6, 6246.3, 6255.0, 6263.7, @@ -242,8 +237,7 @@ namespace Mantid { namespace CurveFitting 7525.9, 7534.6, 7543.3, 7552.0, 7560.7, 7569.4, 7578.1, 7586.8, 7595.5, 7604.2, 7613.0, 7621.7, 7630.4, 7639.1, 7647.8, 7656.5, 7665.2, 7673.9, 7682.6, 7691.3, 7700.0 }; - - const double XVALUES[201] = {\ + static const double XVALUES[201] = {\ 0.00000, 0.00000, 0.00000, 0.00020, 0.00030, 0.00040, 0.00050, 0.00060, 0.00070, 0.00080, 0.00090, 0.00110, 0.00120, 0.00140, 0.00150, 0.00170, 0.00190, 0.00210, 0.00230, 0.00250, 0.00270, 0.00290, 0.00310, 0.00340, 0.00360, 0.00390, 0.00410, 0.00440, 0.00470, 0.00500, 0.00530, 0.00560, 0.00590, 0.00620, 0.00650, 0.00690, @@ -262,15 +256,13 @@ namespace Mantid { namespace CurveFitting 0.99930, 0.99940, 0.99950, 0.99960, 0.99960, 0.99970, 0.99980, 0.99980, 0.99990, 0.99990, 0.99990, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000, 1.00000 }; - - for(size_t i = 0; i < 200; ++i) - { - if(XVALUES[i] < randv && XVALUES[i+1] > randv) - { - return ENERGIES[i] + (randv - XVALUES[i])*(ENERGIES[i+1] - ENERGIES[i])/(XVALUES[i+1] - XVALUES[i]); - } - } - return 0.0; + + const double *xp1 = std::lower_bound(XVALUES, XVALUES+201, randv); + if(xp1 == XVALUES+201) return 0.0; + const double *xm1 = xp1 - 1; + const double *ep1 = ENERGIES + (xp1 - XVALUES); + const double *em1 = ep1 - 1; + return (*em1) + (randv - *xm1)*(*ep1 - *em1)/(*xp1 - *xm1); } //------------------------------------------------------------------------- From 355f90da867b79dcf358da3d849d03856e8ebe24 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Thu, 30 Oct 2014 14:23:39 +0000 Subject: [PATCH 29/37] Remove call to copy constructor in V3D::distance When called millions of times in a loop, e.g. Monte Carlo, then this can make a noticeably difference. Refs #10169 --- Code/Mantid/Framework/Kernel/src/V3D.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Code/Mantid/Framework/Kernel/src/V3D.cpp b/Code/Mantid/Framework/Kernel/src/V3D.cpp index 1710acb8373b..1e6025349131 100644 --- a/Code/Mantid/Framework/Kernel/src/V3D.cpp +++ b/Code/Mantid/Framework/Kernel/src/V3D.cpp @@ -482,9 +482,8 @@ V3D::cross_prod(const V3D& v) const double V3D::distance(const V3D& v) const { - V3D dif(*this); - dif-=v; - return dif.norm(); + const double dx(x-v.x), dy(y-v.y), dz(z-v.z); + return sqrt(dx*dx +dy*dy + dz*dz); } /** Calculates the zenith angle (theta) of this vector with respect to another From 9986c9e3ec6c37045a0c5bd3f498e63bd05da9ab Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Thu, 30 Oct 2014 14:47:46 +0000 Subject: [PATCH 30/37] Use a list instead of a vector inside intersection tests. The lists are small so traversal time is not an issue but it is quicker to push back to a list. Refs #10169 --- .../inc/MantidGeometry/Surfaces/Line.h | 10 ++--- .../Surfaces/LineIntersectVisit.h | 12 +++--- .../Framework/Geometry/src/Objects/Object.cpp | 14 ++++--- .../Framework/Geometry/src/Surfaces/Line.cpp | 12 +++--- .../Geometry/test/LineIntersectVisitTest.h | 39 +++++++------------ .../Mantid/Framework/Geometry/test/LineTest.h | 38 +++++++++--------- 6 files changed, 59 insertions(+), 66 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/Line.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/Line.h index e5f8957178be..45952481c5df 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/Line.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/Line.h @@ -62,7 +62,7 @@ namespace Mantid Kernel::V3D Direct; ///< Direction of outer surface (Unit Vector) int lambdaPair(const int ix,const std::pair, - std::complex >& SQ,std::vector& PntOut) const; + std::complex >& SQ,std::list& PntOut) const; public: @@ -86,10 +86,10 @@ namespace Mantid int setLine(const Kernel::V3D&,const Kernel::V3D&); ///< input Origin + direction - int intersect(std::vector&,const Quadratic&) const; - int intersect(std::vector&,const Cylinder&) const; - int intersect(std::vector&,const Plane&) const; - int intersect(std::vector&,const Sphere&) const; + int intersect(std::list&,const Quadratic&) const; + int intersect(std::list&,const Cylinder&) const; + int intersect(std::list&,const Plane&) const; + int intersect(std::list&,const Sphere&) const; }; diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/LineIntersectVisit.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/LineIntersectVisit.h index dba93f49362a..f225702a7c68 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/LineIntersectVisit.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/LineIntersectVisit.h @@ -4,7 +4,7 @@ #include "MantidGeometry/Surfaces/BaseVisit.h" #include "MantidGeometry/Surfaces/Line.h" #include "MantidKernel/V3D.h" -#include +#include namespace Mantid { @@ -56,8 +56,8 @@ namespace Mantid private: Line ATrack; ///< The line - std::vector PtOut; ///< The intersection point - std::vector DOut; ///< The distance + std::list PtOut; ///< The intersection point + std::list DOut; ///< The distance void procTrack(); @@ -66,7 +66,7 @@ namespace Mantid LineIntersectVisit(const Kernel::V3D&, const Kernel::V3D&); /// Destructor - virtual ~LineIntersectVisit() {}; + virtual ~LineIntersectVisit() {} void Accept(const Surface&); void Accept(const Quadratic&); @@ -78,10 +78,10 @@ namespace Mantid // Accessor /// Get the distance - const std::vector& getDistance() const + const std::list& getDistance() const { return DOut; } /// Get the intersection points - const std::vector& getPoints() const + const std::list& getPoints() const { return PtOut; } /// Get the number of intersection points unsigned long getNPoints() const { return (unsigned long)PtOut.size(); } diff --git a/Code/Mantid/Framework/Geometry/src/Objects/Object.cpp b/Code/Mantid/Framework/Geometry/src/Objects/Object.cpp index 344fc90dd795..221750d9bb4f 100644 --- a/Code/Mantid/Framework/Geometry/src/Objects/Object.cpp +++ b/Code/Mantid/Framework/Geometry/src/Objects/Object.cpp @@ -810,16 +810,18 @@ namespace Mantid { (*vc)->acceptVisitor(LI); } - const std::vector& IPts(LI.getPoints()); - const std::vector& dPts(LI.getDistance()); + const auto& IPts(LI.getPoints()); + const auto& dPts(LI.getDistance()); - for (unsigned int i = 0; i < IPts.size(); i++) + auto ditr = dPts.begin(); + auto itrEnd = IPts.end(); + for (auto iitr = IPts.begin(); iitr != itrEnd; ++iitr, ++ditr) { - if (dPts[i] > 0.0) // only interested in forward going points + if (*ditr > 0.0) // only interested in forward going points { // Is the point and enterance/exit Point - const int flag = calcValidType(IPts[i], UT.direction()); - UT.addPoint(flag, IPts[i], *this); + const int flag = calcValidType(*iitr, UT.direction()); + UT.addPoint(flag, *iitr, *this); } } UT.buildLink(); diff --git a/Code/Mantid/Framework/Geometry/src/Surfaces/Line.cpp b/Code/Mantid/Framework/Geometry/src/Surfaces/Line.cpp index 69e2dbbc1e1d..c4c424e9fb55 100644 --- a/Code/Mantid/Framework/Geometry/src/Surfaces/Line.cpp +++ b/Code/Mantid/Framework/Geometry/src/Surfaces/Line.cpp @@ -136,9 +136,9 @@ namespace Mantid } int - Line::lambdaPair(const int ix,const std::pair< + Line::lambdaPair(const int ix, const std::pair< std::complex,std::complex >& SQ, - std::vector& PntOut) const + std::list &PntOut) const /** Helper function to decide which roots to take. The assumption is that lambda has been solved by quadratic @@ -190,7 +190,7 @@ namespace Mantid } int - Line::intersect(std::vector& VecOut, + Line::intersect(std::list &VecOut, const Quadratic& Sur) const /** For the line that intersects the surfaces @@ -220,7 +220,7 @@ namespace Mantid } int - Line::intersect(std::vector& PntOut ,const Plane& Pln) const + Line::intersect(std::list& PntOut ,const Plane& Pln) const /** For the line that intersects the cylinder generate add the point to the VecOut, return number of points @@ -244,7 +244,7 @@ namespace Mantid } int - Line::intersect(std::vector& PntOut ,const Cylinder& Cyl) const + Line::intersect(std::list &PntOut , const Cylinder& Cyl) const /** For the line that intersects the cylinder generate add the point to the VecOut, return number of points @@ -273,7 +273,7 @@ namespace Mantid } int - Line::intersect(std::vector& PntOut ,const Sphere& Sph) const + Line::intersect(std::list &PntOut , const Sphere& Sph) const /** For the line that intersects the cylinder generate add the point to the VecOut, return number of points diff --git a/Code/Mantid/Framework/Geometry/test/LineIntersectVisitTest.h b/Code/Mantid/Framework/Geometry/test/LineIntersectVisitTest.h index 448aafecbc6e..24765b26c270 100644 --- a/Code/Mantid/Framework/Geometry/test/LineIntersectVisitTest.h +++ b/Code/Mantid/Framework/Geometry/test/LineIntersectVisitTest.h @@ -20,8 +20,8 @@ class LineIntersectVisitTest: public CxxTest::TestSuite{ void testConstructor(){ LineIntersectVisit A(V3D(-1.0,-1.0,-1.0),V3D(1.0,0.0,0.0)); TS_ASSERT_EQUALS(A.getNPoints(),0); - TS_ASSERT_EQUALS(A.getPoints(),std::vector()); - TS_ASSERT_EQUALS(A.getDistance(),std::vector()); + TS_ASSERT_EQUALS(A.getPoints(),std::list()); + TS_ASSERT_EQUALS(A.getDistance(),std::list()); } void testAcceptPlane(){ @@ -31,10 +31,10 @@ class LineIntersectVisitTest: public CxxTest::TestSuite{ TS_ASSERT_EQUALS(extractString(B),"-1 px 0\n"); A.Accept(B); TS_ASSERT_EQUALS(A.getNPoints(),1); - std::vector Pnts; + std::list Pnts; Pnts.push_back(V3D(0.0,-1.0,-1.0)); TS_ASSERT_EQUALS(A.getPoints(),Pnts); - std::vector Dist; + std::list Dist; Dist.push_back(1.0); TS_ASSERT_EQUALS(A.getDistance(),Dist); } @@ -45,14 +45,13 @@ class LineIntersectVisitTest: public CxxTest::TestSuite{ Sphere B; B.setSurface("s 0.0 0.0 0.0 2"); A.Accept(B); - std::vector pntOut; + std::list pntOut; // changed for forward going only intercepts on quadratice surfaces //pntOut.push_back(V3D(-2.0,0.0,0.0)); pntOut.push_back(V3D(2.0,0.0,0.0)); TS_ASSERT_EQUALS(A.getNPoints(),1); TS_ASSERT_EQUALS(A.getPoints(),pntOut); - std::vector Dist; - //Dist.push_back(2.0); + std::list Dist; Dist.push_back(2.0); TS_ASSERT_EQUALS(A.getDistance(),Dist); } @@ -67,19 +66,13 @@ class LineIntersectVisitTest: public CxxTest::TestSuite{ A.Accept(B); // change for forward only intercept TS_ASSERT_EQUALS(A.getNPoints(),1); - std::vector pntOut; - pntOut=A.getPoints(); - //TS_ASSERT_DELTA(pntOut[0].X(),-1,0.0000001); - //TS_ASSERT_DELTA(pntOut[0].Y(),0.0,0.0000001); - //TS_ASSERT_DELTA(pntOut[0].Z(),0.0,0.0000001); - TS_ASSERT_DELTA(pntOut[0].X(),1,0.0000001); - TS_ASSERT_DELTA(pntOut[0].Y(),0.0,0.0000001); - TS_ASSERT_DELTA(pntOut[0].Z(),0.0,0.0000001); + const auto &pntOut = A.getPoints(); + TS_ASSERT_DELTA(pntOut.front().X(),1,0.0000001); + TS_ASSERT_DELTA(pntOut.front().Y(),0.0,0.0000001); + TS_ASSERT_DELTA(pntOut.front().Z(),0.0,0.0000001); - std::vector Dist; - Dist=A.getDistance(); - TS_ASSERT_DELTA(Dist[0],1.0,0.0000001); - //TS_ASSERT_DELTA(Dist[1],1.0,0.0000001); + const auto &Dist = A.getDistance(); + TS_ASSERT_DELTA(Dist.front(),1.0,0.0000001); } void testAcceptCylinder(){ @@ -92,13 +85,13 @@ class LineIntersectVisitTest: public CxxTest::TestSuite{ TS_ASSERT_EQUALS(B.getNormal(),V3D(0,1,0)); A.Accept(B); - std::vector pntOut; + std::list pntOut; // forward only //pntOut.push_back(V3D(-1.0,0.0,0.0)); pntOut.push_back(V3D(1.0,0.0,0.0)); TS_ASSERT_EQUALS(A.getNPoints(),1); TS_ASSERT_EQUALS(A.getPoints(),pntOut); - std::vector Dist; + std::list Dist; //Dist.push_back(1.0); Dist.push_back(1.0); TS_ASSERT_EQUALS(A.getDistance(),Dist); @@ -106,14 +99,12 @@ class LineIntersectVisitTest: public CxxTest::TestSuite{ LineIntersectVisit C(V3D(1.1,0.0,0.0),V3D(-1.0,0.0,0.0)); C.Accept(B); TS_ASSERT_EQUALS(C.getNPoints(),2); - std::vector pntOut2; + std::list pntOut2; pntOut2.push_back(V3D(-1.0,0.0,0.0)); pntOut2.push_back(V3D(1.0,0.0,0.0)); TS_ASSERT_EQUALS(C.getPoints(),pntOut2); } - void testAcceptGeneral(){ - } private: std::string extractString(const Surface& pv) diff --git a/Code/Mantid/Framework/Geometry/test/LineTest.h b/Code/Mantid/Framework/Geometry/test/LineTest.h index b0a2f59dc0f6..2b2d4fcf56ba 100644 --- a/Code/Mantid/Framework/Geometry/test/LineTest.h +++ b/Code/Mantid/Framework/Geometry/test/LineTest.h @@ -142,13 +142,12 @@ class LineTest: public CxxTest::TestSuite TS_ASSERT_EQUALS(B.getRadius(),1); TS_ASSERT_EQUALS(B.getNormal(),V3D(0,1,0)); - std::vector pntOut; + std::list pntOut; A.intersect(pntOut,B); // forward only solution for cylinders - TS_ASSERT_EQUALS(pntOut.size(),1); - //TS_ASSERT_EQUALS(pntOut[0],V3D(-1.0,0.0,0.0)); - TS_ASSERT_EQUALS(pntOut[0],V3D(1.0,0.0,0.0)); + TS_ASSERT_EQUALS(pntOut.size(),1); + TS_ASSERT_EQUALS(pntOut.front(),V3D(1.0,0.0,0.0)); } //A Line with equation equivalent to x axis will cut A Cylinder with 1 radius with center at 0,0,0 y axis normal @@ -164,12 +163,13 @@ class LineTest: public CxxTest::TestSuite TS_ASSERT_EQUALS(B.getRadius(),1); TS_ASSERT_EQUALS(B.getNormal(),V3D(0,1,0)); - std::vector pntOut; + std::list pntOut; A.intersect(pntOut,B); - TS_ASSERT_EQUALS(pntOut.size(),2); - TS_ASSERT_EQUALS(pntOut[0],V3D(1.0,0.0,0.0)); - TS_ASSERT_EQUALS(pntOut[1],V3D(-1.0,0.0,0.0)); + TS_ASSERT_EQUALS(pntOut.size(),2); + auto itr = pntOut.begin(); + TS_ASSERT_EQUALS(*(itr++), V3D(1.0,0.0,0.0)); + TS_ASSERT_EQUALS(*itr,V3D(-1.0,0.0,0.0)); } //A Line with equation equivalent to x axis will cut a plane YZ with equation x=5 will cut at one point 5,0,0 @@ -181,11 +181,11 @@ class LineTest: public CxxTest::TestSuite Plane B; TS_ASSERT_EQUALS(B.setSurface("px 5 0 0"),0); - std::vector pntOut; + std::list pntOut; A.intersect(pntOut,B); - TS_ASSERT_EQUALS(pntOut.size(),1); - TS_ASSERT_EQUALS(pntOut[0],V3D(5.0,0.0,0.0)); + TS_ASSERT_EQUALS(pntOut.size(),1); + TS_ASSERT_EQUALS(pntOut.front(),V3D(5.0,0.0,0.0)); } //A Line with equation equivalent to x axis will cut A sphere with 2 radius with center at 0,0,0 @@ -197,12 +197,11 @@ class LineTest: public CxxTest::TestSuite Sphere B; B.setSurface("s 0.0 0.0 0.0 2"); - std::vector pntOut; + std::list pntOut; A.intersect(pntOut,B); // forward only solutions - TS_ASSERT_EQUALS(pntOut.size(),1); - //TS_ASSERT_EQUALS(pntOut[0],V3D(-2.0,0.0,0.0)); - TS_ASSERT_EQUALS(pntOut[0],V3D(2.0,0.0,0.0)); + TS_ASSERT_EQUALS(pntOut.size(),1); + TS_ASSERT_EQUALS(pntOut.front(),V3D(2.0,0.0,0.0)); } //A Line with equation equivalent to x axis starting at -10 will cut A sphere with 2 radius with center at 0,0,0 @@ -214,11 +213,12 @@ class LineTest: public CxxTest::TestSuite Sphere B; B.setSurface("s 0.0 0.0 0.0 2"); - std::vector pntOut; + std::list pntOut; A.intersect(pntOut,B); - TS_ASSERT_EQUALS(pntOut.size(),2); - TS_ASSERT_EQUALS(pntOut[0],V3D(2.0,0.0,0.0)); - TS_ASSERT_EQUALS(pntOut[1],V3D(-2.0,0.0,0.0)); + TS_ASSERT_EQUALS(pntOut.size(),2); + auto itr = pntOut.begin(); + TS_ASSERT_EQUALS(*(itr++), V3D(2.0,0.0,0.0)); + TS_ASSERT_EQUALS(*itr, V3D(-2.0,0.0,0.0)); } }; From bfd7075d8219f63bb5fd353866ff011895ca1e22 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Thu, 30 Oct 2014 14:48:13 +0000 Subject: [PATCH 31/37] A few minor things to improve efficiency for intersection calcs Refs #10169 --- .../Geometry/inc/MantidGeometry/Surfaces/Plane.h | 2 +- .../Framework/Geometry/src/Objects/RuleItems.cpp | 15 +++++++++------ .../Framework/Geometry/src/Surfaces/Plane.cpp | 3 +-- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/Plane.h b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/Plane.h index 8e4c34e0d412..cdb7e3c93834 100644 --- a/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/Plane.h +++ b/Code/Mantid/Framework/Geometry/inc/MantidGeometry/Surfaces/Plane.h @@ -82,7 +82,7 @@ namespace Mantid double distance(const Kernel::V3D&) const; ///< distance from a point double getDistance() const { return Dist; } ///< Distance from origin - Kernel::V3D getNormal() const { return NormV; } ///< Normal to plane (+ve surface) + const Kernel::V3D & getNormal() const { return NormV; } ///< Normal to plane (+ve surface) void rotate(const Kernel::Matrix&); void displace(const Kernel::V3D&); diff --git a/Code/Mantid/Framework/Geometry/src/Objects/RuleItems.cpp b/Code/Mantid/Framework/Geometry/src/Objects/RuleItems.cpp index b78500bee17f..97a69ac7534f 100644 --- a/Code/Mantid/Framework/Geometry/src/Objects/RuleItems.cpp +++ b/Code/Mantid/Framework/Geometry/src/Objects/RuleItems.cpp @@ -292,9 +292,11 @@ Intersection::isValid(const Kernel::V3D& Vec) const @retval 0 :: Vec is outside object. */ { - if (!A || !B) - return false; - return (A->isValid(Vec) && B->isValid(Vec)) ? true : false; + if(A && B) + { + return (A->isValid(Vec) && B->isValid(Vec)); + } + return false; } bool @@ -849,9 +851,10 @@ SurfPoint::isValid(const Kernel::V3D& Pt) const */ { if (key) - return (key->side(Pt)*sign)>=0 ? true : false; - else - return false; + { + return (key->side(Pt)*sign)>=0; + } + return false; } bool diff --git a/Code/Mantid/Framework/Geometry/src/Surfaces/Plane.cpp b/Code/Mantid/Framework/Geometry/src/Surfaces/Plane.cpp index 95962e6acb5e..4e5004b92152 100644 --- a/Code/Mantid/Framework/Geometry/src/Surfaces/Plane.cpp +++ b/Code/Mantid/Framework/Geometry/src/Surfaces/Plane.cpp @@ -220,8 +220,7 @@ Plane::side(const Kernel::V3D& A) const @retval 0 :: A is on the plane itself (within tolerence) */ { - double Dp=NormV.scalar_prod(A); - Dp-=Dist; + double Dp=NormV.scalar_prod(A)-Dist; if (Tolerance0) ? 1 : -1; return 0; From de1fa94c6fe714988d96a5c7c1e791a38a28b07a Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Thu, 30 Oct 2014 15:09:57 +0000 Subject: [PATCH 32/37] Reduce the number of events per run. It brings the unit test time down to an acceptable place. Refs #10169 --- .../Framework/CurveFitting/test/CalculateMSVesuvioTest.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h index 0fd918d229bc..1c58cb8e71c6 100644 --- a/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h +++ b/Code/Mantid/Framework/CurveFitting/test/CalculateMSVesuvioTest.h @@ -37,7 +37,7 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite TS_ASSERT_THROWS_NOTHING(alg->execute()); TS_ASSERT(alg->isExecuted()); - checkOutputValuesAsExpected(alg, 0.0113021908, 0.0028218125); + checkOutputValuesAsExpected(alg, 0.0099824991, 0.0020558473); } // ------------------------ Failure Cases ----------------------------------------- @@ -122,6 +122,9 @@ class CalculateMSVesuvioTest : public CxxTest::TestSuite 27.50000, 4.0172841E-02, 15.07701}; alg->setProperty("AtomicProperties", std::vector(sampleProps, sampleProps + 9)); alg->setProperty("BeamRadius", 2.5); + // reduce number of events for test purposes + alg->setProperty("NumEventsPerRun", 10000); + // outputs alg->setPropertyValue("TotalScatteringWS", "__unused_for_child"); alg->setPropertyValue("MultipleScatteringWS", "__unused_for_child"); From 68bf42abc5949dd3de7fed5dc64d82b216fb171d Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Thu, 30 Oct 2014 16:46:48 +0000 Subject: [PATCH 33/37] Add include line for std::accumulate. Refs #10169 --- Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp b/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp index 9b51dfac49f4..a55a3a134365 100644 --- a/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp @@ -4,6 +4,7 @@ #include "MantidCurveFitting/MSVesuvioHelpers.h" #include +#include namespace Mantid { namespace CurveFitting { From ed583fa2afffa5da424c05fe8e4f15fa6204e0ed Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Thu, 30 Oct 2014 16:47:10 +0000 Subject: [PATCH 34/37] Fix boost random usage for older boost versions. Refs #10169 --- .../inc/MantidCurveFitting/MSVesuvioHelpers.h | 1 - .../CurveFitting/src/MSVesuvioHelpers.cpp | 14 ++++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/MSVesuvioHelpers.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/MSVesuvioHelpers.h index 89cfe9088d31..9084339ea9eb 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/MSVesuvioHelpers.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/MSVesuvioHelpers.h @@ -7,7 +7,6 @@ #include #include - namespace Mantid { namespace CurveFitting { namespace MSVesuvioHelper diff --git a/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp b/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp index a55a3a134365..7b6aef7d6365 100644 --- a/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/MSVesuvioHelpers.cpp @@ -6,6 +6,8 @@ #include #include +#include + namespace Mantid { namespace CurveFitting { namespace MSVesuvioHelper @@ -279,14 +281,18 @@ namespace Mantid { namespace CurveFitting /// Returns a flat random number between 0.0 & 1.0 double RandomNumberGenerator::flat() { - return uniform_double()(m_generator, - uniform_double::param_type(0.0, 1.0)); + typedef boost::variate_generator uniform_generator; + return uniform_generator(m_generator, + uniform_double(0.0, 1.0))(); } /// Returns a random number distributed by a normal distribution double RandomNumberGenerator::gaussian(const double mean, const double sigma) { - return gaussian_double()(m_generator, - gaussian_double::param_type(mean, sigma)); + typedef boost::variate_generator gauss_generator; + return gauss_generator(m_generator, + gaussian_double(mean, sigma))(); } //------------------------------------------------------------------------- From 8b32bd0d0e1ad1ec8cf1f374ea387985a154be60 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Thu, 30 Oct 2014 17:12:58 +0000 Subject: [PATCH 35/37] Fix ambiguity error on intel compiler. Refs #10169 --- Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp index ef42ec2f43a3..17a999da9740 100644 --- a/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp +++ b/Code/Mantid/Framework/CurveFitting/src/CalculateMSVesuvio.cpp @@ -143,9 +143,9 @@ namespace Mantid m_randgen = new MSVesuvioHelper::RandomNumberGenerator(getProperty("Seed")); // Setup progress - const int64_t nhist = static_cast(m_inputWS->getNumberHistograms()); + const size_t nhist = m_inputWS->getNumberHistograms(); m_progress = new API::Progress(this, 0.0, 1.0, nhist*m_nruns*2); - for(int64_t i = 0; i < nhist; ++i) + for(size_t i = 0; i < nhist; ++i) { // Copy over the X-values From 512cc0ea1eff07c59386bd91dcacab626fbd8807 Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Fri, 31 Oct 2014 08:00:16 +0000 Subject: [PATCH 36/37] Change the category the algorithm to an allowed existing one. Refs #10169 --- .../CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index 89080ea516ec..82069d607175 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -82,7 +82,7 @@ namespace Mantid /// @copydoc Algorithm::version virtual int version() const { return 1; } /// @copydoc Algorithm::category - virtual const std::string category() const { return "Corrections"; } + virtual const std::string category() const { return "ISIS"; } /// @copydoc Algorithm::summary virtual const std::string summary() const { From 6e4da70533e2834f46c6db42bd9554b9a07f62ac Mon Sep 17 00:00:00 2001 From: Martyn Gigg Date: Fri, 31 Oct 2014 12:11:40 +0000 Subject: [PATCH 37/37] Add documentation file for algorithm. Refs #10169 --- .../MantidCurveFitting/CalculateMSVesuvio.h | 4 +- .../algorithms/CalculateMSVesuvio-v1.rst | 59 +++++++++++++++++++ 2 files changed, 61 insertions(+), 2 deletions(-) create mode 100644 Code/Mantid/docs/source/algorithms/CalculateMSVesuvio-v1.rst diff --git a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h index 82069d607175..0938057d47fe 100644 --- a/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h +++ b/Code/Mantid/Framework/CurveFitting/inc/MantidCurveFitting/CalculateMSVesuvio.h @@ -86,8 +86,8 @@ namespace Mantid /// @copydoc Algorithm::summary virtual const std::string summary() const { - return "Corrects for the effects of multiple scattering " - "on a flat plate or cylindrical sample."; + return "Calculates the contributions of multiple scattering " + "on a flat plate sample for VESUVIO"; } private: diff --git a/Code/Mantid/docs/source/algorithms/CalculateMSVesuvio-v1.rst b/Code/Mantid/docs/source/algorithms/CalculateMSVesuvio-v1.rst new file mode 100644 index 000000000000..8eddae38cace --- /dev/null +++ b/Code/Mantid/docs/source/algorithms/CalculateMSVesuvio-v1.rst @@ -0,0 +1,59 @@ +.. algorithm:: + +.. summary:: + +.. alias:: + +.. properties:: + +Description +----------- + +Calculates the multiple scattering contribution for deep inelastic neutron scattering on +the `Vesuvio `__ instrument at +ISIS. The algorithm follows the procedures defined by J. Mayers et al. [1]_. + + +Usage +----- + +.. code-block:: python + + runs = "" # fill in run numbers here + ip_file = "" # fill in IP file here + data = LoadVesuvio(Filename=, SpectrumList=spectra, + Mode="SingleDifference", InstrumentParFile=ip_file) + # Cut it down to the typical range + data = CropWorkspace(raw_ws,XMin=50.0,XMax=562.0) + # Coarser binning + data = Rebin(raw_ws, Params=[49.5, 1.0, 562.5]) + + # Create sample shape + height = 0.1 # y-dir (m) + width = 0.1 # x-dir (m) + thick = 0.005 # z-dir (m) + + half_height, half_width, half_thick = 0.5*height, 0.5*width, 0.5*thick + xml_str = \ + " " \ + + " " % (half_width,-half_height,half_thick) \ + + " " % (half_width, half_height, half_thick) \ + + " " % (half_width, -half_height, -half_thick) \ + + " " % (-half_width, -half_height, half_thick) \ + + "" + CreateSampleShape(data, xml_str) + atom_props = [1.007900, 0.9272392, 5.003738, + 16.00000, 3.2587662E-02, 13.92299, + 27.50000, 4.0172841E-02, 15.07701] + tot_scatter, ms_scatter = \ + CalculateMSVesuvio(data, NoOfMasses=3, SampleDensity=241, AtomicProperties=atom_props, + BeamRadius=2.5) + +References +---------- + +.. [1] J. Mayers, A.L. Fielding and R. Senesi, `Nucl. Inst Methods A 481, 454(2002) `__ + + + +.. categories::