Mantid
Loading...
Searching...
No Matches
OptimizeCrystalPlacement.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 +
7/*
8 *
9 * OptimizeCrystalPlacement.cpp
10 *
11 * Created on: Jan 26, 2013
12 * Author: ruth
13 */
15
16#include "MantidAPI/Run.h"
17#include "MantidAPI/Sample.h"
29
30#include <cstdarg>
31#include <utility>
32
33using namespace Mantid::API;
34using namespace Mantid::DataObjects;
35using namespace Mantid::Kernel;
38using namespace Mantid::Geometry;
39
40namespace Mantid::Crystal {
41
42DECLARE_ALGORITHM(OptimizeCrystalPlacement)
43
45public:
46 OrEnabledWhenProperties(std::string prop1Name, ePropertyCriterion prop1Crit, std::string prop1Value,
47 std::string prop2Name, ePropertyCriterion prop2Crit, std::string prop2Value)
48 : IPropertySettings(), propName1(std::move(prop1Name)), propName2(std::move(prop2Name)), Criteria1(prop1Crit),
49 Criteria2(prop2Crit), value1(std::move(prop1Value)), value2(std::move(prop2Value))
50
51 {
52 Prop1 = std::make_unique<Kernel::EnabledWhenProperty>(propName1, Criteria1, value1);
53 Prop2 = std::make_unique<Kernel::EnabledWhenProperty>(propName2, Criteria2, value2);
54 }
55 ~OrEnabledWhenProperties() override // responsible for deleting all supplied
56 // EnabledWhenProperites
57 = default;
58
59 IPropertySettings *clone() const override {
60 return new OrEnabledWhenProperties(propName1, Criteria1, value1, propName2, Criteria2, value2);
61 }
62
63 bool isEnabled(const IPropertyManager *algo) const override {
64 return Prop1->isEnabled(algo) && Prop2->isEnabled(algo);
65 }
66
67private:
68 std::string propName1, propName2;
70 std::string value1, value2;
71 std::unique_ptr<Kernel::EnabledWhenProperty> Prop1, Prop2;
72};
73
75 declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>("PeaksWorkspace", "", Direction::Input),
76 "Workspace of Peaks with UB loaded");
77 declareProperty(std::make_unique<ArrayProperty<int>>(std::string("KeepGoniometerFixedfor"), Direction::Input),
78 "List of run Numbers for which the goniometer settings will "
79 "NOT be changed");
80
81 declareProperty(std::make_unique<WorkspaceProperty<PeaksWorkspace>>("ModifiedPeaksWorkspace", "", Direction::Output),
82 "Output Workspace of Peaks with optimized sample Orientations");
83
85 std::make_unique<WorkspaceProperty<ITableWorkspace>>("FitInfoTable", "FitInfoTable", Direction::Output),
86 "Workspace of Results");
87
88 declareProperty("AdjustSampleOffsets", false,
89 "If true sample offsets will be adjusted to give better "
90 "fits, otherwise they will be fixed as zero(def=true)");
91
92 declareProperty("OptimizeGoniometerTilt", false, "Set true if main error is due to a tilted Goniometer(def=false)");
93
94 declareProperty("Chi2overDoF", -1.0, "chi squared over dof", Direction::Output);
95 declareProperty("nPeaks", -1, "Number of Peaks Used", Direction::Output);
96 declareProperty("nParams", -1, "Number of Parameters fit", Direction::Output);
97 declareProperty("nIndexed", -1, "Number of new Peaks that WOULD be indexed at 'MaxIndexingError'", Direction::Output);
98
99 declareProperty("MaxAngularChange", 5.0, "Max offset in degrees from current settings(def=5)");
100
101 declareProperty("MaxIndexingError", 0.15,
102 "Use only peaks whose fractional "
103 "hkl values are below this "
104 "tolerance(def=0.15)");
105
106 declareProperty("MaxHKLPeaks2Use", -1.0,
107 "If less than 0 all peaks are used, "
108 "otherwise only peaks whose h,k, "
109 "and l values are below the level "
110 "are used(def=-1)");
111 declareProperty("MaxSamplePositionChangeMeters", .0005, "Maximum Change in Sample position in meters(def=.0005)");
112
113 setPropertyGroup("MaxAngularChange", "Tolerance settings");
114
115 setPropertyGroup("MaxSamplePositionChangeMeters", "Tolerance settings");
116 setPropertyGroup("MaxHKLPeaks2Use", "Tolerance settings");
117 setPropertyGroup("MaxIndexingError", "Tolerance settings");
118
119 setPropertySettings("MaxSamplePositionChangeMeters",
120 std::make_unique<EnabledWhenProperty>("AdjustSampleOffsets", Kernel::IS_EQUAL_TO, "1"));
121
122 setPropertySettings("KeepGoniometerFixedfor",
123 std::make_unique<OrEnabledWhenProperties>("AdjustSampleOffsets", Kernel::IS_EQUAL_TO, "0",
124 "OptimizeGoniometerTilt", Kernel::IS_EQUAL_TO, "0"));
125
126 declareProperty(std::make_unique<WorkspaceProperty<ITableWorkspace>>("OutputNormalisedCovarianceMatrixOptX",
127 "CovarianceInfo", Direction::Output),
128 "The name of the TableWorkspace in which to store the final "
129 "covariance matrix");
130}
131
141 PeaksWorkspace_sptr peaks = getProperty("PeaksWorkspace");
142 PeaksWorkspace_sptr outPeaks = getProperty("ModifiedPeaksWorkspace");
143
144 if (peaks != outPeaks) {
145 outPeaks = peaks->clone();
146 }
147
148 std::vector<int> NOoptimizeRuns = getProperty("KeepGoniometerFixedfor");
149
150 const DblMatrix X = peaks->sample().getOrientedLattice().getUB();
151 Matrix<double> UBinv(X);
152 UBinv.Invert();
153
154 //--------------------------------- Set up data for call to PeakHKLErrors
155 // fitting function ----------
156 // ---- Setting up workspace supplied to PeakHKLErrors
157 // ---------------
158 std::vector<int> RunNumList;
159 std::vector<V3D> ChiPhiOmega;
161
162 int nPeaksUsed = 0;
163 double HKLintOffsetMax = getProperty("MaxIndexingError");
164 double HKLMax = getProperty("MaxHKLPeaks2Use");
165 for (int i = 0; i < peaks->getNumberPeaks(); i++) {
166 IPeak const &peak = peaks->getPeak(i);
167 int runNum = peak.getRunNumber();
168 auto it = RunNumList.begin();
169 for (; it != RunNumList.end() && *it != runNum; ++it) {
170 }
171
172 V3D hkl = UBinv * (peak.getQSampleFrame()) / (2.0 * M_PI);
173 bool use = IndexingUtils::ValidIndex(hkl, HKLintOffsetMax); // use this peak???
174
175 if (use && HKLMax > 0)
176 for (int k = 0; k < 3; k++) {
177 if (fabs(hkl[k]) > HKLMax)
178 use = false;
179 }
180
181 if (it == RunNumList.end() && use) // add to list of unique run numbers in workspace
182 {
183 RunNumList.emplace_back(runNum);
184
186 std::vector<double> phichiOmega = Gon.getEulerAngles("YZY");
187 ChiPhiOmega.emplace_back(phichiOmega[1], phichiOmega[2], phichiOmega[0]);
188 }
189
190 if (use) // add to lists for workspace
191 {
192 nPeaksUsed++;
193 xRef.emplace_back(static_cast<double>(i));
194 xRef.emplace_back(static_cast<double>(i));
195 xRef.emplace_back(static_cast<double>(i));
196 }
197 }
198
199 g_log.notice() << "Number initially indexed = " << nPeaksUsed << " at tolerance = " << HKLintOffsetMax << '\n';
200
201 if (nPeaksUsed < 1) {
202 g_log.error() << "Error in UB too large. 0 peaks indexed at " << HKLintOffsetMax << '\n';
203 throw std::invalid_argument("Error in UB too large. 0 peaks indexed ");
204 }
205
206 int N = 3 * nPeaksUsed; // peaks->getNumberPeaks();
207 auto mwkspc = createWorkspace<Workspace2D>(1, N, N);
208 mwkspc->setPoints(0, xRef);
209 mwkspc->setCounts(0, N, 0.0);
210 mwkspc->setCountStandardDeviations(0, N, 1.0);
211
212 std::string FuncArg = "name=PeakHKLErrors,PeakWorkspaceName=" + getPropertyValue("PeaksWorkspace") + "";
213
214 std::string OptRunNums;
215
216 //--------- Setting Function and Constraint argumens to PeakHKLErrors
217 //---------------
218 std::vector<std::string> ChRunNumList;
219 std::string predChar;
220 for (auto runNum : RunNumList) {
221 auto it1 = NOoptimizeRuns.begin();
222 for (; it1 != NOoptimizeRuns.end() && *it1 != runNum; ++it1) {
223 }
224
225 if (it1 == NOoptimizeRuns.end()) {
226 std::string runNumStr = std::to_string(runNum);
227 OptRunNums += predChar + runNumStr;
228 predChar = "/";
229 ChRunNumList.emplace_back(runNumStr);
230 }
231 }
232
233 bool omitRuns = (bool)getProperty("AdjustSampleOffsets") || (bool)getProperty("OptimizeGoniometerTilt");
234 if (omitRuns) {
235 NOoptimizeRuns = RunNumList;
236 OptRunNums = "";
237 std::string message = "No Goniometer Angles ";
238 if ((bool)getProperty("OptimizeGoniometerTilt"))
239 message += "relative to the tilted Goniometer ";
240 message += "will be 'changed'";
241 g_log.notice(message);
242 }
243 if (!OptRunNums.empty() && !omitRuns)
244 FuncArg += ",OptRuns=" + OptRunNums;
245
246 //------------- Add initial parameter values to FuncArg -----------
247
248 std::ostringstream oss(std::ostringstream::out);
249 oss.precision(3);
250 std::ostringstream oss1(std::ostringstream::out); // constraints
251 oss1.precision(3);
252
253 int nParams = 3;
254 double DegreeTol = getProperty("MaxAngularChange");
255 std::string startConstraint;
256
257 for (size_t i = 0; i < RunNumList.size(); i++) {
258 int runNum = RunNumList[i];
259
260 size_t k = 0;
261 for (; k < NOoptimizeRuns.size(); k++) {
262 if (NOoptimizeRuns[k] == runNum)
263 break;
264 }
265 if (k >= NOoptimizeRuns.size()) {
266 V3D chiphiomega = ChiPhiOmega[i];
267 oss << ",chi" << runNum << "=" << chiphiomega[0] << ",phi" << runNum << "=" << chiphiomega[1] << ",omega"
268 << runNum << "=" << chiphiomega[2];
269
270 oss1 << startConstraint << chiphiomega[0] - DegreeTol << "<chi" << runNum << "<" << chiphiomega[0] + DegreeTol;
271 oss1 << "," << chiphiomega[1] - DegreeTol << "<phi" << runNum << "<" << chiphiomega[1] + DegreeTol;
272
273 oss1 << "," << chiphiomega[2] - DegreeTol << "<omega" << runNum << "<" << chiphiomega[2] + DegreeTol;
274 startConstraint = ",";
275 nParams += 3;
276 }
277 }
278
279 // offset of previous sample position so should start at 0
280 V3D sampPos = V3D(0., 0., 0.);
281
282 oss << ",SampleXOffset=" << sampPos.X() << ",SampleYOffset=" << sampPos.Y() << ",SampleZOffset=" << sampPos.Z();
283 oss << ",GonRotx=0.0,GonRoty=0.0,GonRotz=0.0";
284
285 double maxSampshift = getProperty("MaxSamplePositionChangeMeters");
286 oss1 << startConstraint << sampPos.X() - maxSampshift << "<SampleXOffset<" << sampPos.X() + maxSampshift << ","
287 << sampPos.Y() - maxSampshift << "<SampleYOffset<" << sampPos.Y() + maxSampshift << ","
288 << sampPos.Z() - maxSampshift << "<SampleZOffset<" << sampPos.Z() + maxSampshift;
289
290 oss1 << "," << -DegreeTol << "<GonRotx<" << DegreeTol << "," << -DegreeTol << "<GonRoty<" << DegreeTol << ","
291 << -DegreeTol << "<GonRotz<" << DegreeTol;
292
293 FuncArg += oss.str();
294 std::string Constr = oss1.str();
295
296 g_log.debug() << "Function argument=" << FuncArg << '\n';
297 g_log.debug() << "Constraint argument=" << Constr << '\n';
298
299 //--------------------- set up Fit algorithm call-----------------
300
301 std::shared_ptr<Algorithm> fit_alg = createChildAlgorithm("Fit", .1, .93, true);
302
303 fit_alg->setProperty("Function", FuncArg);
304
305 fit_alg->setProperty("MaxIterations", 60);
306
307 fit_alg->setProperty("Constraints", Constr);
308
309 fit_alg->setProperty("InputWorkspace", mwkspc);
310
311 fit_alg->setProperty("CreateOutput", true);
312
313 std::string Ties;
314
315 if (!(bool)getProperty("AdjustSampleOffsets")) {
316 std::ostringstream oss3(std::ostringstream::out);
317 oss3.precision(3);
318
319 oss3 << "SampleXOffset=" << sampPos.X() << ",SampleYOffset=" << sampPos.Y() << ",SampleZOffset=" << sampPos.Z();
320 Ties = oss3.str();
321 }
322
323 if (!(bool)getProperty("OptimizeGoniometerTilt")) {
324 if (!Ties.empty())
325 Ties += ",";
326
327 Ties += "GonRotx=0.0,GonRoty=0.0,GonRotz=0.0";
328 }
329
330 if (!Ties.empty())
331 fit_alg->setProperty("Ties", Ties);
332
333 fit_alg->setProperty("Output", "out");
334
335 fit_alg->executeAsChildAlg();
336
337 //------------------------- Get/Report Results ------------------
338
339 double chisq = fit_alg->getProperty("OutputChi2overDoF");
340 g_log.notice() << "Fit finished. Status=" << (std::string)fit_alg->getProperty("OutputStatus") << '\n';
341
342 setProperty("Chi2overDoF", chisq);
343
344 setProperty("nPeaks", nPeaksUsed);
345 setProperty("nParams", nParams);
346
347 g_log.debug() << "Chi2overDof=" << chisq << " # Peaks used=" << nPeaksUsed << "# fitting parameters =" << nParams
348 << " dof=" << (nPeaksUsed - nParams) << '\n';
349
350 ITableWorkspace_sptr RRes = fit_alg->getProperty("OutputParameters");
351
352 double sigma = sqrt(chisq);
353
354 std::string OutputStatus = fit_alg->getProperty("OutputStatus");
355 g_log.notice() << "Output Status=" << OutputStatus << '\n';
356
357 //------------------ Fix up Covariance output --------------------
358 ITableWorkspace_sptr NormCov = fit_alg->getProperty("OutputNormalisedCovarianceMatrix");
359 setProperty("OutputNormalisedCovarianceMatrixOptX",
360 NormCov); // What if 2 instances are run
361
362 if (chisq < 0 || chisq != chisq)
363 sigma = -1;
364
365 //------------- Fix up Result workspace values ----------------------------
366 std::map<std::string, double> Results;
367 for (int prm = 0; prm < static_cast<int>(RRes->rowCount()); ++prm) {
368 std::string namee = RRes->getRef<std::string>("Name", prm);
369
370 std::string start = namee.substr(0, 3);
371 if (start != "chi" && start != "phi" && start != "ome" && start != "Sam" && start != "Gon")
372 continue;
373
374 double value = RRes->getRef<double>("Value", prm);
375 Results[namee] = value;
376
377 // Set sigma==1 in optimization. A better estimate is sqrt(Chi2overDoF)
378 double v = sigma * RRes->getRef<double>("Error", prm);
379 RRes->getRef<double>("Error", prm) = v;
380 }
381
382 //-----------Fix up Resultant workspace return info -------------------
383
384 setProperty("FitInfoTable", RRes);
385
386 //----------- update instrument -------------------------
387 V3D newSampPos(Results["SampleXOffset"], Results["SampleYOffset"], Results["SampleZOffset"]);
388
389 auto &componentInfo = outPeaks->mutableComponentInfo();
390 CalibrationHelpers::adjustUpSampleAndSourcePositions(componentInfo.l1(), newSampPos, componentInfo);
391
392 Matrix<double> GonTilt = PeakHKLErrors::RotationMatrixAboutRegAxis(Results["GonRotx"], 'x') *
393 PeakHKLErrors::RotationMatrixAboutRegAxis(Results["GonRoty"], 'y') *
394 PeakHKLErrors::RotationMatrixAboutRegAxis(Results["GonRotz"], 'z');
395
396 int prevRunNum = -1;
397 std::map<int, Matrix<double>> MapRunNum2GonMat;
398 std::string OptRun2 = "/" + OptRunNums + "/";
399
400 int nIndexed = 0;
401 UBinv = outPeaks->sample().getOrientedLattice().getUB();
402 UBinv.Invert();
403 UBinv /= (2 * M_PI);
404 for (int i = 0; i < outPeaks->getNumberPeaks(); ++i) {
405 auto &peak = outPeaks->getPeak(i);
406 peak.setSamplePos(peak.getSamplePos() + newSampPos);
407 int RunNum = peak.getRunNumber();
408 std::string RunNumStr = std::to_string(RunNum);
409 Matrix<double> GonMatrix;
410 if (RunNum == prevRunNum || MapRunNum2GonMat.find(RunNum) != MapRunNum2GonMat.end())
411 GonMatrix = MapRunNum2GonMat[RunNum];
412 else if (OptRun2.find("/" + RunNumStr + "/") < OptRun2.size() - 2) {
413
414 double chi = Results["chi" + RunNumStr];
415 double phi = Results["phi" + RunNumStr];
416 double omega = Results["omega" + RunNumStr];
417
419 uniGonio.makeUniversalGoniometer();
420
421 uniGonio.setRotationAngle("phi", phi);
422 uniGonio.setRotationAngle("chi", chi);
423 uniGonio.setRotationAngle("omega", omega);
424
425 GonMatrix = GonTilt * uniGonio.getR();
426 MapRunNum2GonMat[RunNum] = GonMatrix;
427 } else {
428 GonMatrix = GonTilt * peak.getGoniometerMatrix();
429 MapRunNum2GonMat[RunNum] = GonMatrix;
430 }
431
432 peak.setGoniometerMatrix(GonMatrix);
433 V3D hkl = UBinv * peak.getQSampleFrame();
434 if (Geometry::IndexingUtils::ValidIndex(hkl, HKLintOffsetMax))
435 nIndexed++;
436
437 prevRunNum = RunNum;
438 }
439
440 if (MapRunNum2GonMat.size() == 1) // Only one RunNumber in this PeaksWorkspace
441 {
442 Matrix<double> GonMatrix = MapRunNum2GonMat[outPeaks->getPeak(0).getRunNumber()];
443 Geometry::Goniometer Gon(GonMatrix);
444 outPeaks->mutableRun().setGoniometer(Gon, false);
445 }
446
447 setProperty("ModifiedPeaksWorkspace", outPeaks);
448 setProperty("nIndexed", nIndexed);
449 g_log.notice() << "Number indexed after optimization= " << nIndexed << " at tolerance = " << HKLintOffsetMax << '\n';
450
451} // exec
452
453} // namespace Mantid::Crystal
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:547
double value
The value of the point.
Definition FitMW.cpp:51
#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.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
Kernel::Logger & g_log
Definition Algorithm.h:426
A property class for workspaces.
void init() override
Virtual method - must be overridden by concrete algorithm.
bool isEnabled(const IPropertyManager *algo) const override
Is the property to be shown as "enabled" in the GUI.
std::unique_ptr< Kernel::EnabledWhenProperty > Prop1
OrEnabledWhenProperties(std::string prop1Name, ePropertyCriterion prop1Crit, std::string prop1Value, std::string prop2Name, ePropertyCriterion prop2Crit, std::string prop2Value)
static Kernel::Matrix< double > RotationMatrixAboutRegAxis(double theta, char axis)
Returns the matrix corresponding to a rotation of theta(degrees) around axis.
Class to represent a particular goniometer setting, which is described by the rotation matrix.
Definition Goniometer.h:55
void setRotationAngle(const std::string &name, double value)
Set rotation angle for an axis using motor name.
std::vector< double > getEulerAngles(const std::string &convention="YZX")
Return Euler angles acording to a convention.
void makeUniversalGoniometer()
Make a default universal goniometer with phi,chi,omega angles according to SNS convention.
const Kernel::DblMatrix & getR() const
Return global rotation matrix.
Structure describing a single-crystal peak.
Definition IPeak.h:26
virtual Mantid::Kernel::Matrix< double > getGoniometerMatrix() const =0
virtual int getRunNumber() const =0
virtual Mantid::Kernel::V3D getQSampleFrame() const =0
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.
Support for a property that holds an array of values.
Interface to PropertyManager.
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 const > settings)
Add a PropertySettings instance to the chain of settings for a given property.
void setPropertyGroup(const std::string &name, const std::string &group)
Set the group for a given property.
Interface for modifiers to Property's that specify if they should be enabled or visible in a GUI.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
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
T Invert()
LU inversion routine.
Definition Matrix.cpp:924
Class for 3D vectors.
Definition V3D.h:35
constexpr double X() const noexcept
Get x.
Definition V3D.h:239
constexpr double Y() const noexcept
Get y.
Definition V3D.h:240
constexpr double Z() const noexcept
Get z.
Definition V3D.h:241
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
MANTID_CRYSTAL_DLL void adjustUpSampleAndSourcePositions(double const L0, const Kernel::V3D &newSampPos, Geometry::ComponentInfo &componentInfo)
Updates the ComponentInfo for the workspace containing newInstrument to reflect the position of the s...
std::shared_ptr< PeaksWorkspace > PeaksWorkspace_sptr
Typedef for a shared pointer to a peaks workspace.
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
ePropertyCriterion
Enum for use in EnabledWhenProperty.
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
Definition cow_ptr.h:172
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
Definition Property.h:54
@ Output
An output workspace.
Definition Property.h:55