/** * Change UB to a new matrix corresponding to a hexagonal unit cell with * angles approximately 90, 90, 120. This is used to arrange * the UB matrix for a hexagonal or rhombohedral cell into a standard order. * * @param UB on input this should correspond to a hexagonal or rhombohedral * On output, it will correspond to a hexagonal cell with angles * approximately 90, 90, 120. */ void ConventionalCell::StandardizeHexagonal( Kernel::DblMatrix & UB ) { V3D a; V3D b; V3D c; IndexingUtils::GetABC( UB, a, b, c ); double alpha = b.angle( c ) * 180.0/M_PI; double beta = c.angle( a ) * 180.0/M_PI; // first, make the non 90 // degree angle last if ( fabs(alpha-90) > 20 ) { IndexingUtils::GetUB( UB, b, c, a ); } else if ( fabs(beta-90) > 20 ) { IndexingUtils::GetUB( UB, c, a, b ); } // if the non 90 degree angle // is about 60 degrees, make // it about 120 degrees. IndexingUtils::GetABC( UB, a, b, c ); double gamma = a.angle( b ) * 180.0/M_PI; if ( fabs( gamma - 60 ) < 10 ) { a = a * ( -1 ); // reflect a and c to change c = c * ( -1 ); // alpha and gamma to their IndexingUtils::GetUB( UB, a, b, c ); // supplementary angle } }
/** Get the cell angles for the unit cell corresponding to matrix UB and calculate the sum of the differences of the cell angles from 90 degrees. @param UB the UB matrix @return The sum of the difference of the cell angles from 90 degrees. */ static double GetDiffFrom90Sum(const DblMatrix &UB) { V3D a; V3D b; V3D c; if (!OrientedLattice::GetABC(UB, a, b, c)) return -1; double alpha = b.angle(c) * RAD_TO_DEG; double beta = c.angle(a) * RAD_TO_DEG; double gamma = a.angle(b) * RAD_TO_DEG; double sum = fabs(alpha - 90.0) + fabs(beta - 90.0) + fabs(gamma - 90.0); return sum; }
/** Check if a,b,c cell has angles satifying Niggli condition within epsilon. Specifically, check if all angles are strictly less than 90 degrees, or all angles are greater than or equal to 90 degrees. The inequality requirements are relaxed by an amount specified by the paramter epsilon to accommodate some experimental and/or rounding error in the calculated angles. @param a_dir Vector in the direction of the real cell edge vector 'a' @param b_dir Vector in the direction of the real cell edge vector 'b' @param c_dir Vector in the direction of the real cell edge vector 'c' @param epsilon Tolerance (in degrees) around 90 degrees. For example an angle theta will be considered strictly less than 90 degrees, if it is less than 90+epsilon. @return true if all angles are less than 90 degrees, or if all angles are greater than or equal to 90 degrees. */ bool NiggliCell::HasNiggliAngles(const V3D &a_dir, const V3D &b_dir, const V3D &c_dir, double epsilon) { double alpha = b_dir.angle(c_dir) * RAD_TO_DEG; double beta = c_dir.angle(a_dir) * RAD_TO_DEG; double gamma = a_dir.angle(b_dir) * RAD_TO_DEG; if (alpha < 90 + epsilon && beta < 90 + epsilon && gamma < 90 + epsilon) { return true; } if (alpha >= 90 - epsilon && beta >= 90 - epsilon && gamma >= 90 - epsilon) { return true; } return false; }
/** Get the two theata angle signed according the quadrant @param observer :: The observer position @param axis :: The axis @param instrumentUp :: instrument up direction. @return The angle */ double Detector::getSignedTwoTheta(const V3D &observer, const V3D &axis, const V3D &instrumentUp) const { const V3D sampleDetVec = this->getPos() - observer; double angle = sampleDetVec.angle(axis); V3D cross = axis.cross_prod(sampleDetVec); V3D normToSurface = axis.cross_prod(instrumentUp); if (normToSurface.scalar_prod(cross) < 0) { angle *= -1; } return angle; }
/** * Generate a random position within the final detector in the lab frame * @param nominalPos The poisiton of the centre point of the detector * @param energy The final energy of the neutron * @param scatterPt The position of the scatter event that lead to this * detector * @param direcBeforeSc Directional vector that lead to scatter point that hit * this detector * @param scang [Output] The value of the scattering angle for the generated * point * @param distToExit [Output] The distance covered within the object from * scatter to exit * @return A new position in the detector */ V3D VesuvioCalculateMS::generateDetectorPos( const V3D &nominalPos, const double energy, const V3D &scatterPt, const V3D &direcBeforeSc, double &scang, double &distToExit) const { // Inverse attenuation length (m-1) for vesuvio det. const double mu = 7430.0 / sqrt(energy); // Probability of detection in path thickness. const double ps = 1.0 - exp(-mu * m_detThick); V3D detPos; scang = 0.0; distToExit = 0.0; size_t ntries(0); do { // Beam direction by moving to front of "box"define by detector dimensions // and then // computing expected distance travelled based on probability detPos[m_beamIdx] = (nominalPos[m_beamIdx] - 0.5 * m_detThick) - (log(1.0 - m_randgen->flat() * ps) / mu); // perturb away from nominal position detPos[m_acrossIdx] = nominalPos[m_acrossIdx] + (m_randgen->flat() - 0.5) * m_detWidth; detPos[m_upIdx] = nominalPos[m_upIdx] + (m_randgen->flat() - 0.5) * m_detHeight; // Distance to exit the sample for this order V3D scToDet = detPos - scatterPt; scToDet.normalize(); Geometry::Track scatterToDet(scatterPt, scToDet); if (m_sampleShape->interceptSurface(scatterToDet) > 0) { scang = direcBeforeSc.angle(scToDet); const auto &link = scatterToDet.cbegin(); distToExit = link->distInsideObject; break; } // if point is very close surface then there may be no valid intercept so // try again ++ntries; } while (ntries < MAX_SCATTER_PT_TRIES); if (ntries == MAX_SCATTER_PT_TRIES) { // Assume it is very close to the surface so that the distance travelled // would // be a neglible contribution distToExit = 0.0; } return detPos; }
void ConvertToDiffractionMDWorkspace::convertEventList(int workspaceIndex, EventList &el) { size_t numEvents = el.getNumberEvents(); DataObjects::MDBoxBase<DataObjects::MDLeanEvent<3>, 3> *box = ws->getBox(); // Get the position of the detector there. const std::set<detid_t> &detectors = el.getDetectorIDs(); if (!detectors.empty()) { // Get the detector (might be a detectorGroup for multiple detectors) // or might return an exception if the detector is not in the instrument // definition IDetector_const_sptr det; try { det = m_inWS->getDetector(workspaceIndex); } catch (Exception::NotFoundError &) { this->failedDetectorLookupCount++; return; } // Vector between the sample and the detector V3D detPos = det->getPos() - samplePos; // Neutron's total travelled distance double distance = detPos.norm() + l1; // Detector direction normalized to 1 V3D detDir = detPos / detPos.norm(); // The direction of momentum transfer in the inelastic convention ki-kf // = input beam direction (normalized to 1) - output beam direction // (normalized to 1) V3D Q_dir_lab_frame = beamDir - detDir; double qSign = -1.0; std::string convention = ConfigService::Instance().getString("Q.convention"); if (convention == "Crystallography") qSign = 1.0; Q_dir_lab_frame *= qSign; // Multiply by the rotation matrix to convert to Q in the sample frame (take // out goniometer rotation) // (or to HKL, if that's what the matrix is) V3D Q_dir = mat * Q_dir_lab_frame; // For speed we extract the components. coord_t Q_dir_x = coord_t(Q_dir.X()); coord_t Q_dir_y = coord_t(Q_dir.Y()); coord_t Q_dir_z = coord_t(Q_dir.Z()); // For lorentz correction, calculate sin(theta))^2 double sin_theta_squared = 0; if (LorentzCorrection) { // Scattering angle = 2 theta = angle between neutron beam direction and // the detector (scattering) direction // The formula for Lorentz Correction is sin(theta), i.e. sin(half the // scattering angle) double theta = detDir.angle(beamDir) / 2.0; sin_theta_squared = sin(theta); sin_theta_squared = sin_theta_squared * sin_theta_squared; // square it } /** Constant that you divide by tof (in usec) to get wavenumber in ang^-1 : * Wavenumber (in ang^-1) = (PhysicalConstants::NeutronMass * distance) / * ((tof (in usec) * 1e-6) * PhysicalConstants::h_bar) * 1e-10; */ const double wavenumber_in_angstrom_times_tof_in_microsec = (PhysicalConstants::NeutronMass * distance * 1e-10) / (1e-6 * PhysicalConstants::h_bar); // PARALLEL_CRITICAL( convert_tester_output ) { std::cout << "Spectrum " << // el.getSpectrumNo() << " beamDir = " << beamDir << " detDir = " << detDir // << " Q_dir = " << Q_dir << " conversion factor " << // wavenumber_in_angstrom_times_tof_in_microsec << std::endl; } // g_log.information() << wi << " : " << el.getNumberEvents() << " events. // Pos is " << detPos << std::endl; // g_log.information() << Q_dir.norm() << " Qdir norm" << std::endl; // This little dance makes the getting vector of events more general (since // you can't overload by return type). typename std::vector<T> *events_ptr; getEventsFrom(el, events_ptr); typename std::vector<T> &events = *events_ptr; // Iterators to start/end auto it = events.begin(); auto it_end = events.end(); for (; it != it_end; it++) { // Get the wavenumber in ang^-1 using the previously calculated constant. coord_t wavenumber = coord_t(wavenumber_in_angstrom_times_tof_in_microsec / it->tof()); // Q vector = K_final - K_initial = wavenumber * (output_direction - // input_direction) coord_t center[3] = {Q_dir_x * wavenumber, Q_dir_y * wavenumber, Q_dir_z * wavenumber}; // Check that the event is within bounds if (center[0] < m_extentsMin[0] || center[0] >= m_extentsMax[0]) continue; if (center[1] < m_extentsMin[1] || center[1] >= m_extentsMax[1]) continue; if (center[2] < m_extentsMin[2] || center[2] >= m_extentsMax[2]) continue; if (LorentzCorrection) { // double lambda = 1.0/wavenumber; // (sin(theta))^2 / wavelength^4 float correct = float(sin_theta_squared * wavenumber * wavenumber * wavenumber * wavenumber); // Push the MDLeanEvent but correct the weight. box->addEvent(MDE(float(it->weight() * correct), float(it->errorSquared() * correct * correct), center)); } else { // Push the MDLeanEvent with the same weight box->addEvent( MDE(float(it->weight()), float(it->errorSquared()), center)); } } // Clear out the EventList to save memory if (ClearInputWorkspace) { // Track how much memory you cleared size_t memoryCleared = el.getMemorySize(); // Clear it now el.clear(); // For Linux with tcmalloc, make sure memory goes back, if you've cleared // 200 Megs MemoryManager::Instance().releaseFreeMemoryIfAccumulated( memoryCleared, static_cast<size_t>(2e8)); } } prog->reportIncrement(numEvents, "Adding Events"); }
///Get the twotheta angle between the detector and an observer ///@param observer :: The observer position ///@param axis :: The axis ///@return The angle double Detector::getTwoTheta(const V3D& observer, const V3D& axis) const { const V3D sampleDetVec = this->getPos() - observer; return sampleDetVec.angle(axis); }
/** Execute the algorithm. */ void SaveIsawPeaks::exec() { // Section header std::string header = "2 SEQN H K L COL ROW CHAN L2 2_THETA AZ WL D IPK INTI SIGI RFLG"; std::string filename = getPropertyValue("Filename"); PeaksWorkspace_sptr ws = getProperty("InputWorkspace"); std::vector<Peak> peaks = ws->getPeaks(); // We must sort the peaks first by run, then bank #, and save the list of workspace indices of it typedef std::map<int, std::vector<size_t> > bankMap_t; typedef std::map<int, bankMap_t> runMap_t; std::set<int> uniqueBanks; runMap_t runMap; for (size_t i=0; i < peaks.size(); ++i) { Peak & p = peaks[i]; int run = p.getRunNumber(); int bank = 0; std::string bankName = p.getBankName(); if (bankName.size() <= 4) { g_log.information() << "Could not interpret bank number of peak " << i << "(" << bankName << ")\n"; continue; } // Take out the "bank" part of the bank name and convert to an int bankName = bankName.substr(4, bankName.size()-4); Strings::convert(bankName, bank); // Save in the map runMap[run][bank].push_back(i); // Track unique bank numbers uniqueBanks.insert(bank); } Instrument_const_sptr inst = ws->getInstrument(); if (!inst) throw std::runtime_error("No instrument in PeaksWorkspace. Cannot save peaks file."); double l1; V3D beamline; double beamline_norm; V3D samplePos; inst->getInstrumentParameters(l1, beamline, beamline_norm, samplePos); std::ofstream out; bool append = getProperty("AppendFile"); if (append) { out.open( filename.c_str(), std::ios::app); } else { out.open( filename.c_str()); out << "Version: 2.0 Facility: SNS " ; out << " Instrument: " << inst->getName() << " Date: " ; //TODO: The experiment date might be more useful than the instrument date. // For now, this allows the proper instrument to be loaded back after saving. Kernel::DateAndTime expDate = inst->getValidFromDate() + 1.0; out << expDate.to_ISO8601_string() << std::endl; out << "6 L1 T0_SHIFT" << std::endl; out << "7 "<< std::setw( 10 ) ; out << std::setprecision( 4 ) << std::fixed << ( l1*100 ) ; out << std::setw( 12 ) << std::setprecision( 3 ) << std::fixed ; // Time offset of 0.00 for now out << "0.000" << std::endl; // ============================== Save .detcal info ========================================= if (true) { out << "4 DETNUM NROWS NCOLS WIDTH HEIGHT DEPTH DETD CenterX CenterY CenterZ BaseX BaseY BaseZ UpX UpY UpZ" << std::endl; // Here would save each detector... std::set<int>::iterator it; for (it = uniqueBanks.begin(); it != uniqueBanks.end(); it++) { // Build up the bank name int bank = *it; std::ostringstream mess; mess << "bank" << bank; std::string bankName = mess.str(); // Retrieve it RectangularDetector_const_sptr det = boost::dynamic_pointer_cast<const RectangularDetector>(inst->getComponentByName(bankName)); if (det) { // Center of the detector V3D center = det->getPos(); // Distance to center of detector double detd = (center - inst->getSample()->getPos()).norm(); // Base unit vector (along the horizontal, X axis) V3D base = det->getAtXY(det->xpixels()-1,0)->getPos() - det->getAtXY(0,0)->getPos(); base.normalize(); // Up unit vector (along the vertical, Y axis) V3D up = det->getAtXY(0,det->ypixels()-1)->getPos() - det->getAtXY(0,0)->getPos(); up.normalize(); // Write the line out << "5 " << std::setw(6) << std::right << bank << " " << std::setw(6) << std::right << det->xpixels() << " " << std::setw(6) << std::right << det->ypixels() << " " << std::setw(7) << std::right << std::fixed << std::setprecision(4) << 100.0*det->xsize() << " " << std::setw(7) << std::right << std::fixed << std::setprecision(4) << 100.0*det->ysize() << " " << " 0.2000 " << std::setw(6) << std::right << std::fixed << std::setprecision(2) << 100.0*detd << " " << std::setw(9) << std::right << std::fixed << std::setprecision(4) << 100.0*center.X() << " " << std::setw(9) << std::right << std::fixed << std::setprecision(4) << 100.0*center.Y() << " " << std::setw(9) << std::right << std::fixed << std::setprecision(4) << 100.0*center.Z() << " " << std::setw(8) << std::right << std::fixed << std::setprecision(5) << base.X() << " " << std::setw(8) << std::right << std::fixed << std::setprecision(5) << base.Y() << " " << std::setw(8) << std::right << std::fixed << std::setprecision(5) << base.Z() << " " << std::setw(8) << std::right << std::fixed << std::setprecision(5) << up.X() << " " << std::setw(8) << std::right << std::fixed << std::setprecision(5) << up.Y() << " " << std::setw(8) << std::right << std::fixed << std::setprecision(5) << up.Z() << " " << std::endl; } } } } // ============================== Save all Peaks ========================================= // Sequence number int seqNum = 1; // Go in order of run numbers runMap_t::iterator runMap_it; for (runMap_it = runMap.begin(); runMap_it != runMap.end(); runMap_it++) { // Start of a new run int run = runMap_it->first; bankMap_t & bankMap = runMap_it->second; bankMap_t::iterator bankMap_it; for (bankMap_it = bankMap.begin(); bankMap_it != bankMap.end(); bankMap_it++) { // Start of a new bank. int bank = bankMap_it->first; std::vector<size_t> & ids = bankMap_it->second; if (ids.size() > 0) { // Write the bank header out << "0 NRUN DETNUM CHI PHI OMEGA MONCNT" << std::endl; out << "1" << std::setw( 5 ) << run << std::setw( 7 ) << std::right << bank; // Determine goniometer angles by calculating from the goniometer matrix of a peak in the list Goniometer gon(peaks[ids[0]].getGoniometerMatrix()); std::vector<double> angles = gon.getEulerAngles("yzy"); double phi = angles[2]; double chi = angles[1]; double omega = angles[0]; out << std::setw( 7 ) << std::fixed << std::setprecision( 2 ) << chi << " "; out << std::setw( 7 ) << std::fixed << std::setprecision( 2 ) << phi << " "; out << std::setw( 7 ) << std::fixed << std::setprecision( 2 ) << omega << " "; out << std::setw( 7 ) << (int)( 0 ) << std::endl; out << header << std::endl; // Go through each peak at this run / bank for (size_t i=0; i < ids.size(); i++) { size_t wi = ids[i]; Peak & p = peaks[wi]; // Sequence (run) number out << "3" << std::setw( 7 ) << seqNum; // HKL is flipped by -1 due to different q convention in ISAW vs mantid. out << std::setw( 5 ) << Utils::round(-p.getH()) << std::setw( 5 ) << Utils::round(-p.getK()) << std::setw( 5 ) << Utils::round(-p.getL()); // Row/column out << std::setw( 8 ) << std::fixed << std::setprecision( 2 ) << static_cast<double>(p.getCol()) << " "; out << std::setw( 8 ) << std::fixed << std::setprecision( 2 ) << static_cast<double>(p.getRow()) << " "; out << std::setw( 8 ) << std::fixed << std::setprecision( 0 ) << p.getTOF() << " "; out << std::setw( 9 ) << std::fixed << std::setprecision( 3 ) << (p.getL2()*100.0) << " "; // This is the scattered beam direction V3D dir = p.getDetPos() - inst->getSample()->getPos(); double scattering, azimuth; // Two-theta = polar angle = scattering angle = between +Z vector and the scattered beam scattering = dir.angle( V3D(0.0, 0.0, 1.0) ); // "Azimuthal" angle: project the beam onto the XY plane, and measure the angle between that and the +X axis (right-handed) azimuth = atan2( dir.Y(), dir.X() ); out << std::setw( 9 ) << std::fixed << std::setprecision( 5 ) << scattering << " "; //two-theta scattering out << std::setw( 9 ) << std::fixed << std::setprecision( 5 ) << azimuth << " "; out << std::setw( 10 ) << std::fixed << std::setprecision( 6 ) << p.getWavelength() << " "; out << std::setw( 9 ) << std::fixed << std::setprecision( 4 ) << p.getDSpacing() << " "; out << std::setw( 8 ) << std::fixed << int(p.getBinCount()) << std::setw( 10 ) << " " << std::fixed << std::setprecision( 2 ) << p.getIntensity() << " "; out << std::setw( 7 ) << std::fixed << std::setprecision( 2 ) << p.getSigmaIntensity() << " "; int thisReflag = 310; out << std::setw( 5 ) << thisReflag; out << std::endl; // Count the sequence seqNum++; } } } } out.flush(); out.close(); // //REMOVE: // std::string line; // std::ifstream myfile (filename.c_str()); // if (myfile.is_open()) // { // while ( myfile.good() ) // { // getline (myfile,line); // std::cout << line << std::endl; // } // myfile.close(); // } }
void AnvredCorrection::execEvent() { const int64_t numHists = static_cast<int64_t>(m_inputWS->getNumberHistograms()); std::string unitStr = m_inputWS->getAxis(0)->unit()->unitID(); //Create a new outputworkspace with not much in it DataObjects::EventWorkspace_sptr correctionFactors; correctionFactors = boost::dynamic_pointer_cast<EventWorkspace>( API::WorkspaceFactory::Instance().create("EventWorkspace",numHists,2,1) ); correctionFactors->sortAll(TOF_SORT, NULL); //Copy required stuff from it API::WorkspaceFactory::Instance().initializeFromParent(m_inputWS, correctionFactors, true); bool inPlace = (this->getPropertyValue("InputWorkspace") == this->getPropertyValue("OutputWorkspace")); if (inPlace) g_log.debug("Correcting EventWorkspace in-place."); // If sample not at origin, shift cached positions. const V3D samplePos = m_inputWS->getInstrument()->getSample()->getPos(); const V3D pos = m_inputWS->getInstrument()->getSource()->getPos()-samplePos; double L1 = pos.norm(); Progress prog(this,0.0,1.0,numHists); // Loop over the spectra PARALLEL_FOR2(eventW,correctionFactors) for (int64_t i = 0; i < int64_t(numHists); ++i) { PARALLEL_START_INTERUPT_REGION // Copy over bin boundaries const MantidVec& X = eventW->readX(i); correctionFactors->dataX(i) = X; // Get detector position IDetector_const_sptr det; try { det = eventW->getDetector(i); } catch (Exception::NotFoundError&) { // 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, skip onto the next spectrum if ( !det ) continue; // This is the scattered beam direction Instrument_const_sptr inst = eventW->getInstrument(); V3D dir = det->getPos() - samplePos; double L2 = dir.norm(); // Two-theta = polar angle = scattering angle = between +Z vector and the scattered beam double scattering = dir.angle( V3D(0.0, 0.0, 1.0) ); EventList el = eventW->getEventList(i); el.switchTo(WEIGHTED_NOTIME); std::vector<WeightedEventNoTime> events = el.getWeightedEventsNoTime(); std::vector<WeightedEventNoTime>::iterator itev; std::vector<WeightedEventNoTime>::iterator itev_end = events.end(); Mantid::Kernel::Units::Wavelength wl; std::vector<double> timeflight; // multiplying an event list by a scalar value for (itev = events.begin(); itev != itev_end; itev++) { timeflight.push_back(itev->tof()); if (unitStr.compare("TOF") == 0) wl.fromTOF(timeflight, timeflight, L1, L2, scattering, 0, 0, 0); double value = this->getEventWeight(timeflight[0], scattering); timeflight.clear(); itev->m_errorSquared = static_cast<float>(itev->m_errorSquared * value*value); itev->m_weight *= static_cast<float>(value); } correctionFactors->getOrAddEventList(i) +=events; std::set<detid_t>& dets = eventW->getEventList(i).getDetectorIDs(); std::set<detid_t>::iterator j; for (j = dets.begin(); j != dets.end(); ++j) correctionFactors->getOrAddEventList(i).addDetectorID(*j); // When focussing in place, you can clear out old memory from the input one! if (inPlace) { eventW->getEventList(i).clear(); Mantid::API::MemoryManager::Instance().releaseFreeMemory(); } prog.report(); PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION correctionFactors->doneAddingEventLists(); setProperty("OutputWorkspace", boost::dynamic_pointer_cast<MatrixWorkspace>(correctionFactors)); // Now do some cleaning-up since destructor may not be called immediately this->cleanup(); }