Commit 03e3dad8 authored by Steve Williams's avatar Steve Williams
Browse files

New algorithm DetectorEfficiencyCor corrects numbers of counts in a workspace...

New algorithm DetectorEfficiencyCor corrects numbers of counts in a workspace for the efficiency of detectors, which is a function of neutron angle and energy as well as the detectors size and gas pressure. Only basic tests exist so far. Bugs in other algorithms were correct during development refs #841
parent 32e5de89
......@@ -238,6 +238,10 @@
RelativePath=".\src\CylinderAbsorption.cpp"
>
</File>
<File
RelativePath=".\src\DetectorEfficiencyCor.cpp"
>
</File>
<File
RelativePath=".\src\DetectorEfficiencyVariation.cpp"
>
......@@ -258,6 +262,10 @@
RelativePath=".\src\Divide.cpp"
>
</File>
<File
RelativePath=".\src\EfficiencyScriptInput.cpp"
>
</File>
<File
RelativePath=".\src\ExponentialCorrection.cpp"
>
......@@ -592,6 +600,14 @@
RelativePath=".\test\CylinderAbsorptionTest.h"
>
</File>
<File
RelativePath=".\inc\MantidAlgorithms\DetectorEfficiencyCor.h"
>
</File>
<File
RelativePath=".\test\DetectorEfficiencyCorTest.h"
>
</File>
<File
RelativePath=".\inc\MantidAlgorithms\DetectorEfficiencyVariation.h"
>
......@@ -624,6 +640,10 @@
RelativePath=".\test\DivideTest.h"
>
</File>
<File
RelativePath=".\inc\MantidAlgorithms\EfficiencyScriptInput.h"
>
</File>
<File
RelativePath=".\inc\MantidAlgorithms\ExponentialCorrection.h"
>
......
#ifndef MANTID_ALGORITHM_DETECTEFFICIENCYCOR_H_
#define MANTID_ALGORITHM_DETECTEFFICIENCYCOR_H_
#include "MantidAPI/Algorithm.h"
#include "MantidDataObjects/Workspace2D.h"
#include "MantidGeometry/Instrument/Component.h"
#include <Poco/NObserver.h>
#include <boost/shared_ptr.hpp>
#include <climits>
#include <string>
#include <vector>
namespace Mantid
{
namespace Algorithms
{
using DataObjects::Workspace2D_const_sptr;
using namespace API;
using namespace Geometry;
/**
Returns efficiency of cylindrical helium gas tube.
wvec Final neutron wavevector (Angsstrom^-1)
rad Outer radius of cylinder (m)
atms Pressure in number of atmospheres of 3He
t2rad Ratio of thickness of tube wall to the
radius
sintheta Sine of the angle between the cylinder
axis and the directin of travel of the
neutron i.e. sintheta=1.0d0 when
neutron hits the detector perpendicular
to the cylinder axis.
T.G.Perring June 1990:
Algorithm is based on a combinateion of Taylor series and
assymptotic expansion of the double integral for the
efficiency, linearly interpolating betweent the two in
region of common accuracy. Checked against numerical
integration to yield relative accuracy of 1 part in 10^12
or better over the entire domain of the input arguments
T.G.Perring August 2009:
Added generalisation to allow for arbitrary direction of
path of neutron with respect to the cylinder.
Origin of data for 3He cross-section
-------------------------------------
CKL data : (Argonne)
"At 2200 m/s xsect=5327 barns En=25.415 meV "
"At 10 atms, rho_atomic=2.688e-4, so sigma=1.4323 cm-1"
These data are not quite consistent, but the errors are small :
2200 m/s = 25.299 meV
5327 barns & 1.4323 cm-1 ==> 10atms ofideal gas at 272.9K
but at what temperature are the tubes "10 atms" ?
Shall use 1.4323 cm-1 @ 3.49416 A-1 with sigma prop. 1/v
This corresponds to a reference energy of 25.299meV, NOT 25.415.
This accounts for a difference of typically 1 pt in 1000 for
energies around a few hundred meV.
@author Steve Williams based on code by T.G.Perring
@date 6/10/2009
Copyright &copy; 2008-9 STFC Rutherford Appleton Laboratory
This file is part of Mantid.
Mantid is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Mantid is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>.
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
class DLLExport DetectorEfficiencyCor : public API::Algorithm
{
public:
DetectorEfficiencyCor();
/// Algorithm's name for identification overriding a virtual method
virtual const std::string name() const { return "DetectorEfficiencyCor"; }
/// Algorithm's version for identification overriding a virtual method
virtual const int version() const { return 1; }
/// Algorithm's category for identification overriding a virtual method
virtual const std::string category() const{return "CorrectionFunctions";}
private:
/// the user selected workspace
MatrixWorkspace_const_sptr m_inputWS;
/// output workspace, maybe the same as the input one
MatrixWorkspace_sptr m_outputWS;
/// the map that stores additional properties for detectors in that map
const Geometry::ParameterMap *m_paraMap;
/// stores the user selected value for incidient energy of the neutrons
double m_Ei;
// lots of cached data
/// a cached pointer to the shape used to save calculation time as most detectors have the same shape
const Object *m_shapeCache;
/// the cached value for the radius in the cached shape used to save calculation time
double m_radCache;
/// sin of angle between its axis and a line to the sample.
double m_sinThetaCache;
/// cached value for the axis of cylinder that the detectors are based on, last time it was calculated
V3D m_baseAxisCache;
/// cached value for the detector axis, after it had been rotated to be placed in the instrument (calculated value)
V3D m_detAxisCache;
/// cached value a parameter used in the detector efficiency calculation, 1-wallthickness/radius
double m_1_t2rad;
/// caches the part of the calculation that is constant for a whole detector
double m_CONST_rad_sintheta_1_t2rad_atms;
/// stores 1/wvec for all the bin boundries
std::vector<double> m_1_wvec;
/// points to the start of the workspace X-values, TOF bin boundaries, and is used to tell if the bin boundaries have changed
const std::vector<double> *m_XsCache;
///a flag int value to indicate that the value wasn't set by users
static const int UNSETINT = INT_MAX-15;
/// process this many spectra before checking for user cancel messages and updating the progress bar
static const int INTERVAL = 128;
void set1_wvec(int spectraIn);
double get1OverK(double DeltaE) const;
void getDetectorGeometry(boost::shared_ptr<IDetector> det);
void getCylinderAxis();
double DistToSurface(const V3D start, const Object *shape) const;
double EFF(const double oneOverwvec) const;//, double rad, double atms, double t2rad, double sintheta);
double EFFCHB(double a, double b, const double exspansionCoefs[], double x) const;
void onErrorsmaskDetectorsAndLog(std::vector<int> &spuriousSpectra, std::vector<int> &unsetParams) const;
// Implement abstract Algorithm methods
void init();
void exec();
void retrieveProperties();
void detectorEfficiency(int spectraNumber);
/// Links the energy to the wave number, I got this from Prof T.G.Perring
static const double PLANCKY_CONST;
/// coefficients for Taylor series/assymptotic expansion used at large wavenumbers and large angle
static const double c_eff_f[];
/// coefficients for Taylor series/assymptotic expansion used at low wavenumbers and low angle
static const double c_eff_g[];
/// the number of coefficients in each of the c_eff_f and c_eff_g arrays
static const short NUMCOEFS;
/// constants to use for the ISIS 3He detectors 2.0*sigref*wref/atmref
static const double CONSTA;
};
} // namespace Algorithms
} // namespace Mantid
#endif /*MANTID_ALGORITHM_DETECTEFFICIENCYCOR_H_*/
#ifndef MANTID_ALGORITHM_EFFICIENCYSCRIPTINPUT_H_
#define MANTID_ALGORITHM_EFFICIENCYSCRIPTINPUT_H_
#include "MantidAPI/Algorithm.h"
#include "MantidDataObjects/Workspace2D.h"
namespace Mantid
{
namespace Algorithms
{
using DataObjects::Workspace2D_const_sptr;
using namespace API;
using namespace Geometry;
/**????
@author Steve Williams based on code by T.G.Perring
@date 6/10/2009
Copyright &copy; 2008-9 STFC Rutherford Appleton Laboratory
This file is part of Mantid.
Mantid is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Mantid is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://svn.mantidproject.org/mantid/trunk/Code/Mantid>.
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
class DLLExport EfficiencyScriptInput : public API::Algorithm
{
public:
EfficiencyScriptInput(){}
/// Algorithm's name for identification overriding a virtual method
virtual const std::string name() const { return "EfficiencyScriptInput"; }
/// Algorithm's version for identification overriding a virtual method
virtual const int version() const { return 1; }
/// Algorithm's category for identification overriding a virtual method
virtual const std::string category() const{return "CorrectionFunctions";}
private:
// Implement abstract Algorithm methods
void init();
void exec();
};
} // namespace Algorithms
} // namespace Mantid
#endif /*MANTID_ALGORITHM_EFFICIENCYSCRIPTINPUT_H_*/
......@@ -84,8 +84,8 @@ private:
double timeToFly(double s, double E_KE) const;
double getPeakCentre(Workspace2D_const_sptr WS, const int monitIn, const double peakTime);
void extractSpec(int specInd, double start, double end);
void getPeakEstimates(double &height, double &centre) const;
int findHalfLoc(MantidVec::size_type startInd, double halfHeight, direction go) const;
void getPeakEstimates(double &height, int &centreInd, double &background) const;
double findHalfLoc(MantidVec::size_type startInd, double height, const double noise, const direction go) const;
double neutron_E_At(double speed) const;
void advanceProgress(double toAdd);
......@@ -93,8 +93,11 @@ private:
static const double HALF_WINDOW;
/// ignore an peaks that are less than this factor of the background
static const double PEAK_THRESH_H;
/// ignore an peaks with where the distance to the half heigth is less than this number of bins in either direction e.g. the FWHM is less than twice this number
/// ignore peaks where the half width times the ratio of the peak height to the background is less this
static const double PEAK_THRESH_A;
/// for peaks where the distance to the half heigth is less than this number of bins in either direction e.g. the FWHM is less than twice this number
static const int PEAK_THRESH_W;
// for estimating algorithm progress
static const double CROP; ///< fraction of algorithm time taken up with running CropWorkspace
static const double GET_COUNT_RATE; ///< fraction of algorithm taken by a single call to ConvertToDistribution
......
#ifndef MANTID_ALGORITHM_INPUTWSDETECTORINFO_H_
#define MANTID_ALGORITHM_INPUTWSDETECTORINFO_H_
#include "MantidAPI/SpectraDetectorMap.h"
#include "MantidAPI/MatrixWorkspace.h"
......@@ -62,3 +65,5 @@ namespace Mantid
};
}
}
#endif /*MANTID_ALGORITHM_INPUTWSDETECTORINFO_H*/
This diff is collapsed.
#include "MantidAlgorithms/EfficiencyScriptInput.h"
#include "MantidAPI/WorkspaceValidators.h"
#include "MantidKernel/ArrayProperty.h"
#include "MantidKernel/FileProperty.h"
#include "MantidKernel/RebinParamsValidator.h"
namespace Mantid
{
namespace Algorithms
{
// Register the class into the algorithm factory
DECLARE_ALGORITHM(EfficiencyScriptInput)
using namespace Kernel;
using namespace API;
using namespace Geometry;
void EfficiencyScriptInput::init()
{
std::vector<std::string> raw;
raw.push_back("raw");
declareProperty(new FileProperty("RawFile","", FileProperty::Load, raw), "");
declareProperty(new ArrayProperty<double>("BinBoundaries", new RebinParamsValidator));
std::vector<std::string> map;
raw.push_back("map");
declareProperty(new FileProperty("MapFile","", FileProperty::Load, map), "");
declareProperty(new FileProperty("OutFile","", FileProperty::Save), "");
}
void EfficiencyScriptInput::exec()
{
}
} // namespace Algorithm
} // namespace Mantid
......@@ -21,15 +21,17 @@ using namespace API;
using namespace Geometry;
using namespace DataObjects;
// adjustable fit criteria, increase the first number or reduce any of the last three for more promiscuous peak fitting
// from the estimated location of the peak search forward by the following fraction and backward by the same fraction
const double GetEi::HALF_WINDOW = 4.0/100;
const double GetEi::PEAK_THRESH_H = 3.0;
const double GetEi::PEAK_THRESH_A = 10.0;
const int GetEi::PEAK_THRESH_W = 3;
// progress estimates
const double GetEi::CROP = 0.15;
const double GetEi::GET_COUNT_RATE = 0.15;
const double GetEi::FIT_PEAK = 2.0;
const double GetEi::FIT_PEAK = 0.2;
/// Empty default constructor algorith() calls the constructor in the base class
GetEi::GetEi() : Algorithm(),
......@@ -196,7 +198,7 @@ double GetEi::timeToFly(double s, double E_KE) const
* @param WS the workspace containing the monitor spectrum
* @param monitIn the index of the histogram that contains the monitor spectrum
* @param peakTime the estimated TOF of the monitor peak in the time units of the workspace
* @return a time half way between the two half height locations on the peak
* @return a time of flight value in the peak in microseconds
* @throw invalid_argument if a good peak fit wasn't made or the input workspace does not have common binning
* @throw out_of_range if the peak runs off the edge of the histogram
* @throw runtime_error a sub-algorithm just falls over
......@@ -213,35 +215,19 @@ double GetEi::getPeakCentre(Workspace2D_const_sptr WS, const int monitIn, const
advanceProgress(GET_COUNT_RATE);
// to store fit results
double height, centreGaussian;
getPeakEstimates(height, centreGaussian);
int centreGausInd;
double height, backGroundlev;
getPeakEstimates(height, centreGausInd, backGroundlev);
// look out for user cancel messgages
advanceProgress(FIT_PEAK);
// find the index of the centre point. The centre can't be at index zero as this is at the edge of the spectrum, so centreIndex = 0 is the error value
MantidVec::size_type cenGausIn = 0;
for ( MantidVec::size_type i = 0; i < m_tempWS->readY(0).size(); ++i )
{// assumes that the bin boundaries are all in order of increasing time
if ( m_tempWS->readX(0)[i] > centreGaussian )
{
cenGausIn = i;
break;
}
}
// the peak centre is defined as the centre of the two half maximum points as this is better for asymmetric peaks
// first loop backwards along the histogram to get the first half height point
MantidVec::size_type lHalf = findHalfLoc(cenGausIn, height, GO_LEFT);
const double lHalf = findHalfLoc(centreGausInd, height, backGroundlev, GO_LEFT);
// go forewards to get the half height on the otherside of the peak
MantidVec::size_type rHalf = findHalfLoc(cenGausIn, height, GO_RIGHT);
// the centre is the mean of the two end values
double centreInd = static_cast<double>(lHalf + rHalf)/2.0;
// convert the index into a time of flight value
double tCalcu = m_tempWS->readX(0)[static_cast<int>(floor(centreInd))];
// centreInd could be an integer or a half integer so return the value of the bin boundry or the mean of two bin boundaries
tCalcu += m_tempWS->readX(0)[static_cast<int>(ceil(centreInd))];
return tCalcu/2;
const double rHalf = findHalfLoc(centreGausInd, height, backGroundlev, GO_RIGHT);
// the peak centre is defined as the mean of the two half height times
return (lHalf + rHalf)/2;
}
/** Calls CropWorkspace as a sub-algorithm and passes to it the InputWorkspace property
* @param specInd the index number of the histogram to extract
......@@ -283,69 +269,94 @@ void GetEi::extractSpec(int specInd, double start, double end)
//DEBUGGING CODE uncomment out the line below if you want to see the TOF window that was analysed
//AnalysisDataService::Instance().addOrReplace("croped_dist_del", m_tempWS);
progress(m_fracCompl);
interruption_point();
}
/** Finds the largest peak by looping through the histogram and finding the maximum
* value
* @param height this will became the peak height found by the fit
* @param centre will be set to the location of the peak center
* @throw invalid_argument if the peak is not much above the background
* @param height its passed value ignored it is set to the peak height
* @param centreInd passed value is ignored it will be set to the bin index of the peak center
* @param background passed value ignored set mean number of counts per bin in the spectrum
* @throw invalid_argument if the peak is not clearly above the background
*/
void GetEi::getPeakEstimates(double &height, double &centre) const
void GetEi::getPeakEstimates(double &height, int &centreInd, double &background) const
{
// take note of the number of background counts as error checking, do we have a peak or just a bump in the background
double backgroundCounts = 0;
background = 0;
// start at the first Y value
height = m_tempWS->readY(0)[0];
centre = m_tempWS->readX(0)[0];
centreInd = 0;
// then loop through all the Y values and find the tallest peak
for ( MantidVec::size_type i = 1; i < m_tempWS->readY(0).size(); ++i )
for ( MantidVec::size_type i = 1; i < m_tempWS->readY(0).size()-1; ++i )
{
backgroundCounts += m_tempWS->readY(0)[i];
background += m_tempWS->readY(0)[i];
if ( m_tempWS->readY(0)[i] > height )
{
height = m_tempWS->readY(0)[i];
centre = m_tempWS->readX(0)[i];
centreInd = i;
height = m_tempWS->readY(0)[centreInd];
}
}
if ( height < PEAK_THRESH_H*backgroundCounts/m_tempWS->readY(0).size() )
background = background/m_tempWS->readY(0).size();
if ( height < PEAK_THRESH_H*background )
{
throw std::invalid_argument("No peak was found or its height is less than the threshold of " + boost::lexical_cast<std::string>(PEAK_THRESH_H) + " times the mean background");
}
g_log.debug() << "Initial guess of peak position, based on the maximum Y value in the monitor spectrum, is at TOF " << centre << " (peak height " << height << " counts/microsecond)" << std::endl;
g_log.debug() << "Initial guess of peak position, based on the maximum Y value in the monitor spectrum, is at TOF " << (m_tempWS->readX(0)[centreInd]+m_tempWS->readX(0)[centreInd+1])/2 << " (peak height " << height << " counts/microsecond)" << std::endl;
}
/** Gets the index of the bin that is closest to the bin given and contains a number of
* counts less half of the number passed to this function, bin indexes start at zero
/** Estimates the closest time, looking either or back, when the number of counts is
* half that in the bin whose index that passed
* @param startInd index of the bin to search around, e.g. the index of the peak centre
* @param height the number of counts (or count rate) to compare against e.g. a peak height
* @param noise mean number of counts in each bin in the workspace
* @param go either GetEi::GO_LEFT or GetEi::GO_RIGHT
* @return the index number of the first bin found where the counts are less than half
* @return estimated TOF of the half maximum point
* @throw out_of_range if the end of the histogram is reached before the point is found
* @throw invalid_argument if the peak is too thin
*/
int GetEi::findHalfLoc(MantidVec::size_type startInd, double height, direction go) const
double GetEi::findHalfLoc(MantidVec::size_type startInd, double height, const double noise, const direction go) const
{
MantidVec::size_type endInd = startInd;
while ( m_tempWS->readY(0)[endInd] > height/2.0 )
{
endInd += go;
if ( endInd < 0 )
if ( endInd < 1 )
{
throw std::out_of_range("Can't analyse peak, some of the peak is outside the " + boost::lexical_cast<std::string>(HALF_WINDOW*100) + "% window, at TOF values that are too low");
}
if ( endInd >= m_tempWS->readY(0).size())
if ( endInd > m_tempWS->readY(0).size()-2)
{
throw std::out_of_range("Can't analyse peak, some of the peak is outside the " + boost::lexical_cast<std::string>(HALF_WINDOW*100) + "% window, at TOF values that are too high");
}
}
if ( std::abs(static_cast<int>(endInd - startInd)) < PEAK_THRESH_W )
{// we didn't find a insignificant peak
throw std::invalid_argument("No peak was found or its width is less than the threshold 2x" + boost::lexical_cast<std::string>(PEAK_THRESH_W) + " bins");
g_log.warning() << "Potential precision problem, one half height distance is less than the threshold number of bins: " << std::abs(static_cast<int>(endInd - startInd)) << "<" << PEAK_THRESH_W << std::endl;
}
g_log.debug() << "One half height point found at TOF = " << m_tempWS->readX(0)[endInd] << " microseconds" << std::endl;
return static_cast<int>(endInd);
// we have a peak in range, do an area check to see if the peak has any significance
height = (height-noise)/noise;
if ( height < PEAK_THRESH_A && std::abs(height*(endInd - startInd)) < PEAK_THRESH_A )
{// the peak could just be noise on the background, ignore it
throw std::invalid_argument("No good peak was found. The ratio of the height to the background multiplied either half widths must be above the threshold (>" + boost::lexical_cast<std::string>(PEAK_THRESH_A) + " bins)");
}
// get the TOF value in the middle of the bin
double halfTime = (m_tempWS->readX(0)[endInd]+m_tempWS->readX(0)[endInd+go])/2;
// interpolate between the first bin with less than half the counts to the bin before it
if ( endInd != startInd )
{// let the bin that we found have coordinates (x_1, y_1) the distance of the half point (x_2, y_2) from this is (y_1-y_2)/gradient. Gradient = (y_3-y_1)/(x_3-x_1) where (x_3, y_3) are the coordinates of the other bin we are using
halfTime -= ( (height/2.0)-m_tempWS->readY(0)[endInd] ) *
( m_tempWS->readX(0)[endInd] - m_tempWS->readX(0)[endInd-go] ) /
( m_tempWS->readY(0)[endInd] - m_tempWS->readY(0)[endInd-go] );
}
g_log.debug() << "One half height point found at TOF = " << halfTime << " microseconds" << std::endl;
return halfTime;
}
/** Get the kinetic energy of a neuton in joules given it speed using E=mv^2/2
* @param speed the instantanious speed of a neutron in metres per second
* @return the energy in joules
......
#include "MantidAlgorithms/InputWSDetectorInfo.h"
#include "MantidKernel/Exception.h"
#include <vector>
#include <string>
#include <sstream>
namespace Mantid
{
namespace Algorithms
{
using namespace API;
/** Copies pointers to the Instrument and ParameterMap to data members
* @param input A pointer to a workspace that contains instrument information
* @throw invalid_argument if there is no instrument information in the workspace
*/
InputWSDetectorInfo::InputWSDetectorInfo(MatrixWorkspace_const_sptr input) :
m_Input(input), m_Pmap(NULL)
{
// first something that points to the detectors
m_WInstru = input->getBaseInstrument();
m_RInstru = input->getInstrument();
if ( !m_RInstru || !m_WInstru )
{
throw std::invalid_argument("There is no instrument data in the input workspace.");
}
// the space that contains which are masked
m_Pmap = &m_Input->instrumentParameters();
}
/** To find out if there is a detector in a spectrum that is masked
* @param SpecIndex The number of spectrum, starting at zero is passed to axis::spectraNo(.)
* @return True if there is a masked detector, otherwise false
......@@ -25,6 +47,8 @@ bool InputWSDetectorInfo::aDetecIsMaskedinSpec(int SpecIndex) const
/** Masks all the detectors that contribute to the specified spectrum
* @param SpecIndex The number of spectrum, starting at zero is passed to axis::spectraNo(.)
* @return True if there is a masked detector, otherwise false
* @throw IndexError if the spectra index number isn't in the workspace
* @throw NotFoundError if we can't get a pointer to the detector that the detector map says is linked to the spectrum
*/
void InputWSDetectorInfo::maskAllDetectorsInSpec(int SpecIndex)
{
......@@ -52,7 +76,9 @@ void InputWSDetectorInfo::maskAllDetectorsInSpec(int SpecIndex)
std::ostringstream missingReport;
missingReport << "Information missing in the workspace for " << missing
<< " detector" << (missing > 1 ? "s" : "");
throw std::runtime_error(missingReport.str());
throw Kernel::Exception::NotFoundError(
std::string("InputWSDetectorInfo::maskAllDetectorsInSpec() ") + missingReport.str(),
SpecIndex);
}
}