Skip to content
Snippets Groups Projects
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
RemoveExpDecay.cpp 8.27 KiB
//----------------------------------------------------------------------
// Includes
//----------------------------------------------------------------------
#include <cmath>
#include <vector>

#include "MantidAPI/Workspace.h"
#include "MantidAPI/IFunction.h"
#include "MantidKernel/PhysicalConstants.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidAlgorithms/RemoveExpDecay.h"

namespace Mantid
{
namespace Algorithms
{

using namespace Kernel;
using API::Progress;
using std::size_t;

// Register the class into the algorithm factory
DECLARE_ALGORITHM(MuonRemoveExpDecay)


/** Initialisation method. Declares properties to be used in algorithm.
 *
 */
void MuonRemoveExpDecay::init()
{
  declareProperty(new API::WorkspaceProperty<API::MatrixWorkspace>("InputWorkspace", "",
      Direction::Input), "The name of the input 2D workspace.");
  declareProperty(new API::WorkspaceProperty<API::MatrixWorkspace>("OutputWorkspace", "",
      Direction::Output), "The name of the output 2D workspace.");
  std::vector<int> empty;
  declareProperty(new Kernel::ArrayProperty<int>("Spectra", empty), "The workspace indices to remove the exponential decay from.");
}

/** Executes the algorithm
 *
 */
void MuonRemoveExpDecay::exec()
{
  std::vector<int> spectra = getProperty("Spectra");

  //Get original workspace
  API::MatrixWorkspace_const_sptr inputWS = getProperty("InputWorkspace");
  int numSpectra = static_cast<int>(inputWS->size() / inputWS->blocksize());

  //Create output workspace with same dimensions as input
  API::MatrixWorkspace_sptr outputWS = getProperty("OutputWorkspace");
  if ( inputWS != outputWS )
  {
    outputWS = API::WorkspaceFactory::Instance().create(inputWS);
  }
  //Copy over the X vaules to avoid a race-condition in main the loop
  PARALLEL_FOR2(inputWS,outputWS)
  for (int i = 0; i < numSpectra; ++i)
  {
    PARALLEL_START_INTERUPT_REGION

    outputWS->dataX(i) = inputWS->readX(i);

    PARALLEL_END_INTERUPT_REGION
  }
  PARALLEL_CHECK_INTERUPT_REGION

  if (spectra.empty())
  {
    Progress prog(this, 0.0, 1.0, numSpectra);
    //Do all the spectra	
    PARALLEL_FOR2(inputWS,outputWS)
    for (int i = 0; i < numSpectra; ++i)
    {
      PARALLEL_START_INTERUPT_REGION

      // Make sure reference to input X vector is obtained after output one because in the case
      // where the input & output workspaces are the same, it might move if the vectors were shared.
      const MantidVec& xIn = inputWS->readX(i);

      MantidVec& yOut = outputWS->dataY(i);
      MantidVec& eOut = outputWS->dataE(i);

      removeDecayData(xIn, inputWS->readY(i), yOut);
      removeDecayError(xIn, inputWS->readE(i), eOut);
      double normConst = calNormalisationConst(outputWS, i);

      // do scaling and substract by minus 1.0
      const size_t nbins = outputWS->dataY(i).size();
      for (size_t j = 0; j < nbins; j++)
      {
        yOut[j] /= normConst;
        yOut[j] -= 1.0;
        eOut[j] /= normConst;
      }   

      prog.report();
      PARALLEL_END_INTERUPT_REGION
    }
    PARALLEL_CHECK_INTERUPT_REGION
  }
  else
  {
    Progress prog(this, 0.0, 1.0, numSpectra + spectra.size());
    if (inputWS != outputWS)
    {

      //Copy all the Y and E data
      PARALLEL_FOR2(inputWS,outputWS)
      for (int64_t i = 0; i < int64_t(numSpectra); ++i)
      {
        PARALLEL_START_INTERUPT_REGION
        outputWS->dataY(i) = inputWS->readY(i);
        outputWS->dataE(i) = inputWS->readE(i);
        prog.report();
        PARALLEL_END_INTERUPT_REGION
      }
      PARALLEL_CHECK_INTERUPT_REGION
    }

    //Do the specified spectra only
    int specLength = static_cast<int>(spectra.size());
    PARALLEL_FOR2(inputWS,outputWS)
    for (int i = 0; i < specLength; ++i)
    {
      PARALLEL_START_INTERUPT_REGION
      if (spectra[i] > numSpectra)
      {
        g_log.error("Spectra size greater than the number of spectra!");
        throw std::invalid_argument("Spectra size greater than the number of spectra!");
      }
      // Get references to the x data
      const MantidVec& xIn = inputWS->readX(spectra[i]);
      MantidVec& yOut = outputWS->dataY(spectra[i]);
      MantidVec& eOut = outputWS->dataE(spectra[i]);

      removeDecayData(xIn, inputWS->readY(spectra[i]), yOut);
      removeDecayError(xIn, inputWS->readE(spectra[i]), eOut);
      double normConst = calNormalisationConst(outputWS, spectra[i]);

      // do scaling and substract by minus 1.0
      const size_t nbins = outputWS->dataY(i).size();
      for (size_t j = 0; j < nbins; j++)
      {
        yOut[j] /= normConst;
        yOut[j] -= 1.0;
        eOut[j] /= normConst;
      }   

      prog.report();
      PARALLEL_END_INTERUPT_REGION
    }
    PARALLEL_CHECK_INTERUPT_REGION
  }

  // Update Y axis units
  outputWS->setYUnit("Asymmetry");

  setProperty("OutputWorkspace", outputWS);
}

/** This method corrects the errors for one spectra.
 *	 The muon lifetime is in microseconds not seconds, i.e. 2.2 rather than 0.0000022.
 *   This is because the data is in microseconds.
 *   @param inX ::  The X vector
 *   @param inY ::  The input error vector
 *   @param outY :: The output error vector
 */
void MuonRemoveExpDecay::removeDecayError(const MantidVec& inX, const MantidVec& inY,
    MantidVec& outY)
{
  //Do the removal
  for (size_t i = 0; i < inY.size(); ++i)
  {
    if ( inY[i] )
      outY[i] = inY[i] * exp(inX[i] / (Mantid::PhysicalConstants::MuonLifetime * 1000000.0));
    else
      outY[i] = 1.0 * exp(inX[i] / (Mantid::PhysicalConstants::MuonLifetime * 1000000.0));
  }
}

/** This method corrects the data for one spectra.
 *	 The muon lifetime is in microseconds not seconds, i.e. 2.2 rather than 0.0000022.
 *   This is because the data is in microseconds.
 *   @param inX ::  The X vector
 *   @param inY ::  The input data vector
 *   @param outY :: The output data vector
 */
void MuonRemoveExpDecay::removeDecayData(const MantidVec& inX, const MantidVec& inY,
    MantidVec& outY)
{
  //Do the removal
  for (size_t i = 0; i < inY.size(); ++i)
  {
    if ( inY[i] )
      outY[i] = inY[i] * exp(inX[i] / (Mantid::PhysicalConstants::MuonLifetime * 1000000.0));
    else
      outY[i] = 0.1 * exp(inX[i] / (Mantid::PhysicalConstants::MuonLifetime * 1000000.0));
  }
}

/**
 * calculate normalisation constant after the exponential decay has been removed
 * to a linear fitting function
 * @param ws ::  workspace
 * @param wsIndex :: workspace index
 * @return normalisation constant
*/
double MuonRemoveExpDecay::calNormalisationConst(API::MatrixWorkspace_sptr ws, int wsIndex)
{
  double retVal = 1.0; 

    API::IAlgorithm_sptr fit;
    fit = createChildAlgorithm("Fit", -1, -1, true);

    std::stringstream ss;
    ss << "name=LinearBackground,A0=" << ws->readY(wsIndex)[0] << ",A1=" << 0.0 << ",ties=(A1=0.0)";
    std::string function = ss.str();

    fit->setPropertyValue("Function", function);
    fit->setProperty("InputWorkspace", ws);
    fit->setProperty("WorkspaceIndex", wsIndex);
    fit->setPropertyValue("Minimizer", "Levenberg-MarquardtMD");
    fit->setProperty("Ties", "A1=0.0");
    fit->execute();

    std::string fitStatus = fit->getProperty("OutputStatus");
    API::IFunction_sptr result = fit->getProperty("Function");
    std::vector<std::string> paramnames = result->getParameterNames();

    // Check order of names
    if (paramnames[0].compare("A0") != 0)
    {
      g_log.error() << "Parameter 0 should be A0, but is " << paramnames[0] << std::endl;
      throw std::invalid_argument("Parameters are out of order @ 0, should be A0");
    }
    if (paramnames[1].compare("A1") != 0)
    {
      g_log.error() << "Parameter 1 should be A1, but is " << paramnames[1]
            << std::endl;
      throw std::invalid_argument("Parameters are out of order @ 0, should be A1");
    }

    if (!fitStatus.compare("success"))
    {
      const double A0 = result->getParameter(0);

      if ( A0 < 0 ) 
      {
        g_log.warning() << "When trying to fit Asymmetry normalisation constant this constant comes out negative." 
          << "To proceed Asym norm constant set to 1.0\n";
      }
      else
      {
        retVal = A0;
      }
    } 
    else
    {
      g_log.warning() << "Fit falled. Status = " << fitStatus << std::endl
                        << "For workspace index " << wsIndex << std::endl
                     << "Asym norm constant set to 1.0\n";
    } 

  return retVal;
}


} // namespace Algorithm
} // namespace Mantid