Example #1
0
void Linear::exec()
{
  // Get the input workspace
  MatrixWorkspace_const_sptr inputWorkspace = getProperty("InputWorkspace");
  // Get the spectrum to fit
  const int histNumber = getProperty("WorkspaceIndex");
  // Check validity
  if ( histNumber >= static_cast<int>(inputWorkspace->getNumberHistograms()) )
  {
    g_log.error() << "WorkspaceIndex set to an invalid value of " << histNumber << std::endl;
    throw Exception::IndexError(histNumber,inputWorkspace->getNumberHistograms(),"Linear WorkspaceIndex property");
  }

  // Get references to the data in the chosen spectrum
  const MantidVec& X = inputWorkspace->dataX(histNumber);
  const MantidVec& Y = inputWorkspace->dataY(histNumber);
  const MantidVec& E = inputWorkspace->dataE(histNumber);
  // Check if this spectrum has errors
  double errorsCount = 0.0;

  // Retrieve the Start/EndX properties, if set
  this->setRange(X,Y);
  
  const bool isHistogram = inputWorkspace->isHistogramData();
  // If the spectrum to be fitted has masked bins, we want to exclude them (even if only partially masked)
  const MatrixWorkspace::MaskList * const maskedBins = 
    ( inputWorkspace->hasMaskedBins(histNumber) ? &(inputWorkspace->maskedBins(histNumber)) : NULL );
  // Put indices of masked bins into a set for easy searching later
  std::set<size_t> maskedIndices;
  if (maskedBins)
  {
    MatrixWorkspace::MaskList::const_iterator it;
    for (it = maskedBins->begin(); it != maskedBins->end(); ++it)
      maskedIndices.insert(it->first);
  }
  
  progress(0);

  // Declare temporary vectors and reserve enough space if they're going to be used
  std::vector<double> XCen, unmaskedY, weights;
  int numPoints = m_maxX - m_minX;
  if (isHistogram) XCen.reserve(numPoints);
  if (maskedBins) unmaskedY.reserve(numPoints);
  weights.reserve(numPoints);

  for (int i = 0; i < numPoints; ++i)
  {
    // If the current bin is masked, skip it
    if ( maskedBins && maskedIndices.count(m_minX+i) ) continue;
    // Need to adjust X to centre of bin, if a histogram
    if (isHistogram) XCen.push_back( 0.5*(X[m_minX+i]+X[m_minX+i+1]) );
    // If there are masked bins present, we need to copy the unmasked Y values
    if (maskedBins) unmaskedY.push_back(Y[m_minX+i]);
    // GSL wants the errors as weights, i.e. 1/sigma^2
    // We need to be careful if E is zero because that would naively lead to an infinite weight on the point.
    // Solution taken here is to zero weight if error is zero, which typically means Y is zero
    //   (so it is effectively excluded from the fit).
    const double& currentE = E[m_minX+i];
    weights.push_back( currentE ? 1.0/(currentE*currentE) : 0.0 );
    // However, if the spectrum given has all errors of zero, then we should use the gsl function that
    //   doesn't take account of the errors.
    if ( currentE ) ++errorsCount;
  }
  progress(0.3);
  
  // If masked bins present, need to recalculate numPoints here
  if (maskedBins) numPoints = static_cast<int>(unmaskedY.size());
  // If no points left for any reason, bail out
  if (numPoints == 0)
  {
    g_log.error("No points in this range to fit");
    throw std::runtime_error("No points in this range to fit");
  }

  // Set up pointer variables to pass to gsl, pointing them to the right place
  const double * const xVals = ( isHistogram ? &XCen[0] : &X[m_minX] );
  const double * const yVals = ( maskedBins ? &unmaskedY[0] : &Y[m_minX] );

  // Call the gsl fitting function
  // The stride value of 1 reflects that fact that we want every element of our input vectors
  const int stride = 1;
  double *c0(new double),*c1(new double),*cov00(new double),*cov01(new double),*cov11(new double),*chisq(new double);
  int status;
  // Unless our spectrum has error values for vast majority of points, 
  //   call the gsl function that doesn't use errors
  if ( errorsCount/numPoints < 0.9 )
  {
    g_log.debug("Calling gsl_fit_linear (doesn't use errors in fit)");
    status = gsl_fit_linear(xVals,stride,yVals,stride,numPoints,c0,c1,cov00,cov01,cov11,chisq);
  }
  // Otherwise, call the one that does account for errors on the data points
  else
  {
    g_log.debug("Calling gsl_fit_wlinear (uses errors in fit)");
    status = gsl_fit_wlinear(xVals,stride,&weights[0],stride,yVals,stride,numPoints,c0,c1,cov00,cov01,cov11,chisq);
  }
  progress(0.8);

  // Check that the fit succeeded
  std::string fitStatus = gsl_strerror(status);
  // For some reason, a fit where c0,c1 & chisq are all infinity doesn't report as a 
  //   failure, so check explicitly.
  if ( !gsl_finite(*chisq) || !gsl_finite(*c0) || !gsl_finite(*c1) )
    fitStatus = "Fit gives infinities";
  if (fitStatus != "success") g_log.error() << "The fit failed: " << fitStatus << "\n";
  else
    g_log.information() << "The fit succeeded, giving y = " << *c0 << " + " << *c1 << "*x, with a Chi^2 of " << *chisq << "\n";
  
  // Set the fit result output properties
  setProperty("FitStatus",fitStatus);
  setProperty("FitIntercept",*c0);
  setProperty("FitSlope",*c1);
  setProperty("Cov00",*cov00);
  setProperty("Cov11",*cov11);
  setProperty("Cov01",*cov01);
  setProperty("Chi2",*chisq);
  
  // Create and fill a workspace2D with the same bins as the fitted spectrum and the value of the fit for the centre of each bin
  const size_t YSize = Y.size();
  MatrixWorkspace_sptr outputWorkspace = WorkspaceFactory::Instance().create(inputWorkspace,1,X.size(),YSize);
  
  // Copy over the X bins
  outputWorkspace->dataX(0).assign(X.begin(),X.end());
  // Now loop over the spectrum and use gsl function to calculate the Y & E values for the function
  for (size_t i = 0; i < YSize; ++i)
  {
    const double x = ( isHistogram ? 0.5*(X[i]+X[i+1]) : X[i] );
    const int err = gsl_fit_linear_est(x,*c0,*c1,*cov00,*cov01,*cov11,&(outputWorkspace->dataY(0)[i]),&(outputWorkspace->dataE(0)[i]));
    if (err) g_log.warning() << "Problem in filling the output workspace: " << gsl_strerror(err) << std::endl;
  }
  setProperty("OutputWorkspace",outputWorkspace);
  progress(1);
  // Clean up
  delete c0;
  delete c1;
  delete cov00;
  delete cov01;
  delete cov11;
  delete chisq;
}
Example #2
0
void Qxy::exec()
{
  MatrixWorkspace_const_sptr inputWorkspace = getProperty("InputWorkspace");
  MatrixWorkspace_const_sptr waveAdj = getProperty("WavelengthAdj");
  MatrixWorkspace_const_sptr pixelAdj = getProperty("PixelAdj");
  const bool doGravity = getProperty("AccountForGravity");
  const bool doSolidAngle = getProperty("SolidAngleWeighting");

  //throws if we don't have common binning or another incompatibility
  Qhelper helper;
  helper.examineInput(inputWorkspace, waveAdj, pixelAdj);
  g_log.debug() << "All input workspaces were found to be valid\n";

  // Create the output Qx-Qy grid
  MatrixWorkspace_sptr outputWorkspace = this->setUpOutputWorkspace(inputWorkspace);

  // Will also need an identically-sized workspace to hold the solid angle/time bin masked weight
  MatrixWorkspace_sptr weights = WorkspaceFactory::Instance().create(outputWorkspace);
  // Copy the X values from the output workspace to the solidAngles one
  cow_ptr<MantidVec> axis;
  axis.access() = outputWorkspace->readX(0);
  for ( size_t i = 0; i < weights->getNumberHistograms(); ++i ) weights->setX(i,axis);
  
  const size_t numSpec = inputWorkspace->getNumberHistograms();
  const size_t numBins = inputWorkspace->blocksize();
  
  // the samplePos is often not (0, 0, 0) because the instruments components are moved to account for the beam centre
  const V3D samplePos = inputWorkspace->getInstrument()->getSample()->getPos();
  
  // Set the progress bar (1 update for every one percent increase in progress)
  Progress prog(this, 0.05, 1.0, numSpec);

//  PARALLEL_FOR2(inputWorkspace,outputWorkspace)
  for (int64_t i = 0; i < int64_t(numSpec); ++i)
  {
//    PARALLEL_START_INTERUPT_REGION
    // Get the pixel relating to this spectrum
    IDetector_const_sptr det;
    try {
      det = inputWorkspace->getDetector(i);
    } catch (Exception::NotFoundError&) {
      g_log.warning() << "Spectrum index " << i << " has no detector assigned to it - discarding" << std::endl;
      // Catch if no detector. Next line tests whether this happened - test placed
      // outside here because Mac Intel compiler doesn't like 'continue' in a catch
      // in an openmp block.
    }
    // If no detector found or if it's masked or a monitor, skip onto the next spectrum
    if ( !det || det->isMonitor() || det->isMasked() ) continue;
    
    //get the bins that are included inside the RadiusCut/WaveCutcut off, those to calculate for
    const size_t wavStart = helper.waveLengthCutOff(inputWorkspace, getProperty("RadiusCut"), getProperty("WaveCut"), i);
    if (wavStart >=  inputWorkspace->readY(i).size())
    {
      // all the spectra in this detector are out of range
      continue;
    }


    V3D detPos = det->getPos()-samplePos;
      
    // these will be re-calculated if gravity is on but without gravity there is no need
    double phi = atan2(detPos.Y(),detPos.X());
    double a = cos(phi);
    double b = sin(phi);
    double sinTheta = sin( inputWorkspace->detectorTwoTheta(det)/2.0 );

    // Get references to the data for this spectrum
    const MantidVec& X = inputWorkspace->readX(i);
    const MantidVec& Y = inputWorkspace->readY(i);
    const MantidVec& E = inputWorkspace->readE(i);
 
    const MantidVec& axis = outputWorkspace->readX(0);

    // the solid angle of the detector as seen by the sample is used for normalisation later on
    double angle = det->solidAngle(samplePos);

    // some bins are masked completely or partially, the following vector will contain the fractions
    MantidVec maskFractions;
    if ( inputWorkspace->hasMaskedBins(i) )
    {
      // go through the set and convert it to a vector
      const MatrixWorkspace::MaskList& mask = inputWorkspace->maskedBins(i);
      maskFractions.resize(numBins, 1.0);
      MatrixWorkspace::MaskList::const_iterator it, itEnd(mask.end());
      for (it = mask.begin(); it != itEnd; ++it)
      {
        // The weight for this masked bin is 1 minus the degree to which this bin is masked
        maskFractions[it->first] -= it->second;
      }
    }
    double maskFraction(1);
    
    // this object is not used if gravity correction is off, but it is only constructed once per spectrum
    GravitySANSHelper grav;
    if (doGravity)
    {
      grav = GravitySANSHelper(inputWorkspace, det);
    }

    
    for (int j = static_cast<int>(numBins)-1; j >= static_cast<int>(wavStart); --j)
    {
      if( j < 0 ) break; // Be careful with counting down. Need a better fix but this will work for now
      const double binWidth = X[j+1]-X[j];
      // Calculate the wavelength at the mid-point of this bin
      const double wavLength = X[j]+(binWidth)/2.0;
      
      if (doGravity)
      {
        // SANS instruments must have their y-axis pointing up, show the detector position as where the neutron would be without gravity
        sinTheta = grav.calcComponents(wavLength, a, b);
      }

      // Calculate |Q| for this bin
      const double Q = 4.0*M_PI*sinTheta/wavLength;

      // Now get the x & y components of Q.
      const double Qx = a*Q;
      // Test whether they're in range, if not go to next spectrum.
      if ( Qx < axis.front() || Qx >= axis.back() ) break;
      const double Qy = b*Q;
      if ( Qy < axis.front() || Qy >= axis.back() ) break;
      // Find the indices pointing to the place in the 2D array where this bin's contents should go
      const MantidVec::difference_type xIndex = std::upper_bound(axis.begin(),axis.end(),Qx) - axis.begin() - 1;
      const int yIndex = static_cast<int>(
        std::upper_bound(axis.begin(),axis.end(),Qy) - axis.begin() - 1);
//      PARALLEL_CRITICAL(qxy)    /* Write to shared memory - must protect */
      {
        // the data will be copied to this bin in the output array
        double & outputBinY = outputWorkspace->dataY(yIndex)[xIndex];
        double & outputBinE = outputWorkspace->dataE(yIndex)[xIndex];

        if ( boost::math::isnan(outputBinY))
        {
          outputBinY = outputBinE = 0;
        }
        // Add the contents of the current bin to the 2D array.
        outputBinY += Y[j];
        // add the errors in quadranture
        outputBinE = std::sqrt( (outputBinE*outputBinE) + (E[j]*E[j]) );
        
        // account for masked bins
        if ( ! maskFractions.empty() )
        {
          maskFraction = maskFractions[j];
        }
        // add the total weight for this bin in the weights workspace, 
        // in an equivalent bin to where the data was stored

        // first take into account the product of contributions to the weight which have
        // no errors
        double weight = 0.0;
        if(doSolidAngle)
          weight = maskFraction*angle; 
        else
          weight = maskFraction;
        
        // then the product of contributions which have errors, i.e. optional 
        // pixelAdj and waveAdj contributions

        if (pixelAdj && waveAdj)
        {
          weights->dataY(yIndex)[xIndex] += weight*pixelAdj->readY(i)[0]*waveAdj->readY(0)[j];
          const double pixelYSq = pixelAdj->readY(i)[0]*pixelAdj->readY(i)[0];
          const double pixelESq = pixelAdj->readE(i)[0]*pixelAdj->readE(i)[0]; 
          const double waveYSq = waveAdj->readY(0)[j]*waveAdj->readY(0)[j];
          const double waveESq = waveAdj->readE(0)[j]*waveAdj->readE(0)[j];
          // add product of errors from pixelAdj and waveAdj (note no error on weight is assumed)   
          weights->dataE(yIndex)[xIndex] += weight*weight*(waveESq*pixelYSq + pixelESq*waveYSq);
        }
        else if (pixelAdj)
        {
          weights->dataY(yIndex)[xIndex] += weight*pixelAdj->readY(i)[0];
          const double pixelE = weight*pixelAdj->readE(i)[0]; 
          // add error from pixelAdj
          weights->dataE(yIndex)[xIndex] += pixelE*pixelE;
        }
        else if(waveAdj)
        {
          weights->dataY(yIndex)[xIndex] += weight*waveAdj->readY(0)[j];
          const double waveE = weight*waveAdj->readE(0)[j]; 
          // add error from waveAdj
          weights->dataE(yIndex)[xIndex] += waveE*waveE;
        }
        else
          weights->dataY(yIndex)[xIndex] += weight;
        

      }
    } // loop over single spectrum
    
    prog.report("Calculating Q");

//    PARALLEL_END_INTERUPT_REGION
  } // loop over all spectra
//  PARALLEL_CHECK_INTERUPT_REGION

  // take sqrt of error weight values
  // left to be executed here for computational efficiency
  size_t numHist = weights->getNumberHistograms();
  for (size_t i = 0; i < numHist; i++)
  {
    for (size_t j = 0; j < weights->dataE(i).size(); j++)
    {
      weights->dataE(i)[j] = sqrt(weights->dataE(i)[j]);
    } 
  }

  bool doOutputParts = getProperty("OutputParts");
  if (doOutputParts)
  {
    // copy outputworkspace before it gets further modified
    MatrixWorkspace_sptr ws_sumOfCounts = WorkspaceFactory::Instance().create(outputWorkspace);
    for (size_t i = 0; i < ws_sumOfCounts->getNumberHistograms(); i++)
    {
      ws_sumOfCounts->dataX(i) = outputWorkspace->dataX(i);
      ws_sumOfCounts->dataY(i) = outputWorkspace->dataY(i);
      ws_sumOfCounts->dataE(i) = outputWorkspace->dataE(i);
    }  

    helper.outputParts(this, ws_sumOfCounts, weights);
  }

  // Divide the output data by the solid angles
  outputWorkspace /= weights;
  outputWorkspace->isDistribution(true);

  
  // Count of the number of empty cells
  MatrixWorkspace::const_iterator wsIt(*outputWorkspace);
  int emptyBins = 0;
  for (;wsIt != wsIt.end(); ++wsIt)
  {
      if (wsIt->Y() < 1.0e-12) ++emptyBins;
  }
  // Log the number of empty bins
  g_log.notice() << "There are a total of " << emptyBins << " (" 
                 << (100*emptyBins)/(outputWorkspace->size()) << "%) empty Q bins.\n"; 
}