Mantid
Loading...
Searching...
No Matches
FindGoniometerAngles.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2024 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
9
11#include "MantidAPI/Run.h"
12#include "MantidAPI/Sample.h"
17
18namespace Mantid {
19namespace Crystal {
28
29// Register the algorithm into the AlgorithmFactory
30DECLARE_ALGORITHM(FindGoniometerAngles)
31
32namespace {
33
34DblMatrix createRotationMatrix(const double phi, const double chi, const double omega) {
35 Goniometer g;
36 g.pushAxis("omega", 0., 1., 0., omega);
37 g.pushAxis("chi", 0., 0., 1., chi);
38 g.pushAxis("phi", 0., 1., 0., phi);
39 return g.getR();
40}
41
42std::pair<int, double> NumIndexed(const DblMatrix &UB, const IPeaksWorkspace_sptr peakWS, const double tolerance,
43 const double phi, const double chi, const double omega) {
44 DblMatrix UBinv = UB;
45 UBinv.Invert();
46
47 auto Rinv = createRotationMatrix(phi, chi, omega);
48 Rinv.Invert();
49
50 int n_indexed{0};
51 double sum_sq_err{0};
52
53 for (int i{0}; i < peakWS->getNumberPeaks(); i++) {
54 const auto &peak = peakWS->getPeak(i);
55 const auto q_lab = peak.getQLabFrame();
56 const auto q_sample = Rinv * q_lab;
57 const auto hkl = UBinv * q_sample / (2 * M_PI);
58
60 n_indexed++;
61 V3D HKL = hkl;
62 HKL.round();
63 V3D q_error = UB * HKL;
64 q_error *= 2 * M_PI;
65 q_error -= q_sample;
66 sum_sq_err += q_error.norm2();
67 }
68 }
69
70 return std::make_pair(n_indexed, sum_sq_err);
71}
72} // namespace
73
74//----------------------------------------------------------------------------------------------
75
77const std::string FindGoniometerAngles::name() const { return "FindGoniometerAngles"; }
78
80int FindGoniometerAngles::version() const { return 1; }
81
83const std::string FindGoniometerAngles::category() const { return "Crystal\\Corrections"; }
84
86const std::string FindGoniometerAngles::summary() const {
87 return "Do a brute force search for the goniometer rotation angles that maximize the number of peaks indexed by the "
88 "specified UB.";
89}
90
91//----------------------------------------------------------------------------------------------
95 declareProperty(std::make_unique<WorkspaceProperty<IPeaksWorkspace>>("PeaksWorkspace", "", Direction::Input),
96 "Workspace of Peaks with UB loaded");
98 "MaxAngle", 5.0,
99 "The maximum change in angle to try for any of the goniometer angles, phi, chi and omega, in degrees.",
101 declareProperty("Tolerance", 0.15, "The tolerance on Miller indices for a peak to be considered indexed",
103 declareProperty("Apply", false, "Update goniometer in peaks workspace");
104 declareProperty("Phi", 0.0, "Phi found", Direction::Output);
105 declareProperty("Chi", 0.0, "Chi found", Direction::Output);
106 declareProperty("Omega", 0.0, "Omega found", Direction::Output);
107}
108
109//----------------------------------------------------------------------------------------------
113
114 const double tolerance = getProperty("Tolerance");
115 const double max_angle = getProperty("MaxAngle");
116 IPeaksWorkspace_sptr peakWS = getProperty("PeaksWorkspace");
117
118 const auto UB = peakWS->sample().getOrientedLattice().getUB();
119
120 auto g = peakWS->run().getGoniometer();
121 const std::vector<double> YZY = g.getEulerAngles("YZY");
122
123 double omega = YZY[0];
124 double chi = YZY[1];
125 double phi = YZY[2];
126
127 auto results = NumIndexed(UB, peakWS, tolerance, phi, chi, omega);
128 g_log.information() << "Starting Max Indexed = " << results.first << " Err = " << results.second
129 << " Phi: " << phi << " Chi: " << chi << " Omega: " << omega << "\n";
130
131 const int N_TRIES = 5;
132
133 double phi_offset{phi};
134 double chi_offset{chi};
135 double omega_offset{omega};
136 double best_phi{0};
137 double best_chi{0};
138 double best_omega{0};
139
140 auto progress = std::make_unique<API::Progress>(this, 0.0, 1.0, N_TRIES);
141
142 for (int range{1}; range <= N_TRIES; range++) {
143 double max_quality{0};
144 double max_error{0};
145 int max_indexed{0};
146
147 double step = max_angle / range;
148
149 while (step > sqrt(1e-5)) {
150 for (int i{-range}; i <= range; i++)
151 for (int j{-range}; j <= range; j++)
152 for (int k{-range}; k <= range; k++) {
153 phi = phi_offset + i * step;
154 chi = chi_offset + j * step;
155 omega = omega_offset + k * step;
156 results = NumIndexed(UB, peakWS, tolerance, phi, chi, omega);
157 const double quality = results.first - 0.1 * results.second / results.first;
158
159 if (quality > max_quality) {
160 max_quality = quality;
161 max_indexed = results.first;
162 max_error = results.second;
163
164 best_phi = phi;
165 best_chi = chi;
166 best_omega = omega;
167 }
168 }
169
170 phi_offset = best_phi;
171 chi_offset = best_chi;
172 omega_offset = best_omega;
173
174 step *= M_SQRT1_2;
175 }
176
177 g_log.information() << "Range Factor = " << range << " "
178 << "Max Indexed = " << max_indexed << " Err = " << max_error << " Phi: " << best_phi
179 << " Chi: " << best_chi << " Omega: " << best_omega << "\n";
180 progress->report();
181 }
182
183 setProperty("Phi", best_phi);
184 setProperty("Chi", best_chi);
185 setProperty("Omega", best_omega);
186
187 if (getProperty("Apply")) {
188 const auto R = createRotationMatrix(best_phi, best_chi, best_omega);
189 // set the goniometer on the workspace
190 peakWS->mutableRun().setGoniometer(R, false);
191
192 // set the goniometer on the peaks and reset the q_lab so that the q_sample is recalculated
193 for (int i{0}; i < peakWS->getNumberPeaks(); i++) {
194 auto &peak = peakWS->getPeak(i);
195 const auto q_lab = peak.getQLabFrame();
196 peak.setGoniometerMatrix(R);
197 peak.setQLabFrame(q_lab, std::nullopt);
198 }
199 }
200}
201
202} // namespace Crystal
203} // namespace Mantid
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double tolerance
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Kernel::Logger & g_log
Definition Algorithm.h:422
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
Interface to the class Mantid::DataObjects::PeaksWorkspace.
A property class for workspaces.
const std::string category() const override
Algorithm's category for identification.
int version() const override
Algorithm's version for identification.
void init() override
Initialize the algorithm's properties.
void exec() override
Execute the algorithm.
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
const std::string name() const override
Algorithms name for identification.
Class to represent a particular goniometer setting, which is described by the rotation matrix.
Definition Goniometer.h:55
This class contains static utility methods for indexing peaks and finding the UB matrix.
static bool ValidIndex(const Kernel::V3D &hkl, double tolerance)
Check is hkl is within tolerance of integer (h,k,l) non-zero values.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
T Invert()
LU inversion routine.
Definition Matrix.cpp:924
Class for 3D vectors.
Definition V3D.h:34
std::shared_ptr< IPeaksWorkspace > IPeaksWorkspace_sptr
shared pointer to Mantid::API::IPeaksWorkspace
Mantid::Kernel::Matrix< double > DblMatrix
Definition Matrix.h:206
static constexpr double g
Standard acceleration due to gravity.
Helper class which provides the Collimation Length for SANS instruments.
Describes the direction (within an algorithm) of a Property.
Definition Property.h:50
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54