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; }
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"; }