Skip to content

Commit

Permalink
Add a simple UnitConversion class. Refs #5624
Browse files Browse the repository at this point in the history
Allows conversion between units using a single call.
  • Loading branch information
martyngigg committed Jul 17, 2012
1 parent d982703 commit 078cb1f
Show file tree
Hide file tree
Showing 4 changed files with 283 additions and 12 deletions.
27 changes: 15 additions & 12 deletions Code/Mantid/Framework/Kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -35,19 +35,19 @@ set ( SRC_FILES
src/LogFilter.cpp
src/LogParser.cpp
src/Logger.cpp
src/Math/Distributions/BoseEinsteinDistribution.cpp
src/MagneticIon.cpp
src/MandatoryValidator.cpp
src/MantidVersion.cpp
src/MaskedProperty.cpp
src/Math/Distributions/BoseEinsteinDistribution.cpp
src/Matrix.cpp
src/MatrixProperty.cpp
src/Memory.cpp
src/MersenneTwister.cpp
src/MultiFileNameParser.cpp
src/MultiFileValidator.cpp
src/NDRandomNumberGenerator.cpp
src/NDPseudoRandomNumberGenerator.cpp
src/NDRandomNumberGenerator.cpp
src/NeutronAtom.cpp
src/NexusTestHelper.cpp
src/ProgressBase.cpp
Expand Down Expand Up @@ -75,6 +75,7 @@ set ( SRC_FILES
src/TimeSplitter.cpp
src/Timer.cpp
src/Unit.cpp
src/UnitConversion.cpp
src/UnitFactory.cpp
src/UserStringParser.cpp
src/Utils.cpp
Expand Down Expand Up @@ -133,6 +134,7 @@ set ( INC_FILES
inc/MantidKernel/FilterChannel.h
inc/MantidKernel/FloatingPointComparison.h
inc/MantidKernel/FreeBlock.h
inc/MantidKernel/FunctionTask.h
inc/MantidKernel/Glob.h
inc/MantidKernel/IPropertyManager.h
inc/MantidKernel/IPropertySettings.h
Expand All @@ -147,21 +149,21 @@ set ( INC_FILES
inc/MantidKernel/LogFilter.h
inc/MantidKernel/LogParser.h
inc/MantidKernel/Logger.h
inc/MantidKernel/Math/Distributions/BoseEinsteinDistribution.h
inc/MantidKernel/MRUList.h
inc/MantidKernel/MagneticIon.h
inc/MantidKernel/MandatoryValidator.h
inc/MantidKernel/MantidVersion.h
inc/MantidKernel/MaskedProperty.h
inc/MantidKernel/Math/Distributions/BoseEinsteinDistribution.h
inc/MantidKernel/Matrix.h
inc/MantidKernel/MatrixProperty.h
inc/MantidKernel/Memory.h
inc/MantidKernel/MersenneTwister.h
inc/MantidKernel/MultiFileNameParser.h
inc/MantidKernel/MultiThreaded.h
inc/MantidKernel/MultiFileValidator.h
inc/MantidKernel/NDRandomNumberGenerator.h
inc/MantidKernel/MultiThreaded.h
inc/MantidKernel/NDPseudoRandomNumberGenerator.h
inc/MantidKernel/NDRandomNumberGenerator.h
inc/MantidKernel/NeutronAtom.h
inc/MantidKernel/NexusTestHelper.h
inc/MantidKernel/NullValidator.h
Expand All @@ -174,8 +176,8 @@ set ( INC_FILES
inc/MantidKernel/PropertyManagerOwner.h
inc/MantidKernel/PropertyWithValue.h
inc/MantidKernel/PseudoRandomNumberGenerator.h
inc/MantidKernel/Quat.h
inc/MantidKernel/QuasiRandomNumberSequence.h
inc/MantidKernel/Quat.h
inc/MantidKernel/ReadLock.h
inc/MantidKernel/RebinParamsValidator.h
inc/MantidKernel/RegexStrings.h
Expand All @@ -186,20 +188,20 @@ set ( INC_FILES
inc/MantidKernel/Statistics.h
inc/MantidKernel/Strings.h
inc/MantidKernel/System.h
inc/MantidKernel/Task.h
inc/MantidKernel/TestChannel.h
inc/MantidKernel/ThreadPool.h
inc/MantidKernel/ThreadPool.h
inc/MantidKernel/ThreadPoolRunnable.h
inc/MantidKernel/ThreadSafeLogStream.h
inc/MantidKernel/ThreadScheduler.h
inc/MantidKernel/ThreadSchedulerMutexes.h
inc/MantidKernel/Task.h
inc/MantidKernel/FunctionTask.h
inc/MantidKernel/TimeSeriesProperty.h
inc/MantidKernel/TimeSplitter.h
inc/MantidKernel/Timer.h
inc/MantidKernel/Tolerance.h
inc/MantidKernel/TypedValidator.h
inc/MantidKernel/Unit.h
inc/MantidKernel/UnitConversion.h
inc/MantidKernel/UnitFactory.h
inc/MantidKernel/UserStringParser.h
inc/MantidKernel/Utils.h
Expand Down Expand Up @@ -243,9 +245,9 @@ set ( TEST_FILES
test/GlobTest.h
test/IPropertySettingsTest.h
test/ISaveableTest.h
test/IValidatorTest.h
test/InstrumentInfoTest.h
test/InterpolationTest.h
test/IValidatorTest.h
test/ListValidatorTest.h
test/LogFilterTest.h
test/LogParserTest.h
Expand All @@ -261,9 +263,9 @@ set ( TEST_FILES
test/MultiFileNameParserTest.h
test/MultiFileValidatorTest.h
test/MutexTest.h
test/NeutronAtomTest.h
test/NDRandomNumberGeneratorTest.h
test/NDPseudoRandomNumberGeneratorTest.h
test/NDRandomNumberGeneratorTest.h
test/NeutronAtomTest.h
test/NullValidatorTest.h
test/ProgressBaseTest.h
test/ProgressTextTest.h
Expand All @@ -289,6 +291,7 @@ set ( TEST_FILES
test/TimeSplitterTest.h
test/TimerTest.h
test/TypedValidatorTest.h
test/UnitConversionTest.h
test/UnitFactoryTest.h
test/UnitTest.h
test/UserStringParserTest.h
Expand Down
68 changes: 68 additions & 0 deletions Code/Mantid/Framework/Kernel/inc/MantidKernel/UnitConversion.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#ifndef MANTID_KERNEL_UNITCONVERSION_H_
#define MANTID_KERNEL_UNITCONVERSION_H_
/**
Copyright © 2012 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 <http://www.gnu.org/licenses/>.
File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
#include "MantidKernel/DeltaEMode.h"
#include "MantidKernel/DllConfig.h"
#include <string>

namespace Mantid
{
namespace Kernel
{
//-------------------------------------------------------------------------
// Forward declations
//-------------------------------------------------------------------------
class Unit;

/**
* A set of static helper methods to perform conversions between units
*/
class MANTID_KERNEL_DLL UnitConversion
{
public:
/// Convert a single value between the given units (as strings)
static double run(const std::string & src, const std::string & dest,
const double srcValue,
const double l1, const double l2,
const double twoTheta, const DeltaEMode::Type emode, const double efixed);
/// Convert a single value between the given units
static double run(const Unit & srcUnit, const Unit & destUnit,
const double srcValue,
const double l1, const double l2,
const double twoTheta, const DeltaEMode::Type emode, const double efixed);

private:
/// Perform a quick conversion
static double convertQuickly(const double srcValue, const double factor, const double power);
/// Convert through TOF
static double convertViaTOF(const Unit & srcUnit, const Unit & destUnit,
const double srcValue,
const double l1, const double l2,
const double twoTheta, const DeltaEMode::Type emode, const double efixed);
};


} // namespace Kernel
} // namespace Mantid

#endif /* MANTID_KERNEL_UNITCONVERSION_H_ */
120 changes: 120 additions & 0 deletions Code/Mantid/Framework/Kernel/src/UnitConversion.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
#include "MantidKernel/UnitConversion.h"
#include "MantidKernel/Unit.h"
#include "MantidKernel/UnitFactory.h"

#include <boost/lexical_cast.hpp>

#include <cmath>

namespace Mantid
{
namespace Kernel
{
/**
* Convert a single value between the given units (as strings)
* @param src :: The starting unit
* @param dest :: The destination unit
* @param srcValue :: The value to convert
* @param l1 :: The source-sample distance (in metres)
* @param l2 :: The sample-detector distance (in metres)
* @param twoTheta :: The scattering angle (in radians)
* @param emode :: The energy mode enumeration
* @param efixed :: Value of fixed energy: EI (emode=1) or EF (emode=2) (in meV)
* @return The value converted to the destination unit
*/
double UnitConversion::run(const std::string & src, const std::string & dest,
const double srcValue,
const double l1, const double l2,
const double twoTheta, const DeltaEMode::Type emode, const double efixed)
{
Unit_const_sptr srcUnit = UnitFactory::Instance().create(src);
Unit_const_sptr destUnit = UnitFactory::Instance().create(dest);
return UnitConversion::run(*srcUnit, *destUnit, srcValue, l1, l2, twoTheta, emode, efixed);
}

/**
* Convert a single value between the given units (overload for Unit objects)
* @param srcUnit :: The starting unit
* @param destUnit :: The destination unit
* @param srcValue :: The value to convert
* @param l1 :: The source-sample distance (in metres)
* @param l2 :: The sample-detector distance (in metres)
* @param twoTheta :: The scattering angle (in radians)
* @param emode :: The energy mode enumeration
* @param efixed :: Value of fixed energy: EI (emode=1) or EF (emode=2) (in meV)
* @return The value converted to the destination unit
*/
double UnitConversion::run(const Unit & srcUnit, const Unit & destUnit,
const double srcValue,
const double l1, const double l2,
const double twoTheta, const DeltaEMode::Type emode, const double efixed)
{
double factor(0.0), power(0.0);
if(srcUnit.quickConversion(destUnit, factor, power))
{
return convertQuickly(srcValue, factor, power);
}
else
{
return convertViaTOF(srcUnit, destUnit, srcValue, l1, l2, twoTheta, emode, efixed);
}
}

//---------------------------------------------------------------------------------------------
// Private methods
//---------------------------------------------------------------------------------------------

/**
* Perform a quick conversion by raising the value to a power & multiplying by the factor
* @param srcValue :: The value to convert
* @param factor :: A multiplicative constant
* @param power :: Raise the src value to this power
* @return The converted unit
*/
double UnitConversion::convertQuickly(const double srcValue, const double factor,
const double power)
{
return factor *std::pow(srcValue, power);
}

/**
* @param srcUnit :: The starting unit
* @param destUnit :: The destination unit
* @param srcValue :: The value to convert
* @param l1 :: The source-sample distance (in metres)
* @param l2 :: The sample-detector distance (in metres)
* @param twoTheta :: The scattering angle (in radians)
* @param emode :: The energy mode enumeration
* @param efixed :: Value of fixed energy: EI (emode=1) or EF (emode=2) (in meV)
* @return The value converted to the destination unit
*/
double UnitConversion::convertViaTOF(const Unit & srcUnit, const Unit & destUnit,
const double srcValue,
const double l1, const double l2,
const double twoTheta, const DeltaEMode::Type emode,
const double efixed)
{
// Translate the emode to the int formulation
int emodeAsInt(0);
switch(emode)
{
case DeltaEMode::Elastic: emodeAsInt = 0;
break;
case DeltaEMode::Direct: emodeAsInt = 1;
break;
case DeltaEMode::Indirect: emodeAsInt = 2;
break;
default: throw std::invalid_argument("UnitConversion::convertViaTOF - Unknown emode " + boost::lexical_cast<std::string>(emode));
};

const double unused(0.0);
// The unit API requires a non-const input unit but it doesn't make sense for this method to accept a non-const value
Unit & nonConstSrc = const_cast<Unit&>(srcUnit);
const double tof = nonConstSrc.convertSingleToTOF(srcValue, l1, l2, twoTheta, emodeAsInt, efixed, unused);
Unit & nonConstDest = const_cast<Unit&>(destUnit);
return nonConstDest.convertSingleFromTOF(tof, l1, l2, twoTheta, emodeAsInt, efixed, unused);
}


} // namespace Kernel
} // namespace Mantid
80 changes: 80 additions & 0 deletions Code/Mantid/Framework/Kernel/test/UnitConversionTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
#ifndef MANTID_KERNEL_UNITCONVERTERTEST_H_
#define MANTID_KERNEL_UNITCONVERTERTEST_H_

#include <cxxtest/TestSuite.h>
#include "MantidKernel/UnitConversion.h"
#include "MantidKernel/Exception.h"
#include "MantidKernel/PhysicalConstants.h"

#include <iomanip>

using Mantid::Kernel::UnitConversion;

class UnitConversionTest : 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 UnitConversionTest *createSuite() { return new UnitConversionTest(); }
static void destroySuite( UnitConversionTest *suite ) { delete suite; }

void test_Run_Throws_When_Src_Unit_Is_Unknown()
{
using namespace Mantid::Kernel::Exception;
using Mantid::Kernel::DeltaEMode;
TS_ASSERT_THROWS(UnitConversion::run("zxzxz", "Wavelength", 0.0,0.0,0.0,0.0,DeltaEMode::Elastic,0.0), NotFoundError);
}

void test_Run_Throws_When_Dest_Unit_Is_Unknown()
{
using namespace Mantid::Kernel::Exception;
using Mantid::Kernel::DeltaEMode;
TS_ASSERT_THROWS(UnitConversion::run("Wavelength", "xszfsdf", 0.0,0.0,0.0,0.0,DeltaEMode::Elastic,0.0), NotFoundError);
}

void test_Run_Gives_Throws_With_Unknown_EMode_When_Applicable()
{
// Important to test as if another mode is added but this class is not updated to deal with it
using namespace Mantid::Kernel::Exception;
using Mantid::Kernel::DeltaEMode;
const unsigned int mode = 1000;
TS_ASSERT_THROWS(UnitConversion::run("Wavelength", "MomentumTransfer", 0.0,0.0,0.0,0.0,(DeltaEMode::Type)mode,0.0), std::invalid_argument);
}

void test_Run_Gives_Correct_Value_For_Units_That_Can_Be_Converted_By_Simply_Factor_And_Geometry_Is_Ignored()
{
using Mantid::Kernel::DeltaEMode;

const std::string & srcUnit = "Wavelength";
const double srcValue(1.5); // In angstroms

const std::string & destUnit = "Momentum";
const double dummy(0.0);
const DeltaEMode::Type dummyMode(DeltaEMode::Indirect);
const double expected(2.0*M_PI/srcValue);

double result(-1.0);
TS_ASSERT_THROWS_NOTHING(result = UnitConversion::run(srcUnit, destUnit, srcValue, dummy, dummy, dummy, dummyMode,dummy));
TS_ASSERT_DELTA(result, expected, 1e-12);
}

void test_Run_Gives_Correct_Value_For_Units_That_Go_Through_TOF()
{
using Mantid::Kernel::DeltaEMode;

const std::string & srcUnit = "Wavelength";
const double srcValue(1.5); // In angstroms
const std::string & destUnit = "MomentumTransfer";

const double l1(10.0), l2(1.1), twoTheta(10.0*M_PI/180.0), efixed(12.0);
const DeltaEMode::Type emode = DeltaEMode::Direct;

const double expected(0.437943919458);
double result(-1.0);
TS_ASSERT_THROWS_NOTHING(result = UnitConversion::run(srcUnit, destUnit, srcValue, l1, l2, twoTheta, emode,efixed));
TS_ASSERT_DELTA(result, expected, 1e-12);
}
};


#endif /* MANTID_KERNEL_UNITCONVERTERTEST_H_ */

0 comments on commit 078cb1f

Please sign in to comment.