Skip to content
Snippets Groups Projects
UnitConversion.cpp 5.11 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_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