Mantid
Loading...
Searching...
No Matches
PredictSatellitePeaks.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2020 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 +
7/*
8 * PredictSatellitePeaks.cpp
9 *
10 * Created on: July 15, 2018
11 * Author: Vickie Lynch
12 */
15#include "MantidAPI/Run.h"
16#include "MantidAPI/Sample.h"
25#include <boost/math/special_functions/round.hpp>
26
27namespace Mantid {
28using namespace Mantid::DataObjects;
29using namespace Mantid::API;
30using namespace std;
31using namespace Mantid::Geometry;
32using namespace Mantid::Kernel;
33
34namespace Crystal {
35
36// handy shortcuts
37
38DECLARE_ALGORITHM(PredictSatellitePeaks)
39
40
44
47 auto latticeValidator = std::make_shared<OrientedLatticeValidator>();
48 declareProperty(std::make_unique<WorkspaceProperty<IPeaksWorkspace>>("Peaks", "", Direction::Input, latticeValidator),
49 "Workspace of Peaks with orientation matrix that indexed the peaks and "
50 "instrument loaded");
51
52 declareProperty(std::make_unique<WorkspaceProperty<IPeaksWorkspace>>("SatellitePeaks", "", Direction::Output),
53 "Workspace of Peaks with peaks with fractional h,k, and/or l values");
54
56
57 declareProperty("GetModVectorsFromUB", false, "If false Modulation Vectors will be read from input");
58
59 declareProperty("IncludeIntegerHKL", true, "If false order 0 peaks are not included in workspace (integer HKL)");
60
61 declareProperty("IncludeAllPeaksInRange", false,
62 "If false only offsets from "
63 "peaks from Peaks workspace "
64 "in input are used");
65
66 declareProperty(std::make_unique<PropertyWithValue<double>>("WavelengthMin", 0.1, Direction::Input),
67 "Minimum wavelength limit at which to start looking for "
68 "single-crystal peaks.");
69 declareProperty(std::make_unique<PropertyWithValue<double>>("WavelengthMax", 100.0, Direction::Input),
70 "Maximum wavelength limit at which to start looking for "
71 "single-crystal peaks.");
72 declareProperty(std::make_unique<PropertyWithValue<double>>("MinDSpacing", 0.1, Direction::Input),
73 "Minimum d-spacing of peaks to consider. Default = 0.1");
74 declareProperty(std::make_unique<PropertyWithValue<double>>("MaxDSpacing", 100.0, Direction::Input),
75 "Maximum d-spacing of peaks to consider");
76
77 setPropertySettings("WavelengthMin", std::make_unique<Kernel::EnabledWhenProperty>(string("IncludeAllPeaksInRange"),
79
80 setPropertySettings("WavelengthMax", std::make_unique<Kernel::EnabledWhenProperty>(string("IncludeAllPeaksInRange"),
82 setPropertySettings("MinDSpacing", std::make_unique<Kernel::EnabledWhenProperty>(string("IncludeAllPeaksInRange"),
84
85 setPropertySettings("MaxDSpacing", std::make_unique<Kernel::EnabledWhenProperty>(string("IncludeAllPeaksInRange"),
87}
88
91 bool includePeaksInRange = getProperty("IncludeAllPeaksInRange");
92 Peaks = getProperty("Peaks");
93
94 if (!Peaks)
95 throw std::invalid_argument("Input workspace is not a IPeaksWorkspace. Type=" + Peaks->id());
96 if (!includePeaksInRange) {
97 exec_peaks();
98 return;
99 }
100
106 bool includeOrderZero = getProperty("IncludeIntegerHKL");
107 // boolean for only including order zero once
108 bool notOrderZero = false;
109
110 API::Sample sample = Peaks->mutableSample();
111 auto lattice = std::make_unique<OrientedLattice>(sample.getOrientedLattice());
112
113 bool fromUB = getProperty("GetModVectorsFromUB");
114 if (fromUB) {
115 offsets1 = lattice->getModVec(0);
116 offsets2 = lattice->getModVec(1);
117 offsets3 = lattice->getModVec(2);
118 if (maxOrder == 0)
119 maxOrder = lattice->getMaxOrder();
120 crossTerms = lattice->getCrossTerm();
121 } else {
122 lattice->setModVec1(offsets1);
123 lattice->setModVec2(offsets2);
124 lattice->setModVec3(offsets3);
125 lattice->setMaxOrder(maxOrder);
126 lattice->setCrossTerm(crossTerms);
127 }
128
129 outPeaks = std::dynamic_pointer_cast<IPeaksWorkspace>(WorkspaceFactory::Instance().createPeaks(Peaks->id()));
130 outPeaks->copyExperimentInfoFrom(Peaks.get());
131 outPeaks->mutableSample().setOrientedLattice(std::move(lattice));
132
133 Kernel::Matrix<double> goniometer;
134 goniometer.identityMatrix();
135
136 const double lambdaMin = getProperty("WavelengthMin");
137 const double lambdaMax = getProperty("WavelengthMax");
138
139 std::vector<V3D> possibleHKLs;
140 const double dMin = getProperty("MinDSpacing");
141 const double dMax = getProperty("MaxDSpacing");
142 Geometry::HKLGenerator gen(outPeaks->sample().getOrientedLattice(), dMin);
143 auto dSpacingFilter = std::make_shared<HKLFilterDRange>(outPeaks->sample().getOrientedLattice(), dMin, dMax);
144
145 V3D hkl_begin = *(gen.begin());
146 g_log.information() << "HKL range for d_min of " << dMin << " to d_max of " << dMax << " is from " << hkl_begin
147 << " to " << hkl_begin * -1.0 << ", a total of " << gen.size() << " possible HKL's\n";
148 if (gen.size() > MAX_NUMBER_HKLS)
149 throw std::invalid_argument("More than 10 billion HKLs to search. Is "
150 "your d_min value too small?");
151
152 possibleHKLs.clear();
153 possibleHKLs.reserve(gen.size());
154 std::remove_copy_if(gen.begin(), gen.end(), std::back_inserter(possibleHKLs), (~dSpacingFilter)->fn());
155
156 size_t N = possibleHKLs.size();
157 N = max<size_t>(100, N);
158 const auto &UB = outPeaks->sample().getOrientedLattice().getUB();
159 goniometer = Peaks->run().getGoniometerMatrix();
160 int runNumber = Peaks->getRunNumber();
161 Progress prog(this, 0.0, 1.0, N);
162 vector<vector<int>> alreadyDonePeaks;
163 auto orientedUB = goniometer * UB;
164 HKLFilterWavelength lambdaFilter(orientedUB, lambdaMin, lambdaMax);
165 outPeaks->mutableRun().addProperty<std::vector<double>>("Offset1", offsets1, true);
166 outPeaks->mutableRun().addProperty<std::vector<double>>("Offset2", offsets2, true);
167 outPeaks->mutableRun().addProperty<std::vector<double>>("Offset3", offsets3, true);
168 for (auto &hkl : possibleHKLs) {
169 if (crossTerms) {
170 predictOffsetsWithCrossTerms(offsets1, offsets2, offsets3, maxOrder, runNumber, goniometer, hkl, lambdaFilter,
171 includePeaksInRange, includeOrderZero, alreadyDonePeaks);
172 } else {
173 predictOffsets(0, offsets1, maxOrder, runNumber, goniometer, hkl, lambdaFilter, includePeaksInRange,
174 includeOrderZero, alreadyDonePeaks);
175 predictOffsets(1, offsets2, maxOrder, runNumber, goniometer, hkl, lambdaFilter, includePeaksInRange, notOrderZero,
176 alreadyDonePeaks);
177 predictOffsets(2, offsets3, maxOrder, runNumber, goniometer, hkl, lambdaFilter, includePeaksInRange, notOrderZero,
178 alreadyDonePeaks);
179 }
180 }
181 // Sort peaks by run number so that peaks with equal goniometer matrices are
182 // adjacent
183 std::vector<std::pair<std::string, bool>> criteria;
184 criteria.emplace_back("RunNumber", true);
185 auto isPeaksWorkspace = std::dynamic_pointer_cast<PeaksWorkspace>(outPeaks);
186 if (isPeaksWorkspace)
187 criteria.emplace_back("BankName", true);
188 criteria.emplace_back("h", true);
189 criteria.emplace_back("k", true);
190 criteria.emplace_back("l", true);
191 outPeaks->sort(criteria);
192
193 for (int i = 0; i < static_cast<int>(outPeaks->getNumberPeaks()); ++i) {
194 outPeaks->getPeak(i).setPeakNumber(i);
195 }
196 setProperty("SatellitePeaks", outPeaks);
197}
198
200
206
207 API::Sample sample = Peaks->mutableSample();
208
209 auto lattice = std::make_unique<OrientedLattice>(sample.getOrientedLattice());
210
211 bool fromUB = getProperty("GetModVectorsFromUB");
212 if (fromUB) {
213 offsets1 = lattice->getModVec(0);
214 offsets2 = lattice->getModVec(1);
215 offsets3 = lattice->getModVec(2);
216 if (maxOrder == 0)
217 maxOrder = lattice->getMaxOrder();
218 crossTerms = lattice->getCrossTerm();
219 } else {
220 lattice->setModVec1(offsets1);
221 lattice->setModVec2(offsets2);
222 lattice->setModVec3(offsets3);
223 lattice->setMaxOrder(maxOrder);
224 lattice->setCrossTerm(crossTerms);
225 }
226
227 bool includePeaksInRange = false;
228 bool includeOrderZero = getProperty("IncludeIntegerHKL");
229 // boolean for only including order zero once
230 bool notOrderZero = false;
231
232 if (Peaks->getNumberPeaks() <= 0) {
233 g_log.error() << "There are No peaks in the input PeaksWorkspace\n";
234 return;
235 }
236
237 outPeaks = std::dynamic_pointer_cast<IPeaksWorkspace>(WorkspaceFactory::Instance().createPeaks(Peaks->id()));
238 outPeaks->copyExperimentInfoFrom(Peaks.get());
239 outPeaks->mutableSample().setOrientedLattice(std::move(lattice));
240
241 vector<vector<int>> alreadyDonePeaks;
242 HKLFilterWavelength lambdaFilter(DblMatrix(3, 3, true), 0.1, 100.);
243 outPeaks->mutableRun().addProperty<std::vector<double>>("Offset1", offsets1, true);
244 outPeaks->mutableRun().addProperty<std::vector<double>>("Offset2", offsets2, true);
245 outPeaks->mutableRun().addProperty<std::vector<double>>("Offset3", offsets3, true);
246
247 for (int i = 0; i < static_cast<int>(Peaks->getNumberPeaks()); ++i) {
248
249 const Kernel::Matrix<double> peakGoniometerMatrix = Peaks->getPeak(i).getGoniometerMatrix();
250
251 int runNumber = Peaks->getPeak(i).getRunNumber();
252
253 V3D hkl = Peaks->getPeak(i).getHKL();
254
255 if (crossTerms) {
256 predictOffsetsWithCrossTerms(offsets1, offsets2, offsets3, maxOrder, runNumber, peakGoniometerMatrix, hkl,
257 lambdaFilter, includePeaksInRange, includeOrderZero, alreadyDonePeaks);
258 } else {
259 predictOffsets(0, offsets1, maxOrder, runNumber, peakGoniometerMatrix, hkl, lambdaFilter, includePeaksInRange,
260 includeOrderZero, alreadyDonePeaks);
261
262 predictOffsets(1, offsets2, maxOrder, runNumber, peakGoniometerMatrix, hkl, lambdaFilter, includePeaksInRange,
263 notOrderZero, alreadyDonePeaks);
264
265 predictOffsets(2, offsets3, maxOrder, runNumber, peakGoniometerMatrix, hkl, lambdaFilter, includePeaksInRange,
266 notOrderZero, alreadyDonePeaks);
267 }
268 }
269 // Sort peaks by run number so that peaks with equal goniometer matrices are
270 // adjacent
271 std::vector<std::pair<std::string, bool>> criteria;
272 criteria.emplace_back("RunNumber", true);
273
274 workspace_type_enum const workspace_type = determineWorkspaceType(Peaks);
275 if (workspace_type == workspace_type_enum::regular_peaks) {
276 criteria.emplace_back("BankName", true);
277 }
278
279 criteria.emplace_back("h", true);
280 criteria.emplace_back("k", true);
281 criteria.emplace_back("l", true);
282 outPeaks->sort(criteria);
283
284 for (int i = 0; i < static_cast<int>(outPeaks->getNumberPeaks()); ++i) {
285 outPeaks->getPeak(i).setPeakNumber(i);
286 }
287
288 setProperty("SatellitePeaks", outPeaks);
289}
290
293 if (std::dynamic_pointer_cast<PeaksWorkspace>(iPeaksWorkspace) != nullptr) {
295 }
296
297 else if (std::dynamic_pointer_cast<LeanElasticPeaksWorkspace>(iPeaksWorkspace) != nullptr) {
299 }
300
301 else {
303 }
304}
305
306void PredictSatellitePeaks::predictOffsets(const int indexModulatedVector, const V3D &offsets, const int maxOrder,
307 const int runNumber, const Kernel::Matrix<double> &goniometer,
308 const V3D &hkl, const HKLFilterWavelength &lambdaFilter,
309 const bool includePeaksInRange, const bool includeOrderZero,
310 vector<vector<int>> &alreadyDonePeaks) {
311 if (offsets == V3D(0, 0, 0) && !includeOrderZero)
312 return;
313 for (int order = -maxOrder; order <= maxOrder; order++) {
314 if (order == 0 && !includeOrderZero)
315 continue; // exclude order 0
316 V3D satelliteHKL(hkl);
317
318 satelliteHKL += offsets * order;
319 if (!lambdaFilter.isAllowed(satelliteHKL) && includePeaksInRange)
320 continue;
321
322 std::shared_ptr<IPeak> satellite_iPeak = createPeakForOutputWorkspace(goniometer, satelliteHKL);
323
324 V3D mnp;
325 mnp[indexModulatedVector] = order;
326
327 addPeakToOutputWorkspace(satellite_iPeak, goniometer, hkl, satelliteHKL, runNumber, alreadyDonePeaks, mnp);
328 }
329}
330
331void PredictSatellitePeaks::predictOffsetsWithCrossTerms(V3D offsets1, V3D offsets2, V3D offsets3, const int maxOrder,
332 const int runNumber,
333 Kernel::Matrix<double> const &peakGoniometerMatrix, V3D &hkl,
334 const HKLFilterWavelength &lambdaFilter,
335 const bool includePeaksInRange, const bool includeOrderZero,
336 vector<vector<int>> &alreadyDonePeaks) {
337 if (offsets1 == V3D(0, 0, 0) && offsets2 == V3D(0, 0, 0) && offsets3 == V3D(0, 0, 0) && !includeOrderZero)
338 return;
339 DblMatrix offsetsMat(3, 3);
340 offsetsMat.setColumn(0, offsets1);
341 offsetsMat.setColumn(1, offsets2);
342 offsetsMat.setColumn(2, offsets3);
343 int maxOrder1 = maxOrder;
344 if (offsets1 == V3D(0, 0, 0))
345 maxOrder1 = 0;
346 int maxOrder2 = maxOrder;
347 if (offsets2 == V3D(0, 0, 0))
348 maxOrder2 = 0;
349 int maxOrder3 = maxOrder;
350 if (offsets3 == V3D(0, 0, 0))
351 maxOrder3 = 0;
352 for (int m = -maxOrder1; m <= maxOrder1; m++)
353 for (int n = -maxOrder2; n <= maxOrder2; n++)
354 for (int p = -maxOrder3; p <= maxOrder3; p++) {
355 if (m == 0 && n == 0 && p == 0 && !includeOrderZero)
356 continue; // exclude 0,0,0
357 V3D satelliteHKL(hkl);
358 V3D mnp = V3D(m, n, p);
359 satelliteHKL -= offsetsMat * mnp;
360 if (!lambdaFilter.isAllowed(satelliteHKL) && includePeaksInRange)
361 continue;
362
363 std::shared_ptr<IPeak> satellite_iPeak = createPeakForOutputWorkspace(peakGoniometerMatrix, satelliteHKL);
364
365 addPeakToOutputWorkspace(satellite_iPeak, peakGoniometerMatrix, hkl, satelliteHKL, runNumber, alreadyDonePeaks,
366 mnp);
367 }
368}
369
370std::shared_ptr<Geometry::IPeak>
372 const Kernel::V3D &satelliteHKL) {
374
375 const Kernel::DblMatrix &UB = Peaks->sample().getOrientedLattice().getUB();
376 if (workspace_type == workspace_type_enum::regular_peaks) {
377 Kernel::V3D const Qs = peakGoniometerMatrix * UB * satelliteHKL * 2.0 * M_PI * m_qConventionFactor;
378
379 // Check if Q is non-physical
380 if (Qs.Z() * m_qConventionFactor <= 0)
381 return nullptr;
382
383 std::shared_ptr<IPeak> satellite_iPeak = Peaks->createPeak(Qs, 1);
384
385 return satellite_iPeak;
386 }
387
388 else if (workspace_type == workspace_type_enum::lean_elastic_peaks) {
389 Kernel::V3D const Qs = UB * satelliteHKL * 2.0 * M_PI * m_qConventionFactor;
390
391 std::shared_ptr<IPeak> satellite_iPeak = Peaks->createPeakQSample(Qs);
392
393 return satellite_iPeak;
394 }
395
396 else
397 return nullptr;
398}
399
400void PredictSatellitePeaks::addPeakToOutputWorkspace(const std::shared_ptr<IPeak> &satellite_iPeak,
401 const Kernel::Matrix<double> &peak_goniometer_matrix,
402 const Kernel::V3D &hkl, const Kernel::V3D &satelliteHKL,
403 const int runNumber,
404 std::vector<std::vector<int>> &alreadyDonePeaks,
405 const Kernel::V3D &mnp) {
406 if (satellite_iPeak == nullptr)
407 return;
408
409 const workspace_type_enum workspace_type = determineWorkspaceType(Peaks);
410
411 if (workspace_type == workspace_type_enum::regular_peaks) {
412 const Geometry::InstrumentRayTracer tracer(Peaks->getInstrument());
413
414 const std::shared_ptr<Peak> peak = std::dynamic_pointer_cast<Peak>(satellite_iPeak);
415
416 if (!peak->findDetector(tracer))
417 return;
418 }
419
420 const std::vector<int> savPk{runNumber, boost::math::iround(1000.0 * satelliteHKL[0]),
421 boost::math::iround(1000.0 * satelliteHKL[1]),
422 boost::math::iround(1000.0 * satelliteHKL[2])};
423
424 const bool foundPeak = binary_search(alreadyDonePeaks.begin(), alreadyDonePeaks.end(), savPk);
425
426 if (!foundPeak) {
427 alreadyDonePeaks.emplace_back(savPk);
428 }
429
430 else
431 return;
432
433 satellite_iPeak->setGoniometerMatrix(peak_goniometer_matrix);
434 satellite_iPeak->setHKL(satelliteHKL * m_qConventionFactor);
435 satellite_iPeak->setIntHKL(hkl * m_qConventionFactor);
436 satellite_iPeak->setRunNumber(runNumber);
437 satellite_iPeak->setIntMNP(mnp * m_qConventionFactor);
438
439 outPeaks->addPeak(*satellite_iPeak);
440
441 return;
442}
443
445 vector<double> offsets = getProperty(label);
446 if (offsets.empty()) {
447 offsets.emplace_back(0.0);
448 offsets.emplace_back(0.0);
449 offsets.emplace_back(0.0);
450 }
451 V3D offsets1 = V3D(offsets[0], offsets[1], offsets[2]);
452 return offsets1;
453}
454
455} // namespace Crystal
456} // namespace Mantid
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
const bool crossTerms
Definition: IndexPeaks.cpp:57
const int maxOrder
Definition: IndexPeaks.cpp:55
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Definition: Algorithm.cpp:1913
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
Kernel::Logger & g_log
Definition: Algorithm.h:451
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:154
A property class for workspaces.
PredictSatellitePeaks : Algorithm to create a PeaksWorkspace with peaks corresponding to fractional h...
void predictOffsetsWithCrossTerms(Kernel::V3D offsets1, Kernel::V3D offsets2, Kernel::V3D offsets3, const int maxOrder, const int RunNumber, Kernel::Matrix< double > const &peakGoniometerMatrix, Kernel::V3D &hkl, const Geometry::HKLFilterWavelength &lambdaFilter, const bool includePeaksInRange, const bool includeOrderZero, std::vector< std::vector< int > > &alreadyDonePeaks)
workspace_type_enum determineWorkspaceType(const API::IPeaksWorkspace_sptr &iPeaksWorkspace) const
void init() override
Initialise the properties.
std::shared_ptr< Geometry::IPeak > createPeakForOutputWorkspace(const Kernel::Matrix< double > &peakGoniometerMatrix, const Kernel::V3D &satelliteHKL)
void predictOffsets(const int indexModulatedVector, const Kernel::V3D &offsets, const int maxOrder, const int runNumber, const Kernel::Matrix< double > &goniometer, const Kernel::V3D &hkl, const Geometry::HKLFilterWavelength &lambdaFilter, const bool includePeaksInRange, const bool includeOrderZero, std::vector< std::vector< int > > &alreadyDonePeaks)
void addPeakToOutputWorkspace(const std::shared_ptr< Geometry::IPeak > &iPeak, const Kernel::Matrix< double > &peak_goniometer_matrix, const Kernel::V3D &hkl, const Kernel::V3D &satelliteHKL, const int runNumber, std::vector< std::vector< int > > &alreadyDonePeaks, const Kernel::V3D &mnp)
void exec() override
Run the algorithm.
Kernel::V3D getOffsetVector(const std::string &label)
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.
Definition: HKLGenerator.h:146
const const_iterator & begin() const
Returns an iterator to the beginning of the sequence.
Definition: HKLGenerator.h:143
size_t size() const
Returns the number of HKLs to be generated.
Definition: HKLGenerator.h:140
This class is responsible for tracking rays and accumulating a list of objects that are intersected a...
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void setPropertySettings(const std::string &name, std::unique_ptr< IPropertySettings > settings)
void error(const std::string &msg)
Logs at error level.
Definition: Logger.cpp:77
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
Numerical Matrix class.
Definition: Matrix.h:42
void identityMatrix()
Makes the matrix an idenity matrix.
Definition: Matrix.cpp:661
void setColumn(const size_t nCol, const std::vector< T > &newCol)
Definition: Matrix.cpp:675
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
Class for 3D vectors.
Definition: V3D.h:34
constexpr double Z() const noexcept
Get z.
Definition: V3D.h:234
std::shared_ptr< IPeaksWorkspace > IPeaksWorkspace_sptr
shared pointer to Mantid::API::IPeaksWorkspace
double qConventionFactor()
convenience overload to pull the convention from the config service
Mantid::Kernel::Matrix< double > DblMatrix
Definition: Matrix.h:206
Helper class which provides the Collimation Length for SANS instruments.
STL namespace.
static void appendTo(API::IAlgorithm *alg)
Append the common set of properties that relate to modulation vectors to the given algorithm.
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54