/** * Populate the map with the Q values associated with each spectrum in the * workspace */ void SaveAscii2::populateQMetaData() { std::vector<std::string> qValues; const auto nHist = m_ws->getNumberHistograms(); for (size_t i = 0; i < nHist; i++) { const auto specNo = m_ws->getSpectrum(i).getSpectrumNo(); const auto workspaceIndex = m_specToIndexMap[specNo]; const auto detector = m_ws->getDetector(workspaceIndex); double twoTheta(0.0), efixed(0.0); if (!detector->isMonitor()) { twoTheta = 0.5 * m_ws->detectorTwoTheta(*detector); try { efixed = m_ws->getEFixed(detector); } catch (std::runtime_error) { throw; } } else { twoTheta = 0.0; efixed = DBL_MIN; } // Convert to MomentumTransfer auto qValue = Kernel::UnitConversion::run(twoTheta, efixed); auto qValueStr = boost::lexical_cast<std::string>(qValue); qValues.push_back(qValueStr); } m_metaDataMap["q"] = qValues; }
/// Returns signed 2 theta (signed scattering angle w.r.t. to beam direction). double DetectorInfo::signedTwoTheta(const std::pair<size_t, size_t> &index) const { if (isMonitor(index)) throw std::logic_error( "Two theta (scattering angle) is not defined for monitors."); const auto samplePos = samplePosition(); const auto beamLine = samplePos - sourcePosition(); if (beamLine.nullVector()) { throw Kernel::Exception::InstrumentDefinitionError( "Source and sample are at same position!"); } // Get the axis defining the sign const auto &instrumentUpAxis = m_instrument->getReferenceFrame()->vecThetaSign(); const auto sampleDetVec = position(index) - samplePos; double angle = sampleDetVec.angle(beamLine); const auto cross = beamLine.cross_prod(sampleDetVec); const auto normToSurface = beamLine.cross_prod(instrumentUpAxis); if (normToSurface.scalar_prod(cross) < 0) { angle *= -1; } return angle; }
/** Returns the signed scattering angle 2 theta (angle w.r.t. to beam * direction). * * Throws an exception if the spectrum is a monitor. */ double SpectrumInfo::signedTwoTheta(const size_t index) const { if (isMonitor(index)) throw std::logic_error( "Two theta (scattering angle) is not defined for monitors."); double signedTwoTheta{0.0}; const auto &dets = getDetectorVector(index); for (const auto &det : dets) { const auto &detIndex = m_detectorInfo.indexOf(det->getID()); m_detectorInfo.setCachedDetector(detIndex, det); signedTwoTheta += m_detectorInfo.signedTwoTheta(detIndex); } return signedTwoTheta / static_cast<double>(dets.size()); }
/** * Sum counts from the input workspace in lambda along lines of constant Q by * projecting to "virtual lambda" at a reference angle. * * @param detectorWS [in] :: the input workspace in wavelength * @param indices [in] :: an index set defining the foreground histograms * @return :: the single histogram output workspace in wavelength */ API::MatrixWorkspace_sptr ReflectometrySumInQ::sumInQ(const API::MatrixWorkspace &detectorWS, const Indexing::SpectrumIndexSet &indices) { const auto spectrumInfo = detectorWS.spectrumInfo(); const auto refAngles = referenceAngles(spectrumInfo); // Construct the output workspace in virtual lambda API::MatrixWorkspace_sptr IvsLam = constructIvsLamWS(detectorWS, indices, refAngles); auto &outputE = IvsLam->dataE(0); // Loop through each spectrum in the detector group for (auto spIdx : indices) { if (spectrumInfo.isMasked(spIdx) || spectrumInfo.isMonitor(spIdx)) { continue; } // Get the size of this detector in twoTheta const auto twoThetaRange = twoThetaWidth(spIdx, spectrumInfo); // Check X length is Y length + 1 const auto inputBinEdges = detectorWS.binEdges(spIdx); const auto inputCounts = detectorWS.counts(spIdx); const auto inputStdDevs = detectorWS.countStandardDeviations(spIdx); // Create a vector for the projected errors for this spectrum. // (Output Y values can simply be accumulated directly into the output // workspace, but for error values we need to create a separate error // vector for the projected errors from each input spectrum and then // do an overall sum in quadrature.) std::vector<double> projectedE(outputE.size(), 0.0); // Process each value in the spectrum const int ySize = static_cast<int>(inputCounts.size()); for (int inputIdx = 0; inputIdx < ySize; ++inputIdx) { // Do the summation in Q processValue(inputIdx, twoThetaRange, refAngles, inputBinEdges, inputCounts, inputStdDevs, *IvsLam, projectedE); } // Sum errors in quadrature const int eSize = static_cast<int>(outputE.size()); for (int outIdx = 0; outIdx < eSize; ++outIdx) { outputE[outIdx] += projectedE[outIdx] * projectedE[outIdx]; } } // Take the square root of all the accumulated squared errors for this // detector group. Assumes Gaussian errors double (*rs)(double) = std::sqrt; std::transform(outputE.begin(), outputE.end(), outputE.begin(), rs); return IvsLam; }
/// Returns 2 theta (scattering angle w.r.t. to beam direction). double DetectorInfo::twoTheta(const std::pair<size_t, size_t> &index) const { if (isMonitor(index)) throw std::logic_error( "Two theta (scattering angle) is not defined for monitors."); const auto samplePos = samplePosition(); const auto beamLine = samplePos - sourcePosition(); if (beamLine.nullVector()) { throw Kernel::Exception::InstrumentDefinitionError( "Source and sample are at same position!"); } const auto sampleDetVec = position(index) - samplePos; return sampleDetVec.angle(beamLine); }
void LoadRaw3::separateMonitors(FILE *file, const int64_t &period, const std::vector<specnum_t> &monitorList, DataObjects::Workspace2D_sptr ws_sptr, DataObjects::Workspace2D_sptr mws_sptr) { int64_t histCurrent = -1; int64_t wsIndex = 0; int64_t mwsIndex = 0; double histTotal = static_cast<double>(m_total_specs * m_numberOfPeriods); // loop through spectra for (specnum_t i = 1; i <= m_numberOfSpectra; ++i) { int64_t histToRead = i + period * (m_numberOfSpectra + 1); if ((i >= m_spec_min && i < m_spec_max) || (m_list && find(m_spec_list.begin(), m_spec_list.end(), i) != m_spec_list.end())) { progress(m_prog, "Reading raw file data..."); // read spectrum from raw file if (!readData(file, histToRead)) { throw std::runtime_error("Error reading raw file"); } // if this a monitor store that spectrum to monitor workspace if (isMonitor(monitorList, i)) { setWorkspaceData(mws_sptr, m_timeChannelsVec, mwsIndex, i, m_noTimeRegimes, m_lengthIn, 1); ++mwsIndex; } else { // not a monitor,store the spectrum to normal output workspace setWorkspaceData(ws_sptr, m_timeChannelsVec, wsIndex, i, m_noTimeRegimes, m_lengthIn, 1); ++wsIndex; } if (m_numberOfPeriods == 1) { if (++histCurrent % 100 == 0) { setProg(static_cast<double>(histCurrent) / histTotal); } interruption_point(); } } else { skipData(file, histToRead); } } }
/** This method creates outputworkspace excluding monitors *@param file :: -pointer to file *@param period :: period number *@param monitorList :: a list containing the spectrum numbers for monitors *@param ws_sptr :: shared pointer to workspace */ void LoadRaw3::excludeMonitors(FILE *file, const int &period, const std::vector<specnum_t> &monitorList, DataObjects::Workspace2D_sptr ws_sptr) { int64_t histCurrent = -1; int64_t wsIndex = 0; double histTotal = static_cast<double>(m_total_specs * m_numberOfPeriods); // loop through the spectra for (specnum_t i = 1; i <= m_numberOfSpectra; ++i) { specnum_t histToRead = i + period * (m_numberOfSpectra + 1); if ((i >= m_spec_min && i < m_spec_max) || (m_list && find(m_spec_list.begin(), m_spec_list.end(), i) != m_spec_list.end())) { progress(m_prog, "Reading raw file data..."); // skip monitor spectrum if (isMonitor(monitorList, i)) { skipData(file, histToRead); continue; } // read spectrum if (!readData(file, histToRead)) { throw std::runtime_error("Error reading raw file"); } // set the workspace data setWorkspaceData(ws_sptr, m_timeChannelsVec, wsIndex, i, m_noTimeRegimes, m_lengthIn, 1); // increment workspace index ++wsIndex; if (m_numberOfPeriods == 1) { if (++histCurrent % 100 == 0) { setProg(static_cast<double>(histCurrent) / histTotal); } interruption_point(); } } // end of if loop for spec min,max check else { skipData(file, histToRead); } } // end of for loop }
/** Returns L2 (distance from sample to spectrum). * * For monitors this is defined such that L1+L2 = source-detector distance, * i.e., for a monitor in the beamline between source and sample L2 is negative. */ double DetectorInfo::l2(const std::pair<size_t, size_t> &index) const { if (!isMonitor(index)) return position(index).distance(samplePosition()); else return position(index).distance(sourcePosition()) - l1(); }
/** * Computed the normalization for the input workspace. Results are stored in * m_normWS * @param otherValues * @param affineTrans */ void MDNormDirectSC::calculateNormalization( const std::vector<coord_t> &otherValues, const Kernel::Matrix<coord_t> &affineTrans) { constexpr double energyToK = 8.0 * M_PI * M_PI * PhysicalConstants::NeutronMass * PhysicalConstants::meV * 1e-20 / (PhysicalConstants::h * PhysicalConstants::h); const auto &exptInfoZero = *(m_inputWS->getExperimentInfo(0)); typedef Kernel::PropertyWithValue<std::vector<double>> VectorDoubleProperty; auto *rubwLog = dynamic_cast<VectorDoubleProperty *>(exptInfoZero.getLog("RUBW_MATRIX")); if (!rubwLog) { throw std::runtime_error( "Wokspace does not contain a log entry for the RUBW matrix." "Cannot continue."); } else { Kernel::DblMatrix rubwValue( (*rubwLog)()); // includes the 2*pi factor but not goniometer for now :) m_rubw = exptInfoZero.run().getGoniometerMatrix() * rubwValue; m_rubw.Invert(); } const double protonCharge = exptInfoZero.run().getProtonCharge(); auto instrument = exptInfoZero.getInstrument(); std::vector<detid_t> detIDs = instrument->getDetectorIDs(true); // Prune out those that are part of a group and simply leave the head of the // group detIDs = removeGroupedIDs(exptInfoZero, detIDs); // Mapping const int64_t ndets = static_cast<int64_t>(detIDs.size()); bool haveSA = false; API::MatrixWorkspace_const_sptr solidAngleWS = getProperty("SolidAngleWorkspace"); detid2index_map solidAngDetToIdx; if (solidAngleWS != nullptr) { haveSA = true; solidAngDetToIdx = solidAngleWS->getDetectorIDToWorkspaceIndexMap(); } auto prog = make_unique<API::Progress>(this, 0.3, 1.0, ndets); PARALLEL_FOR_NO_WSP_CHECK() for (int64_t i = 0; i < ndets; i++) { PARALLEL_START_INTERUPT_REGION const auto detID = detIDs[i]; double theta(0.0), phi(0.0); bool skip(false); try { auto spectrum = getThetaPhi(detID, exptInfoZero, theta, phi); if (spectrum->isMonitor() || spectrum->isMasked()) continue; } catch ( std::exception &) // detector might not exist or has no been included // in grouping { skip = true; // Intel compiler has a problem with continue inside a catch // inside openmp... } if (skip) continue; // Intersections auto intersections = calculateIntersections(theta, phi); if (intersections.empty()) continue; // Get solid angle for this contribution double solid = protonCharge; if (haveSA) { solid = solidAngleWS->readY(solidAngDetToIdx.find(detID)->second)[0] * protonCharge; } // Compute final position in HKL const size_t vmdDims = intersections.front().size(); // pre-allocate for efficiency and copy non-hkl dim values into place std::vector<coord_t> pos(vmdDims + otherValues.size() + 1); std::copy(otherValues.begin(), otherValues.end(), pos.begin() + vmdDims); pos.push_back(1.); auto intersectionsBegin = intersections.begin(); for (auto it = intersectionsBegin + 1; it != intersections.end(); ++it) { const auto &curIntSec = *it; const auto &prevIntSec = *(it - 1); // the full vector isn't used so compute only what is necessary double delta = (curIntSec[3] * curIntSec[3] - prevIntSec[3] * prevIntSec[3]) / energyToK; if (delta < 1e-10) continue; // Assume zero contribution if difference is small // Average between two intersections for final position std::transform(curIntSec.getBareArray(), curIntSec.getBareArray() + vmdDims, prevIntSec.getBareArray(), pos.begin(), VectorHelper::SimpleAverage<coord_t>()); // transform kf to energy transfer pos[3] = static_cast<coord_t>(m_Ei - pos[3] * pos[3] / energyToK); std::vector<coord_t> posNew = affineTrans * pos; size_t linIndex = m_normWS->getLinearIndexAtCoord(posNew.data()); if (linIndex == size_t(-1)) continue; // signal = integral between two consecutive intersections *solid angle // *PC double signal = solid * delta; PARALLEL_CRITICAL(updateMD) { signal += m_normWS->getSignalAt(linIndex); m_normWS->setSignalAt(linIndex, signal); } } prog->report(); PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION }
/** * Computed the normalization for the input workspace. Results are stored in * m_normWS * @param otherValues * @param affineTrans */ void MDNormSCD::calculateNormalization( const std::vector<coord_t> &otherValues, const Kernel::Matrix<coord_t> &affineTrans) { API::MatrixWorkspace_const_sptr integrFlux = getProperty("FluxWorkspace"); integrFlux->getXMinMax(m_kiMin, m_kiMax); API::MatrixWorkspace_const_sptr solidAngleWS = getProperty("SolidAngleWorkspace"); const auto &exptInfoZero = *(m_inputWS->getExperimentInfo(0)); typedef Kernel::PropertyWithValue<std::vector<double>> VectorDoubleProperty; auto *rubwLog = dynamic_cast<VectorDoubleProperty *>(exptInfoZero.getLog("RUBW_MATRIX")); if (!rubwLog) { throw std::runtime_error( "Wokspace does not contain a log entry for the RUBW matrix." "Cannot continue."); } else { Kernel::DblMatrix rubwValue( (*rubwLog)()); // includes the 2*pi factor but not goniometer for now :) m_rubw = exptInfoZero.run().getGoniometerMatrix() * rubwValue; m_rubw.Invert(); } const double protonCharge = exptInfoZero.run().getProtonCharge(); auto instrument = exptInfoZero.getInstrument(); std::vector<detid_t> detIDs = instrument->getDetectorIDs(true); // Prune out those that are part of a group and simply leave the head of the // group detIDs = removeGroupedIDs(exptInfoZero, detIDs); // Mappings const int64_t ndets = static_cast<int64_t>(detIDs.size()); const detid2index_map fluxDetToIdx = integrFlux->getDetectorIDToWorkspaceIndexMap(); const detid2index_map solidAngDetToIdx = solidAngleWS->getDetectorIDToWorkspaceIndexMap(); auto prog = make_unique<API::Progress>(this, 0.3, 1.0, ndets); PARALLEL_FOR1(integrFlux) for (int64_t i = 0; i < ndets; i++) { PARALLEL_START_INTERUPT_REGION const auto detID = detIDs[i]; double theta(0.0), phi(0.0); bool skip(false); try { auto spectrum = getThetaPhi(detID, exptInfoZero, theta, phi); if (spectrum->isMonitor() || spectrum->isMasked()) continue; } catch ( std::exception &) // detector might not exist or has no been included // in grouping { skip = true; // Intel compiler has a problem with continue inside a catch // inside openmp... } if (skip) continue; // Intersections auto intersections = calculateIntersections(theta, phi); if (intersections.empty()) continue; // get the flux spetrum number size_t wsIdx = fluxDetToIdx.find(detID)->second; // Get solid angle for this contribution double solid = solidAngleWS->readY(solidAngDetToIdx.find(detID)->second)[0] * protonCharge; // -- calculate integrals for the intersection -- // momentum values at intersections auto intersectionsBegin = intersections.begin(); std::vector<double> xValues(intersections.size()), yValues(intersections.size()); { // copy momenta to xValues auto x = xValues.begin(); for (auto it = intersectionsBegin; it != intersections.end(); ++it, ++x) { *x = (*it)[3]; } } // calculate integrals at momenta from xValues by interpolating between // points in spectrum sp // of workspace integrFlux. The result is stored in yValues calcIntegralsForIntersections(xValues, *integrFlux, wsIdx, yValues); // Compute final position in HKL const size_t vmdDims = intersections.front().size(); // pre-allocate for efficiency and copy non-hkl dim values into place std::vector<coord_t> pos(vmdDims + otherValues.size()); std::copy(otherValues.begin(), otherValues.end(), pos.begin() + vmdDims - 1); pos.push_back(1.); for (auto it = intersectionsBegin + 1; it != intersections.end(); ++it) { const auto &curIntSec = *it; const auto &prevIntSec = *(it - 1); // the full vector isn't used so compute only what is necessary double delta = curIntSec[3] - prevIntSec[3]; if (delta < 1e-07) continue; // Assume zero contribution if difference is small // Average between two intersections for final position std::transform(curIntSec.getBareArray(), curIntSec.getBareArray() + vmdDims - 1, prevIntSec.getBareArray(), pos.begin(), VectorHelper::SimpleAverage<coord_t>()); std::vector<coord_t> posNew = affineTrans * pos; size_t linIndex = m_normWS->getLinearIndexAtCoord(posNew.data()); if (linIndex == size_t(-1)) continue; // index of the current intersection size_t k = static_cast<size_t>(std::distance(intersectionsBegin, it)); // signal = integral between two consecutive intersections double signal = (yValues[k] - yValues[k - 1]) * solid; PARALLEL_CRITICAL(updateMD) { signal += m_normWS->getSignalAt(linIndex); m_normWS->setSignalAt(linIndex, signal); } } prog->report(); PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION }
/** * Clear the vector of strings and then add pairs of strings giving information * about the specified point, x, y. The first string in a pair should * generally be a string describing the value being presented and the second * string should contain the value. * * @param x The x-coordinate of the point of interest in the data. * @param y The y-coordinate of the point of interest in the data. * @param list Vector that will be filled out with the information strings. */ void MatrixWSDataSource::getInfoList( double x, double y, std::vector<std::string> &list ) { // First get the info that is always available for any matrix workspace list.clear(); int row = (int)y; restrictRow( row ); const ISpectrum* spec = m_matWs->getSpectrum( row ); double spec_num = spec->getSpectrumNo(); SVUtils::PushNameValue( "Spec Num", 8, 0, spec_num, list ); std::string x_label = ""; Unit_sptr& old_unit = m_matWs->getAxis(0)->unit(); if ( old_unit != 0 ) { x_label = old_unit->caption(); SVUtils::PushNameValue( x_label, 8, 3, x, list ); } std::set<detid_t> ids = spec->getDetectorIDs(); if ( !ids.empty() ) { list.push_back("Det ID"); const int64_t id = static_cast<int64_t>(*(ids.begin())); list.push_back(boost::lexical_cast<std::string>(id)); } /* Now try to do various unit conversions to get equivalent info */ /* first make sure we can get the needed information */ if ( !(m_instrument && m_source && m_sample) ) { return; } try { if ( old_unit == 0 ) { g_log.debug("No UNITS on MatrixWorkspace X-axis"); return; } auto det = m_matWs->getDetector( row ); if ( det == 0 ) { g_log.debug() << "No DETECTOR for row " << row << " in MatrixWorkspace" << std::endl; return; } double l1 = m_source->getDistance(*m_sample); double l2 = 0.0; double two_theta = 0.0; double azi = 0.0; if ( det->isMonitor() ) { l2 = det->getDistance(*m_source); l2 = l2-l1; } else { l2 = det->getDistance(*m_sample); two_theta = m_matWs->detectorTwoTheta(det); azi = det->getPhi(); } SVUtils::PushNameValue( "L2", 8, 4, l2, list ); SVUtils::PushNameValue( "TwoTheta", 8, 2, two_theta*180./M_PI, list ); SVUtils::PushNameValue( "Azimuthal", 8, 2, azi*180./M_PI, list ); /* For now, only support diffractometers and monitors. */ /* We need a portable way to determine emode and */ /* and efixed that will work for any matrix workspace! */ int emode = 0; double efixed = 0.0; double delta = 0.0; // First try to get emode & efixed from the user if ( m_emodeHandler != NULL ) { efixed = m_emodeHandler->getEFixed(); if ( efixed != 0 ) { emode = m_emodeHandler->getEMode(); if ( emode == 0 ) { g_log.information("EMode invalid, spectrometer needed if emode != 0"); g_log.information("Assuming Direct Geometry Spectrometer...."); emode = 1; } } } // Did NOT get emode & efixed from user, try getting direct geometry information from the run object if ( efixed == 0 ) { const API::Run & run = m_matWs->run(); if ( run.hasProperty("Ei") ) { Kernel::Property* prop = run.getProperty("Ei"); efixed = boost::lexical_cast<double,std::string>(prop->value()); emode = 1; // only correct if direct geometry } else if ( run.hasProperty("EnergyRequested") ) { Kernel::Property* prop = run.getProperty("EnergyRequested"); efixed = boost::lexical_cast<double,std::string>(prop->value()); emode = 1; } else if ( run.hasProperty("EnergyEstimate") ) { Kernel::Property* prop = run.getProperty("EnergyEstimate"); efixed = boost::lexical_cast<double,std::string>(prop->value()); emode = 1; } } // Finally, try getting indirect geometry information from the detector object if ( efixed == 0 ) { if ( !(det->isMonitor() && det->hasParameter("Efixed"))) { try { const ParameterMap& pmap = m_matWs->constInstrumentParameters(); Parameter_sptr par = pmap.getRecursive(det.get(),"Efixed"); if (par) { efixed = par->value<double>(); emode = 2; } } catch ( std::runtime_error& ) { g_log.debug() << "Failed to get Efixed from detector ID: " << det->getID() << " in MatrixWSDataSource" << std::endl; efixed = 0; } } } if ( efixed == 0 ) emode = 0; if ( m_emodeHandler != NULL ) { m_emodeHandler -> setEFixed( efixed ); m_emodeHandler -> setEMode ( emode ); } double tof = old_unit->convertSingleToTOF( x, l1, l2, two_theta, emode, efixed, delta ); if ( ! (x_label == "Time-of-flight") ) SVUtils::PushNameValue( "Time-of-flight", 8, 1, tof, list ); if ( ! (x_label == "Wavelength") ) { const Unit_sptr& wl_unit = UnitFactory::Instance().create("Wavelength"); double wavelength = wl_unit->convertSingleFromTOF( tof, l1, l2, two_theta, emode, efixed, delta ); SVUtils::PushNameValue( "Wavelength", 8, 4, wavelength, list ); } if ( ! (x_label == "Energy") ) { const Unit_sptr& e_unit = UnitFactory::Instance().create("Energy"); double energy = e_unit->convertSingleFromTOF( tof, l1, l2, two_theta, emode, efixed, delta ); SVUtils::PushNameValue( "Energy", 8, 4, energy, list ); } if ( (! (x_label == "d-Spacing")) && (two_theta != 0.0) && ( emode == 0 ) ) { const Unit_sptr& d_unit = UnitFactory::Instance().create("dSpacing"); double d_spacing = d_unit->convertSingleFromTOF( tof, l1, l2, two_theta, emode, efixed, delta ); SVUtils::PushNameValue( "d-Spacing", 8, 4, d_spacing, list ); } if ( (! (x_label == "q")) && (two_theta != 0.0) ) { const Unit_sptr& q_unit=UnitFactory::Instance().create("MomentumTransfer"); double mag_q = q_unit->convertSingleFromTOF( tof, l1, l2, two_theta, emode, efixed, delta ); SVUtils::PushNameValue( "|Q|", 8, 4, mag_q, list ); } if ( (! (x_label == "DeltaE")) && (two_theta != 0.0) && ( emode != 0 ) ) { const Unit_sptr& deltaE_unit=UnitFactory::Instance().create("DeltaE"); double delta_E = deltaE_unit->convertSingleFromTOF( tof, l1, l2, two_theta, emode, efixed, delta ); SVUtils::PushNameValue( "DeltaE", 8, 4, delta_E, list ); } } catch (std::exception & e) { g_log.debug() << "Failed to get information from Workspace:" << e.what() << std::endl; } }