/** * Calculate the twoTheta angle from the detector and sample locations. * @return: twoTheta */ double SpecularReflectionAlgorithm::calculateTwoTheta() const { MatrixWorkspace_sptr inWS = this->getProperty("InputWorkspace"); const std::string analysisMode = this->getProperty("AnalysisMode"); Instrument_const_sptr instrument = inWS->getInstrument(); IComponent_const_sptr detector = this->getDetectorComponent(inWS, analysisMode == pointDetectorAnalysis); IComponent_const_sptr sample = this->getSurfaceSampleComponent(instrument); const V3D detSample = detector->getPos() - sample->getPos(); boost::shared_ptr<const ReferenceFrame> refFrame = instrument->getReferenceFrame(); const double upoffset = refFrame->vecPointingUp().scalar_prod(detSample); const double beamoffset = refFrame->vecPointingAlongBeam().scalar_prod(detSample); const double twoTheta = std::atan2(upoffset, beamoffset) * 180 / M_PI; return twoTheta; }
/** Get instrument geometry setup including L2 for each detector and L1 */ void CreateLogTimeCorrection::getInstrumentSetup() { // 1. Get sample position and source position IComponent_const_sptr sample = m_instrument->getSample(); if (!sample) { throw runtime_error("No sample has been set."); } V3D samplepos = sample->getPos(); IComponent_const_sptr source = m_instrument->getSource(); if (!source) { throw runtime_error("No source has been set."); } V3D sourcepos = source->getPos(); m_L1 = sourcepos.distance(samplepos); // 2. Get detector IDs std::vector<detid_t> detids = m_instrument->getDetectorIDs(true); for (auto &detid : detids) { IDetector_const_sptr detector = m_instrument->getDetector(detid); V3D detpos = detector->getPos(); double l2 = detpos.distance(samplepos); m_l2map.emplace(detid, l2); } // 3. Output information g_log.information() << "Sample position = " << samplepos << "; " << "Source position = " << sourcepos << ", L1 = " << m_L1 << "; " << "Number of detector/pixels = " << detids.size() << ".\n"; }
/** * Correct the position of the detectors based on the input theta value. * @param toCorrect : Workspace to correct detector posisitions on. * @param twoThetaInDeg : 2* Theta in degrees to use in correction calculations. * @param sample : Pointer to the sample * @param detector : Pointer to a given detector */ void SpecularReflectionPositionCorrect::correctPosition( API::MatrixWorkspace_sptr toCorrect, const double &twoThetaInDeg, IComponent_const_sptr sample, IComponent_const_sptr detector) { auto instrument = toCorrect->getInstrument(); const V3D detectorPosition = detector->getPos(); const V3D samplePosition = sample->getPos(); const V3D sampleToDetector = detectorPosition - samplePosition; auto referenceFrame = instrument->getReferenceFrame(); const double twoThetaInRad = twoThetaInDeg * (M_PI / 180.0); double acrossOffset = 0; double beamOffset = sampleToDetector.scalar_prod( referenceFrame ->vecPointingAlongBeam()); // We just recalculate beam offset. double upOffset = (beamOffset * std::tan(0.5 * twoThetaInRad)); // We only correct vertical position // Apply the movements. moveDetectors(toCorrect, detector, sample, upOffset, acrossOffset, detector->getPos()); }
/** * Execute the MoveInstrumentComponent on all (named) subcomponents * @param toCorrect : Workspace to correct * @param detector : Detector or DetectorGroup * @param sample : Sample Component * @param upOffset : Up offset to apply * @param acrossOffset : Across offset to apply * @param detectorPosition: Actual detector or detector group position. */ void SpecularReflectionPositionCorrect::moveDetectors( API::MatrixWorkspace_sptr toCorrect, IComponent_const_sptr detector, IComponent_const_sptr sample, const double &upOffset, const double &acrossOffset, const V3D &detectorPosition) { auto instrument = toCorrect->getInstrument(); const V3D samplePosition = sample->getPos(); auto referenceFrame = instrument->getReferenceFrame(); if (auto groupDetector = boost::dynamic_pointer_cast<const DetectorGroup>( detector)) // Do we have a group of detectors { const std::vector<IDetector_const_sptr> detectors = groupDetector->getDetectors(); const bool commonParent = hasCommonParent(detectors); if (commonParent) { /* * Same parent component. So lets move that. */ moveDetectors(toCorrect, detectors[0], sample, upOffset, acrossOffset, detectorPosition); // Recursive call } else { /* * We have to move individual components. */ for (size_t i = 0; i < detectors.size(); ++i) { moveDetectors(toCorrect, detectors[i], sample, upOffset, acrossOffset, detectorPosition); // Recursive call } } } else { auto moveComponentAlg = this->createChildAlgorithm("MoveInstrumentComponent"); moveComponentAlg->initialize(); moveComponentAlg->setProperty("Workspace", toCorrect); IComponent_const_sptr root = getParentComponent(detector); const std::string componentName = root->getName(); moveComponentAlg->setProperty("ComponentName", componentName); moveComponentAlg->setProperty("RelativePosition", false); // Movements moveComponentAlg->setProperty( referenceFrame->pointingAlongBeamAxis(), detectorPosition.scalar_prod(referenceFrame->vecPointingAlongBeam())); moveComponentAlg->setProperty(referenceFrame->pointingHorizontalAxis(), acrossOffset); const double detectorVerticalPosition = detectorPosition.scalar_prod(referenceFrame->vecPointingUp()); const double rootVerticalPosition = root->getPos().scalar_prod(referenceFrame->vecPointingUp()); const double dm = rootVerticalPosition - detectorVerticalPosition; moveComponentAlg->setProperty( referenceFrame->pointingUpAxis(), samplePosition.scalar_prod(referenceFrame->vecPointingUp()) + upOffset + dm); // Execute the movement. moveComponentAlg->execute(); } }
/** Creates or modifies the parameter map for the specified detector adding * pressure and wall thickness information * @param params :: these will be written to the detector paraments 3He(atm)=pressure) and wallT(m)=wall thickness * @param change :: if the parameters are successfully changed they are stored here * @throw NotFoundError if a pointer to the specified detector couldn't be retrieved */ void LoadDetectorInfo::setDetectorParams(const detectorInfo ¶ms, detectorInfo &change) { Geometry::IDetector_sptr det; try { det = boost::const_pointer_cast<IDetector>(m_workspace->getInstrument()->baseInstrument()->getDetector(params.detID)); } catch( std::runtime_error &e) { throw Exception::NotFoundError(e.what(), params.detID); } Geometry::ParameterMap &pmap = m_workspace->instrumentParameters(); // Set the detectors pressure. pmap.addDouble(det->getComponentID(), "3He(atm)", params.pressure); // Set the wall thickness pmap.addDouble(det->getComponentID(), "wallT(m)", params.wallThick); // If we have a l2, theta and phi. Update the postion if required if( m_moveDets && params.l2 != DBL_MAX && params.theta != DBL_MAX && params.phi != DBL_MAX ) { V3D newPos; newPos.spherical(params.l2, params.theta, params.phi); // The sample position may not be at 0,0,0 newPos += m_samplePos; IComponent_const_sptr parent = det->getParent(); if (parent) { newPos -= parent->getPos(); Quat rot = parent->getRotation(); rot.inverse(); rot.rotate(newPos); } det->setPos(newPos); } // this operation has been successful if we are here, the following infomation is usefull for logging change = params; }
/** * Cache frequently accessed values * @param instrument : The instrument for this run * @param detID : The det ID for this observation */ void CachedExperimentInfo::initCaches( const Geometry::Instrument_const_sptr &instrument, const detid_t detID) { // Throws if detector does not exist // Takes into account possible detector mapping IDetector_const_sptr det = m_exptInfo.getDetectorByID(detID); // Instrument distances boost::shared_ptr<const ReferenceFrame> refFrame = instrument->getReferenceFrame(); m_beam = refFrame->pointingAlongBeam(); m_up = refFrame->pointingUp(); m_horiz = refFrame->pointingHorizontal(); IComponent_const_sptr source = instrument->getSource(); IComponent_const_sptr sample = instrument->getSample(); IComponent_const_sptr aperture = instrument->getComponentByName("aperture", 1); if (!aperture) { throw std::invalid_argument( "No component named \"aperture\" found in instrument."); } IObjComponent_const_sptr firstChopper = instrument->getChopperPoint(0); const Kernel::V3D samplePos = sample->getPos(); const Kernel::V3D beamDir = samplePos - source->getPos(); // Cache m_twoTheta = det->getTwoTheta(samplePos, beamDir); m_phi = det->getPhi(); m_modToChop = firstChopper->getDistance(*source); m_apertureToChop = firstChopper->getDistance(*aperture); m_chopToSample = sample->getDistance(*firstChopper); m_sampleToDet = det->getDistance(*sample); // Aperture Geometry::BoundingBox apertureBox; aperture->getBoundingBox(apertureBox); if (apertureBox.isNull()) { throw std::invalid_argument("CachedExperimentInfo::initCaches - Aperture " "has no bounding box, cannot sample from it"); } m_apertureSize.first = apertureBox.maxPoint()[0] - apertureBox.minPoint()[0]; m_apertureSize.second = apertureBox.maxPoint()[1] - apertureBox.minPoint()[1]; // Sample volume const API::Sample &sampleDescription = m_exptInfo.sample(); const Geometry::Object &shape = sampleDescription.getShape(); m_sampleWidths = shape.getBoundingBox().width(); // Detector volume // Make sure it encompasses all possible detectors det->getBoundingBox(m_detBox); if (m_detBox.isNull()) { throw std::invalid_argument("CachedExperimentInfo::initCaches - Detector " "has no bounding box, cannot sample from it. " "ID:" + boost::lexical_cast<std::string>(det->getID())); } const double rad2deg = 180. / M_PI; const double thetaInDegs = twoTheta() * rad2deg; const double phiInDegs = phi() * rad2deg; m_gonimeter = new Goniometer; m_gonimeter->makeUniversalGoniometer(); m_gonimeter->setRotationAngle("phi", thetaInDegs); m_gonimeter->setRotationAngle("chi", phiInDegs); m_sampleToDetMatrix = m_exptInfo.sample().getOrientedLattice().getU() * m_gonimeter->getR(); // EFixed m_efixed = m_exptInfo.getEFixed(det); }
void SofQWCentre::exec() { using namespace Geometry; MatrixWorkspace_const_sptr inputWorkspace = getProperty("InputWorkspace"); // Do the full check for common binning if (!WorkspaceHelpers::commonBoundaries(*inputWorkspace)) { g_log.error( "The input workspace must have common binning across all spectra"); throw std::invalid_argument( "The input workspace must have common binning across all spectra"); } std::vector<double> verticalAxis; MatrixWorkspace_sptr outputWorkspace = setUpOutputWorkspace( inputWorkspace, getProperty("QAxisBinning"), verticalAxis); setProperty("OutputWorkspace", outputWorkspace); // Holds the spectrum-detector mapping std::vector<specnum_t> specNumberMapping; std::vector<detid_t> detIDMapping; m_EmodeProperties.initCachedValues(*inputWorkspace, this); int emode = m_EmodeProperties.m_emode; // Get a pointer to the instrument contained in the workspace Instrument_const_sptr instrument = inputWorkspace->getInstrument(); // Get the distance between the source and the sample (assume in metres) IComponent_const_sptr source = instrument->getSource(); IComponent_const_sptr sample = instrument->getSample(); V3D beamDir = sample->getPos() - source->getPos(); beamDir.normalize(); try { double l1 = source->getDistance(*sample); g_log.debug() << "Source-sample distance: " << l1 << '\n'; } catch (Exception::NotFoundError &) { g_log.error("Unable to calculate source-sample distance"); throw Exception::InstrumentDefinitionError( "Unable to calculate source-sample distance", inputWorkspace->getTitle()); } // Conversion constant for E->k. k(A^-1) = sqrt(energyToK*E(meV)) const double energyToK = 8.0 * M_PI * M_PI * PhysicalConstants::NeutronMass * PhysicalConstants::meV * 1e-20 / (PhysicalConstants::h * PhysicalConstants::h); // Loop over input workspace bins, reassigning data to correct bin in output // qw workspace const size_t numHists = inputWorkspace->getNumberHistograms(); const size_t numBins = inputWorkspace->blocksize(); Progress prog(this, 0.0, 1.0, numHists); for (int64_t i = 0; i < int64_t(numHists); ++i) { try { // Now get the detector object for this histogram IDetector_const_sptr spectrumDet = inputWorkspace->getDetector(i); if (spectrumDet->isMonitor()) continue; const double efixed = m_EmodeProperties.getEFixed(*spectrumDet); // For inelastic scattering the simple relationship q=4*pi*sinTheta/lambda // does not hold. In order to // be completely general we must calculate the momentum transfer by // calculating the incident and final // wave vectors and then use |q| = sqrt[(ki - kf)*(ki - kf)] DetectorGroup_const_sptr detGroup = boost::dynamic_pointer_cast<const DetectorGroup>(spectrumDet); std::vector<IDetector_const_sptr> detectors; if (detGroup) { detectors = detGroup->getDetectors(); } else { detectors.push_back(spectrumDet); } const size_t numDets = detectors.size(); // cache to reduce number of static casts const double numDets_d = static_cast<double>(numDets); const auto &Y = inputWorkspace->y(i); const auto &E = inputWorkspace->e(i); const auto &X = inputWorkspace->x(i); // Loop over the detectors and for each bin calculate Q for (size_t idet = 0; idet < numDets; ++idet) { IDetector_const_sptr det = detectors[idet]; // Calculate kf vector direction and then Q for each energy bin V3D scatterDir = (det->getPos() - sample->getPos()); scatterDir.normalize(); for (size_t j = 0; j < numBins; ++j) { const double deltaE = 0.5 * (X[j] + X[j + 1]); // Compute ki and kf wave vectors and therefore q = ki - kf double ei(0.0), ef(0.0); if (emode == 1) { ei = efixed; ef = efixed - deltaE; if (ef < 0) { std::string mess = "Energy transfer requested in Direct mode exceeds incident " "energy.\n Found for det ID: " + std::to_string(idet) + " bin No " + std::to_string(j) + " with Ei=" + boost::lexical_cast<std::string>(efixed) + " and energy transfer: " + boost::lexical_cast<std::string>(deltaE); throw std::runtime_error(mess); } } else { ei = efixed + deltaE; ef = efixed; if (ef < 0) { std::string mess = "Incident energy of a neutron is negative. Are you trying to " "process Direct data in Indirect mode?\n Found for det ID: " + std::to_string(idet) + " bin No " + std::to_string(j) + " with efied=" + boost::lexical_cast<std::string>(efixed) + " and energy transfer: " + boost::lexical_cast<std::string>(deltaE); throw std::runtime_error(mess); } } if (ei < 0) throw std::runtime_error( "Negative incident energy. Check binning."); const V3D ki = beamDir * sqrt(energyToK * ei); const V3D kf = scatterDir * (sqrt(energyToK * (ef))); const double q = (ki - kf).norm(); // Test whether it's in range of the Q axis if (q < verticalAxis.front() || q > verticalAxis.back()) continue; // Find which q bin this point lies in const MantidVec::difference_type qIndex = std::upper_bound(verticalAxis.begin(), verticalAxis.end(), q) - verticalAxis.begin() - 1; // Add this spectra-detector pair to the mapping specNumberMapping.push_back( outputWorkspace->getSpectrum(qIndex).getSpectrumNo()); detIDMapping.push_back(det->getID()); // And add the data and it's error to that bin, taking into account // the number of detectors contributing to this bin outputWorkspace->mutableY(qIndex)[j] += Y[j] / numDets_d; // Standard error on the average outputWorkspace->mutableE(qIndex)[j] = sqrt((pow(outputWorkspace->e(qIndex)[j], 2) + pow(E[j], 2)) / numDets_d); } } } catch (Exception::NotFoundError &) { // Get to here if exception thrown when calculating distance to detector // Presumably, if we get to here the spectrum will be all zeroes anyway // (from conversion to E) continue; } prog.report(); } // If the input workspace was a distribution, need to divide by q bin width if (inputWorkspace->isDistribution()) this->makeDistribution(outputWorkspace, verticalAxis); // Set the output spectrum-detector mapping SpectrumDetectorMapping outputDetectorMap(specNumberMapping, detIDMapping); outputWorkspace->updateSpectraUsing(outputDetectorMap); // Replace any NaNs in outputWorkspace with zeroes if (this->getProperty("ReplaceNaNs")) { auto replaceNans = this->createChildAlgorithm("ReplaceSpecialValues"); replaceNans->setChild(true); replaceNans->initialize(); replaceNans->setProperty("InputWorkspace", outputWorkspace); replaceNans->setProperty("OutputWorkspace", outputWorkspace); replaceNans->setProperty("NaNValue", 0.0); replaceNans->setProperty("InfinityValue", 0.0); replaceNans->setProperty("BigNumberThreshold", DBL_MAX); replaceNans->execute(); } }