Mantid
Loading...
Searching...
No Matches
PredictPeaks.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
10#include "MantidAPI/Run.h"
11#include "MantidAPI/Sample.h"
30
31#include <fstream>
33
34namespace Mantid::Crystal {
35
36// Register the algorithm into the AlgorithmFactory
37DECLARE_ALGORITHM(PredictPeaks)
38
39using namespace Mantid::API;
40using namespace Mantid::DataObjects;
41using namespace Mantid::Geometry;
42using namespace Mantid::Kernel;
43
47 : m_refConds(getAllReflectionConditions()), m_runNumber(-1), m_inst(), m_pw(), m_sfCalculator(),
48 m_qConventionFactor(qConventionFactor()) {}
49
53 declareProperty(std::make_unique<WorkspaceProperty<Workspace>>("InputWorkspace", "", Direction::Input),
54 "An input workspace (MatrixWorkspace, MDEventWorkspace, or "
55 "PeaksWorkspace) containing:\n"
56 " - The relevant Instrument (calibrated as needed).\n"
57 " - A sample with a UB matrix.\n"
58 " - The goniometer rotation matrix.");
59
60 declareProperty(std::make_unique<PropertyWithValue<double>>("WavelengthMin", 0.1, Direction::Input),
61 "Minimum wavelength limit at which to start looking for single-crystal "
62 "peaks.");
63 declareProperty(std::make_unique<PropertyWithValue<double>>("WavelengthMax", 100.0, Direction::Input),
64 "Maximum wavelength limit at which to stop looking for single-crystal "
65 "peaks.");
66
67 declareProperty(std::make_unique<PropertyWithValue<double>>("MinDSpacing", 1.0, Direction::Input),
68 "Minimum d-spacing of peaks to consider. Default = 1.0");
69 declareProperty(std::make_unique<PropertyWithValue<double>>("MaxDSpacing", 100.0, Direction::Input),
70 "Maximum d-spacing of peaks to consider.");
71
72 declareProperty("CalculateGoniometerForCW", false,
73 "This will calculate the goniometer rotation (around y-axis "
74 "only) for a constant wavelength.");
75
76 auto nonNegativeDbl = std::make_shared<BoundedValidator<double>>();
77 nonNegativeDbl->setLower(0);
78 declareProperty("Wavelength", DBL_MAX, nonNegativeDbl, "Wavelength to use when calculating goniometer angle");
79 setPropertySettings("Wavelength", std::make_unique<EnabledWhenProperty>("CalculateGoniometerForCW", IS_NOT_DEFAULT));
80
81 declareProperty("InnerGoniometer", false,
82 "Whether the goniometer to be calculated is the most inner "
83 "(phi) or most outer (omega)");
84 setPropertySettings("InnerGoniometer",
85 std::make_unique<EnabledWhenProperty>("CalculateGoniometerForCW",
87
88 declareProperty("FlipX", false,
89 "Used when calculating goniometer angle if the q_lab x value "
90 "should be negative, hence the detector of the other side "
91 "(right) of the beam");
92 setPropertySettings("FlipX", std::make_unique<EnabledWhenProperty>(
93 "CalculateGoniometerForCW", Mantid::Kernel::ePropertyCriterion::IS_NOT_DEFAULT));
94
95 declareProperty(std::make_unique<PropertyWithValue<double>>("MinAngle", -180, Direction::Input),
96 "Minimum goniometer rotation angle");
97 setPropertySettings("MinAngle", std::make_unique<EnabledWhenProperty>("CalculateGoniometerForCW", IS_NOT_DEFAULT));
98
99 declareProperty(std::make_unique<PropertyWithValue<double>>("MaxAngle", 180, Direction::Input),
100 "Maximum goniometer rotation angle");
101 setPropertySettings("MaxAngle", std::make_unique<EnabledWhenProperty>("CalculateGoniometerForCW", IS_NOT_DEFAULT));
102
103 // Build up a list of reflection conditions to use
104 std::vector<std::string> propOptions;
105 propOptions.reserve(m_refConds.size());
106 std::transform(m_refConds.cbegin(), m_refConds.cend(), std::back_inserter(propOptions),
107 [](const auto &condition) { return condition->getName(); });
108 declareProperty("ReflectionCondition", "Primitive", std::make_shared<StringListValidator>(propOptions),
109 "Which reflection condition applies to this crystal, "
110 "reducing the number of expected HKL peaks?");
111
112 declareProperty("CalculateStructureFactors", false,
113 "Calculate structure factors for the predicted peaks. This "
114 "option only works if the sample of the input workspace has "
115 "a crystal structure assigned.");
116
117 declareProperty(std::make_unique<WorkspaceProperty<IPeaksWorkspace>>("HKLPeaksWorkspace", "", Direction::Input,
119 "Optional: An input PeaksWorkspace with the HKL of the peaks "
120 "that we should predict. \n"
121 "The WavelengthMin/Max and Min/MaxDSpacing parameters are "
122 "unused if this is specified.");
123
124 declareProperty("RoundHKL", true,
125 "When using HKLPeaksWorkspace, this will round the HKL "
126 "values in the HKLPeaksWorkspace to the nearest integers if "
127 "checked.\n"
128 "Keep unchecked to use the original values");
129
130 setPropertySettings("RoundHKL", std::make_unique<EnabledWhenProperty>("HKLPeaksWorkspace", IS_NOT_DEFAULT));
131
132 // Disable some props when using HKLPeaksWorkspace
133 auto makeSet = [] {
134 std::unique_ptr<IPropertySettings> set = std::make_unique<EnabledWhenProperty>("HKLPeaksWorkspace", IS_DEFAULT);
135 return set;
136 };
137 setPropertySettings("WavelengthMin", makeSet());
138 setPropertySettings("WavelengthMax", makeSet());
139 setPropertySettings("MinDSpacing", makeSet());
140 setPropertySettings("MaxDSpacing", makeSet());
141 setPropertySettings("ReflectionCondition", makeSet());
142
143 std::vector<std::string> peakTypes = {"Peak", "LeanElasticPeak"};
144 declareProperty("OutputType", "Peak", std::make_shared<StringListValidator>(peakTypes),
145 "Type of Peak in OutputWorkspace");
147 "CalculateWavelength", true,
148 "When OutputType is LeanElasticPeak you can choose to calculate the wavelength of the peak using the instrument "
149 "and check it is in the valid range or alternatively just accept every peak while not setting the goniometer "
150 "(Q-lab will be incorrect).");
151 setPropertySettings("CalculateWavelength", std::make_unique<EnabledWhenProperty>("OutputType", IS_NOT_DEFAULT));
152
153 declareProperty(std::make_unique<WorkspaceProperty<IPeaksWorkspace>>("OutputWorkspace", "", Direction::Output),
154 "An output PeaksWorkspace.");
155
156 declareProperty("PredictPeaksOutsideDetectors", false,
157 "Use an extended detector space (if defined for the"
158 " instrument) to predict peaks which do not fall onto any"
159 "detector. This may produce a very high number of results.");
160
161 auto nonNegativeInt = std::make_shared<BoundedValidator<int>>();
162 nonNegativeInt->setLower(0);
163 declareProperty("EdgePixels", 0, nonNegativeInt, "Remove peaks that are at pixels this close to edge. ");
164}
165
169 // Get the input properties
170 Workspace_sptr rawInputWorkspace = getProperty("InputWorkspace");
171 m_edge = this->getProperty("EdgePixels");
172 m_leanElasticPeak = (getPropertyValue("OutputType") == "LeanElasticPeak");
173 bool usingInstrument = !(m_leanElasticPeak && !getProperty("CalculateWavelength"));
174
175 ExperimentInfo_sptr inputExperimentInfo = std::dynamic_pointer_cast<ExperimentInfo>(rawInputWorkspace);
176
177 MatrixWorkspace_sptr matrixWS = std::dynamic_pointer_cast<MatrixWorkspace>(rawInputWorkspace);
178 PeaksWorkspace_sptr peaksWS = std::dynamic_pointer_cast<PeaksWorkspace>(rawInputWorkspace);
179 MultipleExperimentInfos_sptr mdWS = std::dynamic_pointer_cast<MultipleExperimentInfos>(rawInputWorkspace);
180
181 std::vector<DblMatrix> gonioVec;
182 std::string goniometerConvension;
183 if (matrixWS) {
184 // Retrieve the goniometer rotation matrix
185 try {
186 auto goniometerMatrix = matrixWS->run().getGoniometerMatrices();
187 std::move(goniometerMatrix.begin(), goniometerMatrix.end(), back_inserter(gonioVec));
188 } catch (std::runtime_error &e) {
189 // If there is no goniometer matrix, use identity matrix instead.
190 g_log.error() << "Error getting the goniometer rotation matrix from the "
191 "InputWorkspace.\n"
192 << e.what() << '\n';
193 g_log.warning() << "Using identity goniometer rotation matrix instead.\n";
194 }
195
196 goniometerConvension = matrixWS->run().getGoniometer().getConventionFromMotorAxes();
197 } else if (peaksWS) {
198 // Sort peaks by run number so that peaks with equal goniometer matrices are
199 // adjacent
200 std::vector<std::pair<std::string, bool>> criteria;
201 criteria.emplace_back("RunNumber", true);
202
203 peaksWS->sort(criteria);
204
205 // Get all goniometer matrices
206 DblMatrix lastGoniometerMatrix = Matrix<double>(3, 3, false);
207 for (int i = 0; i < static_cast<int>(peaksWS->getNumberPeaks()); ++i) {
208 IPeak const &p = peaksWS->getPeak(i);
209 DblMatrix currentGoniometerMatrix = p.getGoniometerMatrix();
210 if (!(currentGoniometerMatrix == lastGoniometerMatrix)) {
211 gonioVec.emplace_back(currentGoniometerMatrix);
212 lastGoniometerMatrix = currentGoniometerMatrix;
213 }
214 }
215
216 goniometerConvension = peaksWS->run().getGoniometer().getConventionFromMotorAxes();
217 } else if (mdWS) {
218 if (mdWS->getNumExperimentInfo() <= 0)
219 throw std::invalid_argument("Specified a MDEventWorkspace as InputWorkspace but it does not have "
220 "any ExperimentInfo associated. Please choose a workspace with a "
221 "full instrument and sample.");
222
223 inputExperimentInfo = mdWS->getExperimentInfo(0);
224
225 // Retrieve the goniometer rotation matrices for each experiment info
226 for (uint16_t i = 0; i < mdWS->getNumExperimentInfo(); ++i) {
227 try {
228 auto goniometerMatrix = mdWS->getExperimentInfo(i)->mutableRun().getGoniometerMatrices();
229 std::move(goniometerMatrix.begin(), goniometerMatrix.end(), back_inserter(gonioVec));
230 } catch (std::runtime_error &e) {
231 // If there is no goniometer matrix, use identity matrix instead.
232 gonioVec.emplace_back(DblMatrix(3, 3, true));
233
234 g_log.error() << "Error getting the goniometer rotation matrix from the "
235 "InputWorkspace.\n"
236 << e.what() << '\n';
237 g_log.warning() << "Using identity goniometer rotation matrix instead.\n";
238 }
239 }
240
241 goniometerConvension = inputExperimentInfo->run().getGoniometer().getConventionFromMotorAxes();
242 }
243
244 // If there's no goniometer matrix at this point, push back an identity
245 // matrix.
246 if (gonioVec.empty()) {
247 gonioVec.emplace_back(DblMatrix(3, 3, true));
248 }
249
250 if (usingInstrument) {
251 setInstrumentFromInputWorkspace(inputExperimentInfo);
252 setRunNumberFromInputWorkspace(inputExperimentInfo);
255 }
256
257 // Create the output
258 if (m_leanElasticPeak) {
259 m_pw = std::make_shared<LeanElasticPeaksWorkspace>();
260 } else {
261 m_pw = std::make_shared<PeaksWorkspace>();
262 }
263 // Copy instrument, sample, etc.
264 m_pw->copyExperimentInfoFrom(inputExperimentInfo.get());
265
266 const Sample &sample = inputExperimentInfo->sample();
267
268 // Retrieve the OrientedLattice (UnitCell) from the workspace
269 const OrientedLattice &orientedLattice = sample.getOrientedLattice();
270
271 // Get the UB matrix from it
272 Matrix<double> ub(3, 3, true);
273 ub = orientedLattice.getUB();
274
275 std::vector<V3D> possibleHKLs;
276 IPeaksWorkspace_sptr possibleHKLWorkspace = getProperty("HKLPeaksWorkspace");
277
278 if (!possibleHKLWorkspace) {
279 fillPossibleHKLsUsingGenerator(orientedLattice, possibleHKLs);
280 } else {
281 fillPossibleHKLsUsingPeaksWorkspace(possibleHKLWorkspace, possibleHKLs);
282 }
283
285
286 /* The wavelength filtering can not be done before because it depends
287 * on q being correctly oriented, so an additional filtering step is required.
288 */
289 double lambdaMin = getProperty("WavelengthMin");
290 double lambdaMax = getProperty("WavelengthMax");
291
292 Progress prog(this, 0.0, 1.0, possibleHKLs.size() * gonioVec.size());
293 prog.setNotifyStep(0.01);
294
295 if (usingInstrument)
296 m_detectorCacheSearch = std::make_unique<DetectorSearcher>(m_inst, m_pw->detectorInfo());
297
298 if (!usingInstrument) {
299 for (auto const &possibleHKL : possibleHKLs) {
301 }
302 } else if (getProperty("CalculateGoniometerForCW")) {
303 size_t allowedPeakCount = 0;
304
305 double wavelength = getProperty("Wavelength");
306 if (wavelength == DBL_MAX) {
307 if (m_inst->hasParameter("wavelength")) {
308 wavelength = m_inst->getNumberParameter("wavelength").at(0);
309 } else {
310 throw std::runtime_error("Could not get wavelength, neither Wavelength algorithm property "
311 "set nor instrument wavelength parameter");
312 }
313 }
314 double angleMin = getProperty("MinAngle");
315 double angleMax = getProperty("MaxAngle");
316 bool innerGoniometer = getProperty("InnerGoniometer");
317 bool flipX = getProperty("FlipX");
318 if (goniometerConvension.empty()) {
319 // use default universal goniometer
320 goniometerConvension = "YZY";
321 }
322 for (const auto &possibleHKL : possibleHKLs) {
323 Geometry::Goniometer goniometer(gonioVec.front());
324 V3D q_sample = ub * possibleHKL * (2.0 * M_PI * m_qConventionFactor);
325 goniometer.calcFromQSampleAndWavelength(q_sample, wavelength, flipX, innerGoniometer);
326 std::vector<double> angles = goniometer.getEulerAngles(goniometerConvension);
327 double angle = innerGoniometer ? angles[2] : angles[0];
328 DblMatrix orientedUB = goniometer.getR() * ub;
329
330 // NOTE: use standard formula
331 // qLab = goniometerMatirx * qSample
332 // = goniometerMatirx * (2pi * UB * hkl * signConvention)
333 V3D q_lab = goniometer.getR() * q_sample;
334 // NOTE: use standard formula
335 // 4pi 4pi |Q^lab_z|
336 // lambda = --------- sin(theta) = ----------------
337 // Q^lab |Q^lab|^2
338 double lambda = (4.0 * M_PI * std::abs(q_lab.Z())) / q_lab.norm2();
339
340 if (!std::isfinite(angle) || angle < angleMin || angle > angleMax)
341 continue;
342
343 if (Kernel::withinAbsoluteDifference(wavelength, lambda, 0.01)) {
344 g_log.information() << "Found goniometer rotation to be in " << goniometerConvension << " convention ["
345 << angles[0] << ", " << angles[1] << ". " << angles[2]
346 << "] degrees for Q sample = " << q_sample << "\n";
347 calculateQAndAddToOutput(possibleHKL, orientedUB, goniometer.getR());
348 ++allowedPeakCount;
349 }
350 prog.report();
351 }
352
353 logNumberOfPeaksFound(allowedPeakCount);
354
355 } else {
356 for (auto const &goniometerMatrix : gonioVec) {
357 // Final transformation matrix (HKL to Q in lab frame)
358 DblMatrix orientedUB = goniometerMatrix * ub;
359
360 /* Because of the additional filtering step it's better to keep track of
361 * the allowed peaks with a counter. */
362 HKLFilterWavelength lambdaFilter(orientedUB, lambdaMin, lambdaMax);
363
364 size_t allowedPeakCount = 0;
365
366 bool useExtendedDetectorSpace = getProperty("PredictPeaksOutsideDetectors");
367 if (useExtendedDetectorSpace && !m_inst->getComponentByName("extended-detector-space")) {
368 g_log.warning() << "Attempting to find peaks outside of detectors but "
369 "no extended detector space has been defined\n";
370 }
371
372 for (auto const &possibleHKL : possibleHKLs) {
373 if (lambdaFilter.isAllowed(possibleHKL)) {
374 calculateQAndAddToOutput(possibleHKL, orientedUB, goniometerMatrix);
375 ++allowedPeakCount;
376 }
377 prog.report();
378 }
379
380 logNumberOfPeaksFound(allowedPeakCount);
381 }
382 }
383
384 // Sort peaks by run number so that peaks with equal goniometer matrices are
385 // adjacent
386 std::vector<std::pair<std::string, bool>> criteria;
387 criteria.emplace_back("RunNumber", true);
389 criteria.emplace_back("BankName", true);
390 m_pw->sort(criteria);
391
392 for (int i = 0; i < static_cast<int>(m_pw->getNumberPeaks()); ++i) {
393 m_pw->getPeak(i).setPeakNumber(i);
394 }
395 setProperty<IPeaksWorkspace_sptr>("OutputWorkspace", m_pw);
396}
397
403void PredictPeaks::logNumberOfPeaksFound(size_t allowedPeakCount) const {
404 if (auto pw = std::dynamic_pointer_cast<PeaksWorkspace>(m_pw)) {
405 const bool usingExtendedDetectorSpace = getProperty("PredictPeaksOutsideDetectors");
406 const auto &peaks = pw->getPeaks();
407 size_t offDetectorPeakCount = 0;
408 size_t onDetectorPeakCount = 0;
409
410 for (const auto &peak : peaks) {
411 if (peak.getDetectorID() == -1) {
412 offDetectorPeakCount++;
413 } else {
414 onDetectorPeakCount++;
415 }
416 }
417
418 g_log.notice() << "Out of " << allowedPeakCount << " allowed peaks within parameters, " << onDetectorPeakCount
419 << " were found to hit a detector";
420 if (usingExtendedDetectorSpace) {
421 g_log.notice() << " and " << offDetectorPeakCount << " were found in "
422 << "extended detector space.";
423 }
424
425 g_log.notice() << "\n";
426 }
427}
428
431 // Check that there is an input workspace that has a sample.
432 if (!inWS || !inWS->getInstrument())
433 throw std::invalid_argument("Did not specify a valid InputWorkspace with a "
434 "full instrument.");
435
436 m_inst = inWS->getInstrument();
437}
438
441 if (!inWS) {
442 throw std::runtime_error("Failed to get run number");
443 }
444
445 m_runNumber = inWS->getRunNumber();
446}
447
450 const auto sample = m_inst->getSample();
451 if (!sample)
452 throw std::runtime_error("Instrument sample position has not been set");
453 const V3D samplePos = sample->getPos();
454
455 // L1 path and direction
456 V3D beamDir = m_inst->getSource()->getPos() - samplePos;
457
458 if ((fabs(beamDir.X()) > 1e-2) || (fabs(beamDir.Y()) > 1e-2)) // || (beamDir.Z() < 0))
459 throw std::invalid_argument("Instrument must have a beam direction that "
460 "is only in the +Z direction for this "
461 "algorithm to be valid..");
462}
463
467 std::vector<V3D> &possibleHKLs) const {
468 const double dMin = getProperty("MinDSpacing");
469 const double dMax = getProperty("MaxDSpacing");
470
471 // --- Reflection condition ----
472 // Use the primitive by default
473 ReflectionCondition_sptr refCond = std::make_shared<ReflectionConditionPrimitive>();
474 // Get it from the property
475 const std::string refCondName = getPropertyValue("ReflectionCondition");
476 const auto found = std::find_if(m_refConds.crbegin(), m_refConds.crend(), [&refCondName](const auto &condition) {
477 return condition->getName() == refCondName;
478 });
479 if (found != m_refConds.crend()) {
480 refCond = *found;
481 }
482
483 HKLGenerator gen(orientedLattice, dMin);
484 auto filter =
485 std::make_shared<HKLFilterCentering>(refCond) & std::make_shared<HKLFilterDRange>(orientedLattice, dMin, dMax);
486
487 V3D hklMin = *(gen.begin());
488
489 g_log.information() << "HKL range for d_min of " << dMin << " to d_max of " << dMax << " is from " << hklMin << " to "
490 << hklMin * -1.0 << ", a total of " << gen.size() << " possible HKL's\n";
491
492 if (gen.size() > 10000000000)
493 throw std::invalid_argument("More than 10 billion HKLs to search. Is "
494 "your d_min value too small?");
495
496 possibleHKLs.clear();
497 possibleHKLs.reserve(gen.size());
498 std::remove_copy_if(gen.begin(), gen.end(), std::back_inserter(possibleHKLs), (~filter)->fn());
499}
500
503 std::vector<V3D> &possibleHKLs) const {
504 possibleHKLs.clear();
505 possibleHKLs.reserve(peaksWorkspace->getNumberPeaks());
506
507 bool roundHKL = getProperty("RoundHKL");
508
509 /* Q is at the end multiplied with the factor determined in the
510 * constructor (-1 for crystallography, 1 otherwise). So to avoid
511 * "flippling HKLs" when it's not required, the HKLs of the input
512 * workspace are also multiplied by the factor that is appropriate
513 * for the convention stored in the workspace.
514 */
515 double peaks_q_convention_factor = qConventionFactor(peaksWorkspace->getConvention());
516
517 for (int i = 0; i < static_cast<int>(peaksWorkspace->getNumberPeaks()); ++i) {
518 IPeak const &p = peaksWorkspace->getPeak(i);
519 // Get HKL from that peak
520 V3D hkl = p.getHKL() * peaks_q_convention_factor;
521
522 if (roundHKL)
523 hkl.round();
524
525 possibleHKLs.emplace_back(hkl);
526 } // for each hkl in the workspace
527}
528
543 bool calculateStructureFactors = getProperty("CalculateStructureFactors");
544
545 if (calculateStructureFactors && sample.hasCrystalStructure()) {
546 CrystalStructure crystalStructure = sample.getCrystalStructure();
547 crystalStructure.setCell(sample.getOrientedLattice());
548
549 m_sfCalculator = StructureFactorCalculatorFactory::create<StructureFactorCalculatorSummation>(crystalStructure);
550 }
551}
552
566void PredictPeaks::calculateQAndAddToOutput(const V3D &hkl, const DblMatrix &orientedUB,
567 const DblMatrix &goniometerMatrix) {
568 // The q-vector direction of the peak is = goniometer * ub * hkl_vector
569 // This is in inelastic convention: momentum transfer of the LATTICE!
570 // Also, q does have a 2pi factor = it is equal to 2pi/wavelength.
571 const auto q = orientedUB * hkl * (2.0 * M_PI * m_qConventionFactor);
572 const auto params = getPeakParametersFromQ(q);
573 const auto detectorDir = std::get<0>(params);
574 const auto wl = std::get<1>(params);
575
576 const bool useExtendedDetectorSpace = getProperty("PredictPeaksOutsideDetectors");
577 const auto result = m_detectorCacheSearch->findDetectorIndex(q);
578 const auto hitDetector = std::get<0>(result);
579 const auto index = std::get<1>(result);
580
581 if (!hitDetector && !useExtendedDetectorSpace) {
582 return;
583 }
584
585 const auto &detInfo = m_pw->detectorInfo();
586 const auto &det = detInfo.detector(index);
587 std::unique_ptr<Peak> peak;
588
589 if (hitDetector) {
590 // peak hit a detector to add it to the list
591 peak = std::make_unique<Peak>(m_inst, det.getID(), wl);
592 if (!peak->getDetector()) {
593 return;
594 }
595 } else if (useExtendedDetectorSpace) {
596 // use extended detector space to try and guess peak position
597 const auto returnedComponent = m_inst->getComponentByName("extended-detector-space");
598 // Check that the component is valid
599 const auto component = std::dynamic_pointer_cast<const ObjComponent>(returnedComponent);
600 if (!component)
601 throw std::runtime_error("PredictPeaks: user requested use of a extended "
602 "detector space to predict peaks but there is no"
603 "definition in the IDF");
604
605 // find where this Q vector should intersect with "extended" space
606 Geometry::Track track(detInfo.samplePosition(), detectorDir);
607 if (!component->interceptSurface(track))
608 return;
609
610 // The exit point is the vector to the place that we hit a detector
611 const auto magnitude = track.back().exitPoint.norm();
612 peak = std::make_unique<Peak>(m_inst, q, std::optional<double>(magnitude));
613 }
614
615 if (m_edge > 0 && edgePixel(m_inst, peak->getBankName(), peak->getCol(), peak->getRow(), m_edge))
616 return;
617
618 // Only add peaks that hit the detector
619 peak->setGoniometerMatrix(goniometerMatrix);
620 // Save the run number found before.
621 peak->setRunNumber(m_runNumber);
622 peak->setHKL(hkl * m_qConventionFactor);
623 peak->setIntHKL(hkl * m_qConventionFactor);
624
625 if (m_sfCalculator) {
626 peak->setIntensity(m_sfCalculator->getFSquared(hkl));
627 }
628
629 // Add it to the workspace
630 m_pw->addPeak(*peak);
631}
632
644 // The q-vector direction of the peak is = goniometer * ub * hkl_vector
645 // This is in inelastic convention: momentum transfer of the LATTICE!
646 // Also, q does have a 2pi factor = it is equal to 2pi/wavelength.
647 const auto q = UB * hkl * (2.0 * M_PI * m_qConventionFactor);
648 auto peak = std::make_unique<LeanElasticPeak>(q);
649
650 // Save the run number found before.
651 peak->setRunNumber(m_runNumber);
652 peak->setHKL(hkl * m_qConventionFactor);
653 peak->setIntHKL(hkl * m_qConventionFactor);
654
655 if (m_sfCalculator) {
656 peak->setIntensity(m_sfCalculator->getFSquared(hkl));
657 }
658
659 // Add it to the workspace
660 m_pw->addPeak(*peak);
661}
662
668std::tuple<V3D, double> PredictPeaks::getPeakParametersFromQ(const V3D &q) const {
669 double norm_q = q.norm();
670 // Default for ki-kf has -q
671 const double qBeam = q.scalar_prod(m_refBeamDir) * m_qConventionFactor;
672 double one_over_wl = (norm_q * norm_q) / (2.0 * qBeam);
673 double wl = (2.0 * M_PI) / one_over_wl;
674 // Default for ki-kf has -q
675 V3D detectorDir = q * -m_qConventionFactor;
676 detectorDir[m_refFrame->pointingAlongBeam()] = one_over_wl - qBeam;
677 detectorDir.normalize();
678 return std::make_tuple(detectorDir, wl);
679}
680
684 m_refFrame = m_inst->getReferenceFrame();
685 m_refBeamDir = m_refFrame->vecPointingAlongBeam();
686}
687
688} // namespace Mantid::Crystal
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
const std::vector< double > * lambda
#define fabs(x)
Definition Matrix.cpp:22
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
Helper class for reporting progress from algorithms.
Definition Progress.h:25
This class stores information about the sample used in particular run.
Definition Sample.h:33
const Geometry::OrientedLattice & getOrientedLattice() const
Get a reference to the sample's OrientedLattice.
Definition Sample.cpp:153
bool hasCrystalStructure() const
Returns true if the sample actually holds a CrystalStructure.
Definition Sample.cpp:194
const Geometry::CrystalStructure & getCrystalStructure() const
Definition Sample.cpp:180
A property class for workspaces.
void calculateQAndAddToOutputLeanElastic(const Kernel::V3D &hkl, const Kernel::DblMatrix &UB)
Calculates Q from HKL and adds a peak to the output workspace.
void setInstrumentFromInputWorkspace(const API::ExperimentInfo_sptr &inWS)
Tries to set the internally stored instrument from an ExperimentInfo-object.
int m_runNumber
Run number of input workspace.
void checkBeamDirection() const
Checks that the beam direction is +Z, throws exception otherwise.
int m_edge
Number of edge pixels with no peaks.
void setRunNumberFromInputWorkspace(const API::ExperimentInfo_sptr &inWS)
Sets the run number from the supplied ExperimentInfo or throws an exception.
void exec() override
Run the algorithm.
std::tuple< Kernel::V3D, double > getPeakParametersFromQ(const Kernel::V3D &q) const
Get the predicted detector direction from Q.
void logNumberOfPeaksFound(size_t allowedPeakCount) const
Log the number of peaks found to fall on and off detectors.
std::shared_ptr< const Geometry::ReferenceFrame > m_refFrame
Reference frame for the instrument.
void fillPossibleHKLsUsingPeaksWorkspace(const API::IPeaksWorkspace_sptr &peaksWorkspace, std::vector< Kernel::V3D > &possibleHKLs) const
Fills possibleHKLs with all HKLs from the supplied PeaksWorkspace.
Geometry::Instrument_const_sptr m_inst
Instrument reference.
void calculateQAndAddToOutput(const Kernel::V3D &hkl, const Kernel::DblMatrix &orientedUB, const Kernel::DblMatrix &goniometerMatrix)
Calculates Q from HKL and adds a peak to the output workspace.
std::unique_ptr< API::DetectorSearcher > m_detectorCacheSearch
Detector search cache for fast look-up of detectors.
std::vector< Mantid::Geometry::ReflectionCondition_sptr > m_refConds
Reflection conditions possible.
void setReferenceFrameAndBeamDirection()
Cache the reference frame and beam direction from the instrument.
Mantid::API::IPeaksWorkspace_sptr m_pw
Output peaks workspace.
Geometry::StructureFactorCalculator_sptr m_sfCalculator
void fillPossibleHKLsUsingGenerator(const Geometry::OrientedLattice &orientedLattice, std::vector< Kernel::V3D > &possibleHKLs) const
Fills possibleHKLs with all HKLs that are allowed within d- and lambda-limits.
void init() override
Initialise the properties.
void setStructureFactorCalculatorFromSample(const API::Sample &sample)
Assigns a StructureFactorCalculator if available in sample.
Kernel::V3D m_refBeamDir
Direction of the beam for this instrument.
Three components are required to describe a crystal structure:
void setCell(const UnitCell &cell)
Assigns a new unit cell.
Class to represent a particular goniometer setting, which is described by the rotation matrix.
Definition Goniometer.h:55
std::vector< double > getEulerAngles(const std::string &convention="YZX")
Return Euler angles acording to a convention.
const Kernel::DblMatrix & getR() const
Return global rotation matrix.
void calcFromQSampleAndWavelength(const Mantid::Kernel::V3D &Q, double wavelength, bool flip_x=false, bool inner=false)
Calculate goniometer for rotation around y-asix for constant wavelength from Q Sample.
bool isAllowed(const Kernel::V3D &hkl) const noexcept override
Returns true if lambda of the reflection is within the limits.
const const_iterator & end() const
Returns an iterator which "points at" one element past the end.
const const_iterator & begin() const
Returns an iterator to the beginning of the sequence.
size_t size() const
Returns the number of HKLs to be generated.
Structure describing a single-crystal peak.
Definition IPeak.h:26
virtual Mantid::Kernel::Matrix< double > getGoniometerMatrix() const =0
virtual Mantid::Kernel::V3D getHKL() const =0
Class to implement UB matrix.
const Kernel::DblMatrix & getUB() const
Get the UB matrix.
Defines a track as a start point and a direction.
Definition Track.h:165
LType::reference back()
Returns a reference to the last link.
Definition Track.h:213
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings > settings)
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
void setNotifyStep(double notifyStepPct)
Override the frequency at which notifications are sent out.
The concrete, templated class for properties.
Class for 3D vectors.
Definition V3D.h:34
constexpr double scalar_prod(const V3D &v) const noexcept
Calculates the cross product.
Definition V3D.h:280
constexpr double X() const noexcept
Get x.
Definition V3D.h:238
double normalize()
Make a normalized vector (return norm value)
Definition V3D.cpp:129
constexpr double Y() const noexcept
Get y.
Definition V3D.h:239
double norm() const noexcept
Definition V3D.h:269
constexpr double norm2() const noexcept
Vector length squared.
Definition V3D.h:271
constexpr double Z() const noexcept
Get z.
Definition V3D.h:240
void round() noexcept
Round each component to the nearest integer.
Definition V3D.cpp:139
std::shared_ptr< IPeaksWorkspace > IPeaksWorkspace_sptr
shared pointer to Mantid::API::IPeaksWorkspace
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< ExperimentInfo > ExperimentInfo_sptr
Shared pointer to ExperimentInfo.
std::shared_ptr< MultipleExperimentInfos > MultipleExperimentInfos_sptr
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
double qConventionFactor()
convenience overload to pull the convention from the config service
std::shared_ptr< PeaksWorkspace > PeaksWorkspace_sptr
Typedef for a shared pointer to a peaks workspace.
std::shared_ptr< ReflectionCondition > ReflectionCondition_sptr
Shared pointer to a ReflectionCondition.
MANTID_GEOMETRY_DLL bool edgePixel(const Geometry::Instrument_const_sptr &inst, const std::string &bankName, int col, int row, int Edge)
Function to find peaks near detector edge.
Definition EdgePixel.cpp:22
MANTID_GEOMETRY_DLL const ReflectionConditions & getAllReflectionConditions()
MANTID_KERNEL_DLL bool withinAbsoluteDifference(T const x, T const y, S const tolerance)
Test whether x, y are within absolute tolerance tol.
Mantid::Kernel::Matrix< double > DblMatrix
Definition Matrix.h:206
Peak indexing algorithm, which works by assigning multiple possible HKL values to each peak and then ...
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54