Skip to content
Snippets Groups Projects
UnitConversion.cpp 5.81 KiB
Newer Older
#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_sptr srcUnit = UnitFactory::Instance().create(src);
  Unit_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(Unit &srcUnit, 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);
  }
}
/**
 * Convert a single value between the given units (overload for Unit objects)
 * @param twoTheta :: The scattering angle (in radians)
 * @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 double twoTheta, const double efixed) {
  return convertToElasticQ(twoTheta, 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(Unit &srcUnit, 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);
  const double tof = srcUnit.convertSingleToTOF(srcValue, l1, l2, twoTheta,
                                                emodeAsInt, efixed, unused);
  return destUnit.convertSingleFromTOF(tof, l1, l2, twoTheta, emodeAsInt,
                                       efixed, unused);
}
/**
 *  Convert a to ElasticQ
 *  @param twoTheta :: The scattering angle (in radians)
 *  @param efixed ::   Value of fixed energy: EI (emode=1) or EF (emode=2) (in
 * meV)
 * @return The value converted to ElasticQ
 */
double UnitConversion::convertToElasticQ(const double twoTheta,
                                         const double efixed) {
  Mantid::Kernel::Units::Energy energyUnit;
  double wavelengthFactor(0.0), wavelengthPower(0.0);
  energyUnit.quickConversion("Wavelength", wavelengthFactor, wavelengthPower);
  const double stheta = std::sin(twoTheta);
  // Calculate the wavelength to allow it to be used to convert to elasticQ.
  double wavelength = wavelengthFactor * std::pow(efixed, wavelengthPower);
  // The MomentumTransfer value.
  return 4.0 * M_PI * stheta / wavelength;
}
} // namespace Kernel
} // namespace Mantid