/** Gets the distances between the source and detectors whose IDs you pass to it * @param WS :: the input workspace * @param mon0Spec :: Spectrum number of the output from the first monitor * @param mon1Spec :: Spectrum number of the output from the second monitor * @param monitor0Dist :: the calculated distance to the detector whose ID was * passed to this function first * @param monitor1Dist :: calculated distance to the detector whose ID was * passed to this function second * @throw NotFoundError if no detector is found for the detector ID given */ void GetEi::getGeometry(API::MatrixWorkspace_const_sptr WS, specid_t mon0Spec, specid_t mon1Spec, double &monitor0Dist, double &monitor1Dist) const { const IComponent_const_sptr source = WS->getInstrument()->getSource(); // retrieve a pointer to the first detector and get its distance size_t monWI = 0; try { monWI = WS->getIndexFromSpectrumNumber(mon0Spec); } catch (std::runtime_error &) { g_log.error() << "Could not find the workspace index for the monitor at spectrum " << mon0Spec << "\n"; g_log.error() << "Error retrieving data for the first monitor" << std::endl; throw std::bad_cast(); } const std::set<detid_t> &dets = WS->getSpectrum(monWI)->getDetectorIDs(); if (dets.size() != 1) { g_log.error() << "The detector for spectrum number " << mon0Spec << " was either not found or is a group, grouped monitors " "are not supported by this algorithm\n"; g_log.error() << "Error retrieving data for the first monitor" << std::endl; throw std::bad_cast(); } IDetector_const_sptr det = WS->getInstrument()->getDetector(*dets.begin()); monitor0Dist = det->getDistance(*(source.get())); // repeat for the second detector try { monWI = WS->getIndexFromSpectrumNumber(mon0Spec); } catch (std::runtime_error &) { g_log.error() << "Could not find the workspace index for the monitor at spectrum " << mon0Spec << "\n"; g_log.error() << "Error retrieving data for the second monitor\n"; throw std::bad_cast(); } const std::set<detid_t> &dets2 = WS->getSpectrum(monWI)->getDetectorIDs(); if (dets2.size() != 1) { g_log.error() << "The detector for spectrum number " << mon1Spec << " was either not found or is a group, grouped monitors " "are not supported by this algorithm\n"; g_log.error() << "Error retrieving data for the second monitor\n"; throw std::bad_cast(); } det = WS->getInstrument()->getDetector(*dets2.begin()); monitor1Dist = det->getDistance(*(source.get())); }
/** sets up the object with workspace data and calculates cached values ready to * calculate gravitional * effects across a spectrum * @param ws :: the workspace that contains the neutron counts * @param det :: the detector for which the calculations will be for */ GravitySANSHelper::GravitySANSHelper(API::MatrixWorkspace_const_sptr ws, Geometry::IDetector_const_sptr det) : m_beamLineNorm(-1), m_det(det), m_dropPerAngstrom2(-1), m_cachedDrop(0) { m_samplePos = ws->getInstrument()->getSample()->getPos(); const V3D sourcePos = ws->getInstrument()->getSource()->getPos(); m_beamLine = m_samplePos - sourcePos; m_beamLineNorm = 2.0 * (m_samplePos - sourcePos).norm(); // this is the LineOfSight assuming no drop, the drop is added (and // subtracted) later in the code when required m_cachedLineOfSight = m_det->getPos() - m_samplePos; // the drop is proportional to the wave length squared and using this to do // the full calculation only once increases the speed a lot m_dropPerAngstrom2 = ws->gravitationalDrop(m_det, 1e-10); }
// read the monitors list from the workspace and try to do it once for any // particular ws; bool MonIDPropChanger::monitorIdReader( API::MatrixWorkspace_const_sptr inputWS) const { // no workspace if (!inputWS) return false; // no instrument Geometry::Instrument_const_sptr pInstr = inputWS->getInstrument(); if (!pInstr) return false; std::vector<detid_t> mon = pInstr->getMonitors(); if (mon.empty()) { if (iExistingAllowedValues.empty()) { return false; } else { iExistingAllowedValues.clear(); return true; } } // are these monitors really there? // got the index of correspondent spectra. std::vector<size_t> indexList = inputWS->getIndicesFromDetectorIDs(mon); if (indexList.empty()) { if (iExistingAllowedValues.empty()) { return false; } else { iExistingAllowedValues.clear(); return true; } } // index list can be less or equal to the mon list size (some monitors do not // have spectra) size_t mon_count = (mon.size() < indexList.size()) ? mon.size() : indexList.size(); std::vector<int> allowed_values(mon_count); for (size_t i = 0; i < mon_count; i++) { allowed_values[i] = mon[i]; } // are known values the same as the values we have just identified? if (iExistingAllowedValues.size() != mon_count) { iExistingAllowedValues.clear(); iExistingAllowedValues.assign(allowed_values.begin(), allowed_values.end()); return true; } // the monitor list has the same size as before. Is it equivalent to the // existing one? bool values_redefined = false; for (size_t i = 0; i < mon_count; i++) { if (iExistingAllowedValues[i] != allowed_values[i]) { values_redefined = true; iExistingAllowedValues[i] = allowed_values[i]; } } return values_redefined; }
/** Checks that the two input workspace have common binning & size, the same instrument & unit. * Also calls the checkForOverlap method. * @param ws1 :: The first input workspace * @param ws2 :: The second input workspace * @throw std::invalid_argument If the workspaces are not compatible */ void ConjoinWorkspaces::validateInputs(API::MatrixWorkspace_const_sptr ws1, API::MatrixWorkspace_const_sptr ws2) const { // This is the full check for common binning if ( !WorkspaceHelpers::commonBoundaries(ws1) || !WorkspaceHelpers::commonBoundaries(ws2) ) { g_log.error("Both input workspaces must have common binning for all their spectra"); throw std::invalid_argument("Both input workspaces must have common binning for all their spectra"); } if ( ws1->getInstrument()->getName() != ws2->getInstrument()->getName() ) { const std::string message("The input workspaces are not compatible because they come from different instruments"); g_log.error(message); throw std::invalid_argument(message); } Unit_const_sptr ws1_unit = ws1->getAxis(0)->unit(); Unit_const_sptr ws2_unit = ws2->getAxis(0)->unit(); const std::string ws1_unitID = ( ws1_unit ? ws1_unit->unitID() : "" ); const std::string ws2_unitID = ( ws2_unit ? ws2_unit->unitID() : "" ); if ( ws1_unitID != ws2_unitID ) { const std::string message("The input workspaces are not compatible because they have different units on the X axis"); g_log.error(message); throw std::invalid_argument(message); } if ( ws1->isDistribution() != ws2->isDistribution() ) { const std::string message("The input workspaces have inconsistent distribution flags"); g_log.error(message); throw std::invalid_argument(message); } if ( !WorkspaceHelpers::matchingBins(ws1,ws2,true) ) { const std::string message("The input workspaces are not compatible because they have different binning"); g_log.error(message); throw std::invalid_argument(message); } this->checkForOverlap(ws1,ws2, true); }
double ConvertEmptyToTof::getL2(API::MatrixWorkspace_const_sptr workspace, int detId) { // Get a pointer to the instrument contained in the workspace Geometry::Instrument_const_sptr instrument = workspace->getInstrument(); // Get the distance between the source and the sample (assume in metres) Geometry::IComponent_const_sptr sample = instrument->getSample(); // Get the sample-detector distance for this detector (in metres) double l2 = workspace->getDetector(detId)->getPos().distance(sample->getPos()); return l2; }
/** Checks that the axes of the input workspaces match and creates the output * workspace if necessary * @param w1 :: The first input workspace * @param w2 :: The second input workspace * @param out :: Pointer to the output workspace */ void PointByPointVCorrection::check_validity( API::MatrixWorkspace_const_sptr &w1, API::MatrixWorkspace_const_sptr &w2, API::MatrixWorkspace_sptr &out) { // First check that the instrument matches for both input workspaces if (w1->getInstrument()->getName() != w2->getInstrument()->getName()) { g_log.error("The input workspaces have different instrument definitions"); throw std::runtime_error( "The input workspaces have different instrument definitions"); } // Check that the two workspaces are the same size if (w1->size() != w2->size()) { g_log.error("The input workspaces are not the same size"); throw std::runtime_error("The input workspaces are not the same size"); } // Now check that the bins match if (!WorkspaceHelpers::matchingBins(*w1, *w2)) { g_log.error("The input workspaces have different binning"); throw std::runtime_error("The input workspaces have different binning"); } const Mantid::API::Axis *const axis1 = w1->getAxis(1); const Mantid::API::Axis *const axis2 = w2->getAxis(1); if (!((*axis1) == (*axis2))) // Spectra axis are different, so division does // not make any sense { g_log.error( "The two workspaces InputW1 and InputW2 have different spectra list"); throw std::runtime_error( "The two workspaces InputW1 and InputW2 have different spectra list"); } if (out != w1 && out != w2) // Create a new workspace only if it is different // from of the input ones. { out = API::WorkspaceFactory::Instance().create(w1); setProperty("OutputWorkspace", out); } else if (out == w2) { g_log.warning("Any masking in the output workspaces will be taken from the " "vanadium workspace (InputW2)"); } }
/**Calculates the distance a neutron coming from the sample will have deviated * from a * straight tragetory before hitting a detector. If calling this function many * times * for the same detector you can call this function once, with waveLength=1, and * use * the fact drop is proportional to wave length squared .This function has no * knowledge * of which axis is vertical for a given instrument * @param ws :: workspace * @param det :: the detector that the neutron entered * @param waveLength :: the neutrons wave length in meters * @param extraLength :: additional length * @return the deviation in meters */ double GravitySANSHelper::gravitationalDrop(API::MatrixWorkspace_const_sptr ws, Geometry::IDetector_const_sptr det, const double waveLength, const double extraLength) const { using namespace PhysicalConstants; /// Pre-factor in gravity calculation: gm^2/2h^2 static const double gm2_OVER_2h2 = g * NeutronMass * NeutronMass / (2.0 * h * h); const V3D samplePos = ws->getInstrument()->getSample()->getPos(); const double pathLength = det->getPos().distance(samplePos) + extraLength; // Want L2 (sample-pixel distance) squared, times the prefactor g^2/h^2 const double L2 = gm2_OVER_2h2 * std::pow(pathLength, 2); return waveLength * waveLength * L2; }
/** Method for updating m_waveLength. * If size of m_waveLength is equal to number of data (for a new instance of *this * class this vector is empty initially) then don't recalculate it. * * @param xValues :: x values * @param nData :: length of xValues */ void IkedaCarpenterPV::calWavelengthAtEachDataPoint(const double *xValues, const size_t &nData) const { // if wavelength vector already have the right size no need for resizing it // further we make the assumption that no need to recalculate this vector if // it already has the right size if (m_waveLength.size() != nData) { m_waveLength.resize(nData); Mantid::Kernel::Unit_sptr wavelength = Mantid::Kernel::UnitFactory::Instance().create("Wavelength"); for (size_t i = 0; i < nData; i++) { m_waveLength[i] = xValues[i]; } // note if a version of convertValue was added which allows a double* as // first argument // then could avoid copying above plus only have to resize m_wavelength when // its size smaller than nData API::MatrixWorkspace_const_sptr mws = getMatrixWorkspace(); if (mws) { API::MatrixWorkspace_const_sptr mws = getMatrixWorkspace(); Instrument_const_sptr instrument = mws->getInstrument(); Geometry::IComponent_const_sptr sample = instrument->getSample(); if (sample != nullptr) { convertValue(m_waveLength, wavelength, mws, m_workspaceIndex); } else { g_log.warning() << "No sample set for instrument in workspace.\n" << "Can't calculate wavelength in IkedaCarpenter.\n" << "Default all wavelengths to one.\n" << "Solution is to load appropriate instrument into workspace.\n"; for (size_t i = 0; i < nData; i++) m_waveLength[i] = 1.0; } } else { g_log.warning() << "Workspace not set.\n" << "Can't calculate wavelength in IkedaCarpenter.\n" << "Default all wavelengths to one.\n" << "Solution call setMatrixWorkspace() for function.\n"; for (size_t i = 0; i < nData; i++) m_waveLength[i] = 1.0; } } }
/** * Init variables caches * @param :: Workspace pointer */ void SofQW2::initCachedValues(API::MatrixWorkspace_const_sptr workspace) { m_progress->report("Initializing caches"); // Retrieve the emode & efixed properties const std::string emode = getProperty("EMode"); // Convert back to an integer representation m_emode = 0; if (emode == "Direct") m_emode=1; else if (emode == "Indirect") m_emode = 2; m_efixed = getProperty("EFixed"); // Conversion constant for E->k. k(A^-1) = sqrt(energyToK*E(meV)) m_EtoK = 8.0*M_PI*M_PI*PhysicalConstants::NeutronMass*PhysicalConstants::meV*1e-20 / (PhysicalConstants::h*PhysicalConstants::h); // Get a pointer to the instrument contained in the workspace Geometry::Instrument_const_sptr instrument = workspace->getInstrument(); // Get the distance between the source and the sample (assume in metres) Geometry::IObjComponent_const_sptr source = instrument->getSource(); Geometry::IObjComponent_const_sptr sample = instrument->getSample(); m_samplePos = sample->getPos(); m_beamDir = m_samplePos - source->getPos(); m_beamDir.normalize(); // Is the instrument set up correctly double l1(0.0); try { l1 = source->getDistance(*sample); g_log.debug() << "Source-sample distance: " << l1 << std::endl; } catch (Exception::NotFoundError &) { throw Exception::InstrumentDefinitionError("Unable to calculate source-sample distance", workspace->getTitle()); } // Index Q cache initQCache(workspace); }
/** * If the InstrumentParameter property is set then it attempts to retrieve the parameter * from the component, else it returns the value of the Factor property * @param inputWS A pointer to the input workspace * @param index The current index to inspect * @return Value for the scale factor */ double ScaleX::getScaleFactor(const API::MatrixWorkspace_const_sptr & inputWS, const size_t index) { if(m_parname.empty()) return m_algFactor; // Try and get factor from component. If we see a DetectorGroup use this will use the first component Geometry::IDetector_const_sptr det; auto inst = inputWS->getInstrument(); auto *spec = inputWS->getSpectrum(index); const auto & ids = spec->getDetectorIDs(); const size_t ndets(ids.size()); if(ndets > 0) { try { det = inst->getDetector(*ids.begin()); } catch(Exception::NotFoundError&) { return 0.0; } } else return 0.0; const auto & pmap = inputWS->constInstrumentParameters(); auto par = pmap.getRecursive(det->getComponentID(), m_parname); if(par) { if(!m_combine) return par->value<double>(); else return m_binOp(m_algFactor,par->value<double>()); } else { std::ostringstream os; os << "Spectrum at index '" << index << "' has no parameter named '" << m_parname << "'\n"; throw std::runtime_error(os.str()); } }
/**Calculates the distance a neutron coming from the sample will have deviated * from a * straight tragetory before hitting a detector. If calling this function many * times * for the same detector you can call this function once, with waveLength=1, and * use * the fact drop is proportional to wave length squared .This function has no * knowledge * of which axis is vertical for a given instrument * @param ws :: workspace * @param det :: the detector that the neutron entered * @param waveLength :: the neutrons wave length in meters * @param extraLength :: additional length * @return the deviation in meters */ double GravitySANSHelper::gravitationalDrop(API::MatrixWorkspace_const_sptr ws, Geometry::IDetector_const_sptr det, const double waveLength, const double extraLength) const { using namespace PhysicalConstants; /// Pre-factor in gravity calculation: gm^2/2h^2 static const double gm2_OVER_2h2 = g * NeutronMass * NeutronMass / (2.0 * h * h); const V3D samplePos = ws->getInstrument()->getSample()->getPos(); // Perform a path length correction if an Lextra is specified. // The correction is Lcorr^2 = (L + Lextra)^2 -(LExtra)^2 const auto pathLengthWithExtraLength = det->getPos().distance(samplePos) + extraLength; const auto pathLengthSquared = std::pow(pathLengthWithExtraLength, 2) - std::pow(extraLength, 2); // Want L2 (sample-pixel distance) squared, times the prefactor g^2/h^2 const double L2 = gm2_OVER_2h2 * pathLengthSquared; return waveLength * waveLength * L2; }
double ConvertEmptyToTof::getL1(API::MatrixWorkspace_const_sptr workspace) { Geometry::Instrument_const_sptr instrument = workspace->getInstrument(); Geometry::IComponent_const_sptr sample = instrument->getSample(); double l1 = instrument->getSource()->getDistance(*sample); return l1; }
/** method does preliminary calculations of the detectors positions to convert results into k-dE space ; and places the results into static cash to be used in subsequent calls to this algorithm */ void PreprocessDetectorsToMD::processDetectorsPositions( const API::MatrixWorkspace_const_sptr &inputWS, DataObjects::TableWorkspace_sptr &targWS) { g_log.information() << "Preprocessing detector locations in a target reciprocal space\n"; // Geometry::Instrument_const_sptr instrument = inputWS->getInstrument(); // this->pBaseInstr = instrument->baseInstrument(); // Geometry::IComponent_const_sptr source = instrument->getSource(); Geometry::IComponent_const_sptr sample = instrument->getSample(); if ((!source) || (!sample)) { g_log.error() << " Instrument is not fully defined. Can not identify " "source or sample\n"; throw Kernel::Exception::InstrumentDefinitionError( "Instrument not sufficiently defined: failed to get source and/or " "sample"); } // L1 try { double L1 = source->getDistance(*sample); targWS->logs()->addProperty<double>("L1", L1, true); g_log.debug() << "Source-sample distance: " << L1 << '\n'; } catch (Kernel::Exception::NotFoundError &) { throw Kernel::Exception::InstrumentDefinitionError( "Unable to calculate source-sample distance for workspace", inputWS->getTitle()); } // Instrument name std::string InstrName = instrument->getName(); targWS->logs()->addProperty<std::string>( "InstrumentName", InstrName, true); // "The name which should unique identify current instrument"); targWS->logs()->addProperty<bool>("FakeDetectors", false, true); // get access to the workspace memory auto &sp2detMap = targWS->getColVector<size_t>("spec2detMap"); auto &detId = targWS->getColVector<int32_t>("DetectorID"); auto &detIDMap = targWS->getColVector<size_t>("detIDMap"); auto &L2 = targWS->getColVector<double>("L2"); auto &TwoTheta = targWS->getColVector<double>("TwoTheta"); auto &Azimuthal = targWS->getColVector<double>("Azimuthal"); auto &detDir = targWS->getColVector<Kernel::V3D>("DetDirections"); // Efixed; do we need one and does one exist? double Efi = targWS->getLogs()->getPropertyValueAsType<double>("Ei"); float *pEfixedArray(nullptr); const Geometry::ParameterMap &pmap = inputWS->constInstrumentParameters(); if (m_getEFixed) pEfixedArray = targWS->getColDataArray<float>("eFixed"); // check if one needs to generate masked detectors column. int *pMasksArray(nullptr); if (m_getIsMasked) pMasksArray = targWS->getColDataArray<int>("detMask"); //// progress message appearance size_t div = 100; size_t nHist = targWS->rowCount(); Mantid::API::Progress theProgress(this, 0, 1, nHist); //// Loop over the spectra uint32_t liveDetectorsCount(0); const auto &spectrumInfo = inputWS->spectrumInfo(); for (size_t i = 0; i < nHist; i++) { sp2detMap[i] = std::numeric_limits<uint64_t>::quiet_NaN(); detId[i] = std::numeric_limits<int32_t>::quiet_NaN(); detIDMap[i] = std::numeric_limits<uint64_t>::quiet_NaN(); L2[i] = std::numeric_limits<double>::quiet_NaN(); TwoTheta[i] = std::numeric_limits<double>::quiet_NaN(); Azimuthal[i] = std::numeric_limits<double>::quiet_NaN(); // detMask[i] = true; if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMonitor(i)) continue; // if masked detectors state is not used, masked detectors just ignored; bool maskDetector = spectrumInfo.isMasked(i); if (m_getIsMasked) *(pMasksArray + liveDetectorsCount) = maskDetector ? 1 : 0; else if (maskDetector) continue; const auto &spDet = spectrumInfo.detector(i); // calculate the requested values; sp2detMap[i] = liveDetectorsCount; detId[liveDetectorsCount] = int32_t(spDet.getID()); detIDMap[liveDetectorsCount] = i; L2[liveDetectorsCount] = spectrumInfo.l2(i); double polar = spectrumInfo.twoTheta(i); double azim = spDet.getPhi(); TwoTheta[liveDetectorsCount] = polar; Azimuthal[liveDetectorsCount] = azim; double sPhi = sin(polar); double ez = cos(polar); double ex = sPhi * cos(azim); double ey = sPhi * sin(azim); detDir[liveDetectorsCount].setX(ex); detDir[liveDetectorsCount].setY(ey); detDir[liveDetectorsCount].setZ(ez); // double sinTheta=sin(0.5*polar); // this->SinThetaSq[liveDetectorsCount] = sinTheta*sinTheta; // specific code which should work and makes sense // for indirect instrument but may be deployed on any code with Ei property // defined; if (pEfixedArray) { try { Geometry::Parameter_sptr par = pmap.getRecursive(&spDet, "eFixed"); if (par) Efi = par->value<double>(); } catch (std::runtime_error &) { } // set efixed for each existing detector *(pEfixedArray + liveDetectorsCount) = static_cast<float>(Efi); } liveDetectorsCount++; if (i % div == 0) theProgress.report(i, "Preprocessing detectors"); } targWS->logs()->addProperty<uint32_t>("ActualDetectorsNum", liveDetectorsCount, true); theProgress.report(); g_log.information() << "Finished preprocessing detector locations. Found: " << liveDetectorsCount << " detectors out of: " << nHist << " histograms\n"; }
/** * Apply the detector test criterion * @param counts1 :: A workspace containing the integrated counts of the first * white beam run * @param counts2 :: A workspace containing the integrated counts of the first * white beam run * @param average :: The computed median * @param variation :: The allowed variation in terms of number of medians, i.e * those spectra where * the ratio of the counts outside this range will fail the tests and will be * masked on counts1 * @return number of detectors for which tests failed */ int DetectorEfficiencyVariation::doDetectorTests( API::MatrixWorkspace_const_sptr counts1, API::MatrixWorkspace_const_sptr counts2, const double average, double variation) { // DIAG in libISIS did this. A variation of less than 1 doesn't make sense in // this algorithm if (variation < 1) { variation = 1.0 / variation; } // criterion for if the the first spectrum is larger than expected double largest = average * variation; // criterion for if the the first spectrum is lower than expected double lowest = average / variation; const int numSpec = static_cast<int>(counts1->getNumberHistograms()); const int progStep = static_cast<int>(std::ceil(numSpec / 30.0)); // Create a workspace for the output MaskWorkspace_sptr maskWS = this->generateEmptyMask(counts1); bool checkForMask = false; Geometry::Instrument_const_sptr instrument = counts1->getInstrument(); if (instrument != nullptr) { checkForMask = ((instrument->getSource() != nullptr) && (instrument->getSample() != nullptr)); } const double deadValue(1.0); int numFailed(0); PARALLEL_FOR3(counts1, counts2, maskWS) for (int i = 0; i < numSpec; ++i) { PARALLEL_START_INTERUPT_REGION // move progress bar if (i % progStep == 0) { advanceProgress(progStep * static_cast<double>(RTMarkDetects) / numSpec); progress(m_fracDone); interruption_point(); } if (checkForMask) { const std::set<detid_t> &detids = counts1->getSpectrum(i)->getDetectorIDs(); if (instrument->isMonitor(detids)) continue; if (instrument->isDetectorMasked(detids)) { // Ensure it is masked on the output maskWS->dataY(i)[0] = deadValue; continue; } } const double signal1 = counts1->readY(i)[0]; const double signal2 = counts2->readY(i)[0]; // Mask out NaN and infinite if (boost::math::isinf(signal1) || boost::math::isnan(signal1) || boost::math::isinf(signal2) || boost::math::isnan(signal2)) { maskWS->dataY(i)[0] = deadValue; PARALLEL_ATOMIC ++numFailed; continue; } // Check the ratio is within the given range const double ratio = signal1 / signal2; if (ratio < lowest || ratio > largest) { maskWS->dataY(i)[0] = deadValue; PARALLEL_ATOMIC ++numFailed; } PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION // Register the results with the ADS setProperty("OutputWorkspace", maskWS); return numFailed; }