Mantid
Loading...
Searching...
No Matches
SCDCalibratePanels2.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
15#include "MantidAPI/Run.h"
16#include "MantidAPI/Sample.h"
17#include "MantidAPI/TableRow.h"
27#include "MantidKernel/Logger.h"
28#include <boost/container/flat_set.hpp>
29#include <boost/property_tree/ptree.hpp>
30#include <boost/property_tree/xml_parser.hpp>
31
32#include <boost/math/special_functions/round.hpp>
33#include <cmath>
34#include <filesystem>
35#include <fstream>
36#include <iostream>
37#include <sstream>
38#include <utility>
39
40namespace Mantid::Crystal {
41
42using namespace Mantid::API;
43using namespace Mantid::DataObjects;
44using namespace Mantid::Geometry;
45using namespace Mantid::Kernel;
46
48namespace {
49Logger logger("SCDCalibratePanels2");
50}
51
52DECLARE_ALGORITHM(SCDCalibratePanels2)
53
54
58void SCDCalibratePanels2::init() {
59 // Input peakworkspace
60 declareProperty(std::make_unique<WorkspaceProperty<IPeaksWorkspace>>("PeakWorkspace", "", Kernel::Direction::Input),
61 "Workspace of Indexed Peaks");
62
63 // Lattice constant group
64 auto mustBeNonNegative = std::make_shared<BoundedValidator<double>>();
65 mustBeNonNegative->setLower(0.0);
66 declareProperty("RecalculateUB", true, "Recalculate UB matrix using given lattice constants");
67 declareProperty("a", EMPTY_DBL(), mustBeNonNegative,
68 "Lattice Parameter a (Leave empty to use lattice constants "
69 "in peaks workspace)");
70 declareProperty("b", EMPTY_DBL(), mustBeNonNegative,
71 "Lattice Parameter b (Leave empty to use lattice constants "
72 "in peaks workspace)");
73 declareProperty("c", EMPTY_DBL(), mustBeNonNegative,
74 "Lattice Parameter c (Leave empty to use lattice constants "
75 "in peaks workspace)");
76 declareProperty("alpha", EMPTY_DBL(), mustBeNonNegative,
77 "Lattice Parameter alpha in degrees (Leave empty to use "
78 "lattice constants in peaks workspace)");
79 declareProperty("beta", EMPTY_DBL(), mustBeNonNegative,
80 "Lattice Parameter beta in degrees (Leave empty to use "
81 "lattice constants in peaks workspace)");
82 declareProperty("gamma", EMPTY_DBL(), mustBeNonNegative,
83 "Lattice Parameter gamma in degrees (Leave empty to use "
84 "lattice constants in peaks workspace)");
85 const std::string LATTICE("Lattice Constants");
86 setPropertyGroup("RecalculateUB", LATTICE);
87 setPropertyGroup("a", LATTICE);
88 setPropertyGroup("b", LATTICE);
89 setPropertyGroup("c", LATTICE);
90 setPropertyGroup("alpha", LATTICE);
91 setPropertyGroup("beta", LATTICE);
92 setPropertyGroup("gamma", LATTICE);
93 setPropertySettings("a", std::make_unique<EnabledWhenProperty>("RecalculateUB", IS_DEFAULT));
94 setPropertySettings("b", std::make_unique<EnabledWhenProperty>("RecalculateUB", IS_DEFAULT));
95 setPropertySettings("c", std::make_unique<EnabledWhenProperty>("RecalculateUB", IS_DEFAULT));
96 setPropertySettings("alpha", std::make_unique<EnabledWhenProperty>("RecalculateUB", IS_DEFAULT));
97 setPropertySettings("beta", std::make_unique<EnabledWhenProperty>("RecalculateUB", IS_DEFAULT));
98 setPropertySettings("gamma", std::make_unique<EnabledWhenProperty>("RecalculateUB", IS_DEFAULT));
99
100 declareProperty("Tolerance", 0.15, mustBeNonNegative, "Peak indexing tolerance");
101
102 declareProperty("WavelengthFromUB", false,
103 "If True, compute each peak's wavelength from Bragg's law using the UB matrix and its "
104 "assigned integer HKL, instead of from the measured TOF. Intended for quasi-Laue workflows, "
105 "where a peak's TOF-derived wavelength can be unreliable, but works for standard "
106 "time-of-flight Laue data as well. NOTE: when enabled, CalibrateT0 has no effect, since "
107 "wavelength no longer depends on TOF.");
108
109 // Calibration options group
110 // NOTE:
111 // The general workflow of calibration is
112 // - calibrate L1 using all peaks
113 // - calibrate each bank
114 // - calibrate/update L1 again since bank movement will affect L1
115 // - calibrate T0
116 // - calibrate samplePos
117 const std::string CALIBRATION("Calibration Options");
118 // --------------
119 // ----- L1 -----
120 // --------------
121 declareProperty("CalibrateL1", true, "Change the L1(source to sample) distance");
122 declareProperty("SearchRadiusL1", 0.1, mustBeNonNegative,
123 "Search radius of delta L1 in meters, which is used to constrain optimization search space"
124 "when calibrating L1");
125 // editability
126 setPropertySettings("SearchRadiusL1", std::make_unique<EnabledWhenProperty>("CalibrateL1", IS_EQUAL_TO, "1"));
127 // grouping
128 setPropertyGroup("CalibrateL1", CALIBRATION);
129 setPropertyGroup("SearchRadiusL1", CALIBRATION);
130 // ----------------
131 // ----- bank -----
132 // ----------------
133 declareProperty("CalibrateBanks", false, "Calibrate position and orientation of each bank.");
134 declareProperty(
135 "SearchRadiusTransBank", 5e-2, mustBeNonNegative,
136 "This is the search radius (in meter) when calibrating component translations, used to constrain optimization"
137 "search space when calibration translation of banks");
138 declareProperty("SearchradiusRotXBank", 1.0, mustBeNonNegative,
139 "This is the search radius (in deg) when calibrating component reorientation, used to constrain "
140 "optimization search space");
141 declareProperty("SearchradiusRotYBank", 1.0, mustBeNonNegative,
142 "This is the search radius (in deg) when calibrating component reorientation, used to constrain "
143 "optimization search space");
144 declareProperty("SearchradiusRotZBank", 1.0, mustBeNonNegative,
145 "This is the search radius (in deg) when calibrating component reorientation, used to constrain "
146 "optimization search space");
147 declareProperty("CalibrateSize", false, "Calibrate detector size for each bank.");
148 declareProperty("SearchRadiusSize", 0.0, mustBeNonNegative,
149 "This is the search radius (unit less) of scale factor around at value 1.0 "
150 "when calibrating component size if it is a rectangualr detector.");
151 declareProperty("FixAspectRatio", true,
152 "If true, the scaling factor for detector along X- and Y-axis "
153 "must be the same. Otherwise, the 2 scaling factors are free.");
154 declareProperty("BankName", "",
155 "If given, only the specified bank/component will be calibrated."
156 "Otherwise, all banks will be calibrated.");
157
158 // editability
159 setPropertySettings("BankName", std::make_unique<EnabledWhenProperty>("CalibrateBanks", IS_EQUAL_TO, "1"));
160 setPropertySettings("SearchRadiusTransBank",
161 std::make_unique<EnabledWhenProperty>("CalibrateBanks", IS_EQUAL_TO, "1"));
162 setPropertySettings("SearchradiusRotXBank",
163 std::make_unique<EnabledWhenProperty>("CalibrateBanks", IS_EQUAL_TO, "1"));
164 setPropertySettings("SearchradiusRotYBank",
165 std::make_unique<EnabledWhenProperty>("CalibrateBanks", IS_EQUAL_TO, "1"));
166 setPropertySettings("SearchradiusRotZBank",
167 std::make_unique<EnabledWhenProperty>("CalibrateBanks", IS_EQUAL_TO, "1"));
168 setPropertySettings("CalibrateSize", std::make_unique<EnabledWhenProperty>("CalibrateBanks", IS_EQUAL_TO, "1"));
169 setPropertySettings("SearchRadiusSize", std::make_unique<EnabledWhenProperty>("CalibrateSize", IS_EQUAL_TO, "1"));
170 setPropertySettings("FixAspectRatio", std::make_unique<EnabledWhenProperty>("CalibrateSize", IS_EQUAL_TO, "1"));
171 // grouping
172 setPropertyGroup("CalibrateBanks", CALIBRATION);
173 setPropertyGroup("BankName", CALIBRATION);
174 setPropertyGroup("SearchRadiusTransBank", CALIBRATION);
175 setPropertyGroup("SearchradiusRotXBank", CALIBRATION);
176 setPropertyGroup("SearchradiusRotYBank", CALIBRATION);
177 setPropertyGroup("SearchradiusRotZBank", CALIBRATION);
178 setPropertyGroup("CalibrateSize", CALIBRATION);
179 setPropertyGroup("SearchRadiusSize", CALIBRATION);
180 setPropertyGroup("FixAspectRatio", CALIBRATION);
181
182 // --------------
183 // ----- T0 -----
184 // --------------
185 declareProperty("CalibrateT0", false, "Calibrate the T0 (initial TOF)");
186 declareProperty("SearchRadiusT0", 10.0, mustBeNonNegative,
187 "Search radius of T0 (in ms), used to constrain optimization search space");
188 // editability
189 setPropertySettings("SearchRadiusT0", std::make_unique<EnabledWhenProperty>("CalibrateT0", IS_EQUAL_TO, "1"));
190 // grouping
191 setPropertyGroup("CalibrateT0", CALIBRATION);
192 setPropertyGroup("SearchRadiusT0", CALIBRATION);
193 // ---------------------
194 // ----- samplePos -----
195 // ---------------------
196 declareProperty("TuneSamplePosition", false, "Fine tunning sample position");
197 declareProperty("SearchRadiusSamplePos", 0.1, mustBeNonNegative,
198 "Search radius of sample position change (in meters), used to constrain optimization search space");
199 // editability
200 setPropertySettings("SearchRadiusSamplePos",
201 std::make_unique<EnabledWhenProperty>("TuneSamplePosition", IS_EQUAL_TO, "1"));
202 // grouping
203 setPropertyGroup("TuneSamplePosition", CALIBRATION);
204 setPropertyGroup("SearchRadiusSamplePos", CALIBRATION);
205
206 // Output options group
207 declareProperty(std::make_unique<WorkspaceProperty<ITableWorkspace>>("OutputWorkspace", "", Direction::Output),
208 "The workspace containing the calibration table.");
209 declareProperty("T0", 0.0, "Returns the TOF offset from optimization", Kernel::Direction::Output);
210 const std::vector<std::string> detcalExts{".DetCal", ".Det_Cal"};
211 declareProperty(
212 std::make_unique<FileProperty>("DetCalFilename", "SCDCalibrate2.DetCal", FileProperty::OptionalSave, detcalExts),
213 "Path to an ISAW-style .detcal file to save.");
214 declareProperty(
215 std::make_unique<FileProperty>("XmlFilename", "SCDCalibrate2.xml", FileProperty::OptionalSave, ".xml"),
216 "Path to an Mantid .xml description(for LoadParameterFile) file to "
217 "save.");
218 declareProperty(
219 std::make_unique<FileProperty>("CSVFilename", "SCDCalibrate2.csv", FileProperty::OptionalSave, ".csv"),
220 "Path to an .csv file which contains the Calibration Table");
221 // group into Output group
222 const std::string OUTPUT("Output");
223 setPropertyGroup("OutputWorkspace", OUTPUT);
224 setPropertyGroup("DetCalFilename", OUTPUT);
225 setPropertyGroup("XmlFilename", OUTPUT);
226 setPropertyGroup("CSVFilename", OUTPUT);
227
228 // Add new section for advanced control of the calibration/optimization
229 // NOTE: profiling is expensive, think twice before start
230 declareProperty("VerboseOutput", false, "Toggle of child algorithm console output.");
231 declareProperty("ProfileL1", false, "Perform profiling of objective function with given input for L1");
232 declareProperty("ProfileBanks", false, "Perform profiling of objective function with given input for Banks");
233 declareProperty("ProfileT0", false, "Perform profiling of objective function with given input for T0");
234 declareProperty("ProfileL1T0", false, "Perform profiling of objective function along L1 and T0");
235 // grouping into one category
236 const std::string ADVCNTRL("Advanced Option");
237 setPropertyGroup("VerboseOutput", ADVCNTRL);
238 setPropertyGroup("ProfileL1", ADVCNTRL);
239 setPropertyGroup("ProfileBanks", ADVCNTRL);
240 setPropertyGroup("ProfileT0", ADVCNTRL);
241 setPropertyGroup("ProfileL1T0", ADVCNTRL);
242
243 // About fitting
244 auto mustBePositive = std::make_shared<Kernel::BoundedValidator<int>>();
245 mustBePositive->setLower(0);
246 declareProperty("MaxFitIterations", 500, mustBePositive,
247 "Stop after this number of iterations if a good fit is not found");
248}
249
255std::map<std::string, std::string> SCDCalibratePanels2::validateInputs() {
256 std::map<std::string, std::string> issues;
257
258 // Lattice constants are required if no UB is attached to the input
259 // peak workspace
260 IPeaksWorkspace_sptr pws = getProperty("PeakWorkspace");
261 double a = getProperty("a");
262 double b = getProperty("b");
263 double c = getProperty("c");
264 double alpha = getProperty("alpha");
265 double beta = getProperty("beta");
266 double gamma = getProperty("gamma");
267 if ((a == EMPTY_DBL() || b == EMPTY_DBL() || c == EMPTY_DBL() || alpha == EMPTY_DBL() || beta == EMPTY_DBL() ||
268 gamma == EMPTY_DBL()) &&
269 (!pws->sample().hasOrientedLattice())) {
270 issues["RecalculateUB"] = "Lattice constants are needed for peak "
271 "workspace without a UB mattrix";
272 }
273
274 // T0 has no effect on the objective function once wavelength is derived
275 // from the UB matrix instead of TOF
276 m_waveFromUB = getProperty("WavelengthFromUB");
277 bool calibrateT0 = getProperty("CalibrateT0");
278 if (m_waveFromUB && calibrateT0) {
279 issues["CalibrateT0"] = "CalibrateT0 has no effect when WavelengthFromUB is enabled, since wavelength no "
280 "longer depends on TOF.";
281 }
282
283 // sanity check
285 throw std::runtime_error("calibrationTableColumnTypes and calibrationTableColumnTypes have different size.");
286
287 return issues;
288}
289
295 // parse all inputs
296 IPeaksWorkspace_sptr m_pws = getProperty("PeakWorkspace");
297
298 // recalculate UB with given lattice constant
299 // if required
300 if (getProperty("RecalculateUB")) {
301 // parse lattice constants
303
304 // recalculate UB and index peaks
305 updateUBMatrix(m_pws);
306 }
307
308 // remove unindexed peaks
309 m_pws = removeUnindexedPeaks(m_pws);
310
311 bool calibrateT0 = getProperty("CalibrateT0");
312 bool calibrateL1 = getProperty("CalibrateL1");
313 bool calibrateBanks = getProperty("CalibrateBanks");
314 bool tuneSamplePos = getProperty("TuneSamplePosition");
315 mCalibBankName = getPropertyValue("BankName");
316 bool profL1 = getProperty("ProfileL1");
317 bool profBanks = getProperty("ProfileBanks");
318 bool profT0 = getProperty("ProfileT0");
319 bool profL1T0 = getProperty("ProfileL1T0");
320
321 const std::string DetCalFilename = getProperty("DetCalFilename");
322 const std::string XmlFilename = getProperty("XmlFilename");
323 const std::string CSVFilename = getProperty("CSVFilename");
324
325 // Properties for resizing rectangular detector size
326 bool docalibsize = getProperty("CalibrateSize");
327 double sizesearchradius = getProperty("SearchRadiusSize");
328 bool fixdetxyratio = getProperty("FixAspectRatio");
329
330 maxFitIterations = getProperty("MaxFitIterations");
331 LOGCHILDALG = getProperty("VerboseOutput");
332
333 // STEP_0: sort the peaks
334 std::vector<std::pair<std::string, bool>> criteria{{"BankName", true}};
335 m_pws->sort(criteria);
336 // need to keep a copy of the peak workspace at its input state
337 IPeaksWorkspace_sptr pws_original = m_pws->clone();
338
339 // STEP_2: preparation
340 // get names of banks that can be calibrated
341 getBankNames(m_pws);
342
343 // DEV ONLY
344 // !!!WARNNING!!!
345 // Profiling a parameter space can be time-consuming and may freeze up your
346 // computing resources for days, therefore please proceed with caution.
347 if (profL1) {
348 profileL1(m_pws, pws_original);
349 }
350 if (profBanks) {
351 profileBanks(m_pws, pws_original);
352 }
353 if (profT0) {
354 profileT0(m_pws, pws_original);
355 }
356 if (profL1T0) {
357 profileL1T0(m_pws, pws_original);
358 }
359
360 // STEP_3: optimize
361 // - L1 (with or without T0 cali attached)
362 // - Banks
363 // - sample position
364 if (calibrateL1) {
365 // NOTE:
366 // L1 and T0 can be calibrated together to provide stable calibration results.
367 g_log.notice() << "** Calibrating L1 (moderator) as requested\n";
368 optimizeL1(m_pws, pws_original);
369 }
370
371 if (calibrateBanks) {
372 g_log.notice() << "** Calibrating L2 and orientation (bank) as requested\n";
373 optimizeBanks(m_pws, pws_original, docalibsize, sizesearchradius, fixdetxyratio);
374 }
375
376 if (calibrateL1 && calibrateBanks) {
377 g_log.notice() << "** Calibrating L1 (moderator) after bank adjusted\n";
378 optimizeL1(m_pws, pws_original);
379 // NOTE:
380 // Turns out 1 pass is sufficient (tested with the following block)
381 //
382 // double delta = 1;
383 // int cnt = 0;
384 // while (delta > 0.01) {
385 // double L1_pre = m_pws->getInstrument()->getSource()->getPos().Z();
386 // optimizeBanks(m_pws, pws_original);
387 // optimizeL1(m_pws, pws_original);
388 // double L1_post = m_pws->getInstrument()->getSource()->getPos().Z();
389 // delta = std::abs((L1_pre - L1_post) / L1_pre);
390 // cnt += 1;
391 // g_log.notice() << "@pass_" << cnt << "\n" << L1_pre << "-->" << L1_post << "\n";
392 // }
393 }
394
395 if (calibrateT0 && !calibrateL1) {
396 // NOTE:
397 // L1 and T0 can be calibrated together to provide a stable results, which is the
398 // recommended way.
399 // However, one can still calibrate T0 only if desired.
400 g_log.notice() << "** Calibrating T0 only as requested\n";
401 optimizeT0(m_pws, pws_original);
402 }
403
404 if (tuneSamplePos && !calibrateL1) {
405 g_log.notice() << "** Tunning sample position only as requested\n";
406 optimizeSamplePos(m_pws, pws_original);
407 }
408
409 if (calibrateT0 && tuneSamplePos && !calibrateL1) {
410 g_log.warning() << "** You have chosen to calibrate T0 and sample position while ignoring"
411 << " L1, which means an iterative search outside this calibration is needed"
412 << " in order to find the minimum.\n";
413 }
414
415 // STEP_4: generate a table workspace to save the calibration results
416 g_log.notice() << "-- Generate calibration table\n";
417 Instrument_sptr instCalibrated = std::const_pointer_cast<Geometry::Instrument>(m_pws->getInstrument());
418 const Geometry::ParameterMap &pmap = m_pws->instrumentParameters();
419 ITableWorkspace_sptr tablews = generateCalibrationTable(instCalibrated, pmap);
420
421 // STEP_5: Write to disk if required
422 if (!XmlFilename.empty()) {
423 saveXmlFile(XmlFilename, m_BankNames, instCalibrated, pmap);
424 }
425
426 if (!DetCalFilename.empty()) {
427 saveIsawDetCal(DetCalFilename, m_BankNames, instCalibrated, m_T0);
428 }
429
430 if (!CSVFilename.empty()) {
431 saveCalibrationTable(CSVFilename, tablews);
432 }
433
434 // STEP_4: Set the output
435 setProperty("T0", m_T0); // output the calibrated T0 as a single value
436}
437
441
449 // cache starting L1 position
450 double original_L1 = std::abs(pws->getInstrument()->getSource()->getPos().Z());
451 // T0 can be calibrate along with L1 to provide a more stable results
452 bool caliT0 = getProperty("CalibrateT0");
453 bool tuneSamplepos = getProperty("TuneSamplePosition");
454
456
457 // fit algorithm for the optimization of L1
458 auto fitL1_alg = createChildAlgorithm("Fit", -1, -1, false);
459 auto objf = std::make_shared<SCDCalibratePanels2ObjFunc>();
460 // NOTE: always use the original pws to get the tofs
461 std::vector<double> tofs = captureTOF(pws_original);
462 objf->setPeakWorkspace(pws, "moderator", tofs, m_waveFromUB);
463 fitL1_alg->setProperty("Function", std::dynamic_pointer_cast<IFunction>(objf));
464
465 //-- bounds&constraints def
466 std::ostringstream tie_str;
467 tie_str << "DeltaX=0.0,DeltaY=0.0,"
468 << "RotX=0.0,RotY=0.0,RotZ=0.0";
469 if (!tuneSamplepos) {
470 tie_str << ",DeltaSampleX=0.0,DeltaSampleY=0.0,DeltaSampleZ=0.0";
471 }
472 if (!caliT0) {
473 tie_str << ",DeltaT0=" << m_T0;
474 }
475 std::ostringstream constraint_str;
476 double r_L1 = getProperty("SearchRadiusL1"); // get search radius
477 r_L1 = std::abs(r_L1);
478 constraint_str << -r_L1 << "<DeltaZ<" << r_L1;
479 // throw in the constrain for T0 cali if needed
480 if (caliT0) {
481 double r_dT0 = getProperty("SearchRadiusT0");
482 r_dT0 = std::abs(r_dT0);
483 constraint_str << "," << -r_dT0 << "<DeltaT0<" << r_dT0;
484 }
485 if (tuneSamplepos) {
486 double r_dsp = getProperty("SearchRadiusSamplePos");
487 r_dsp = std::abs(r_dsp);
488 constraint_str << "," << -r_dsp << "<DeltaSampleX<" << r_dsp // dsx
489 << "," << -r_dsp << "<DeltaSampleY<" << r_dsp // dsy
490 << "," << -r_dsp << "<DeltaSampleZ<" << r_dsp; // dsz
491 }
492 //-- set and go
493 fitL1_alg->setProperty("Ties", tie_str.str());
494 fitL1_alg->setProperty("Constraints", constraint_str.str());
495 fitL1_alg->setProperty("InputWorkspace", l1ws);
496 fitL1_alg->setProperty("CreateOutput", true);
497 fitL1_alg->setProperty("Output", "fit");
498 fitL1_alg->executeAsChildAlg();
499
500 //-- parse output
501 std::ostringstream calilog;
502 double chi2OverDOF = fitL1_alg->getProperty("OutputChi2overDoF");
503 ITableWorkspace_sptr rst = fitL1_alg->getProperty("OutputParameters");
504 // get results for L1
505 double dL1_optimized = rst->getRef<double>("Value", 2);
506
507 // get results for T0 (optional)
508 double dT0_optimized = rst->getRef<double>("Value", 6);
509
510 // get results for sample pos
511 // NOTE:
512 // if samplePos is not part of calibration, we will get zeros here, which means zero
513 // negative impact on the whole pws
514 double dsx_optimized = rst->getRef<double>("Value", 7);
515 double dsy_optimized = rst->getRef<double>("Value", 8);
516 double dsz_optimized = rst->getRef<double>("Value", 9);
517
518 // apply the cali results (for output cali table and file)
519 adjustComponent(0.0, 0.0, dL1_optimized, 0.0, 0.0, 0.0, EMPTY_DBL(), EMPTY_DBL(),
520 pws->getInstrument()->getSource()->getName(), pws);
521 m_T0 = dT0_optimized;
522 adjustComponent(dsx_optimized, dsy_optimized, dsz_optimized, 0.0, 0.0, 0.0, EMPTY_DBL(), EMPTY_DBL(),
523 "sample-position", pws);
524 // logging
525 int npks = pws->getNumberPeaks();
526 calilog << "-- Fit L1 results using " << npks << " peaks:\n"
527 << " dL1: " << dL1_optimized << " \n"
528 << " L1 " << original_L1 << " -> " << -pws->getInstrument()->getSource()->getPos().Z() << " \n"
529 << " dT0 = " << m_T0 << " (ms)\n"
530 << " dSamplePos = (" << dsx_optimized << "," << dsy_optimized << "," << dsz_optimized << ")\n"
531 << " chi2/DOF = " << chi2OverDOF << "\n";
532 g_log.notice() << calilog.str();
533}
534
545 const bool &docalibsize, const double &sizesearchradius,
546 const bool &fixdetxyratio) {
548 for (int i = 0; i < static_cast<int>(m_BankNames.size()); ++i) {
550 // prepare local copies to work with
551 const std::string bankname = *std::next(m_BankNames.begin(), i);
552 const std::string pwsBankiName = "_pws_" + bankname;
553
554 // Find out whether to skip the calibration by user's specification
555 // This check is only requied when mCalibBankName is not empty string
556 if (mCalibBankName != "") {
557 bool isbank = (bankname == mCalibBankName);
558 std::stringstream ss;
559 ss << "i = " << i << " m bank name = " << bankname;
560 if (isbank)
561 ss << " ... True ...";
562 else
563 ss << " ... Stop ...";
564 g_log.notice(ss.str());
565 // continue/skip if bank name is not what is specified
566 if (!isbank)
567 continue;
568 }
569
570 //-- step 0: extract peaks that lies on the current bank
571 IPeaksWorkspace_sptr pwsBanki = selectPeaksByBankName(pws, bankname, pwsBankiName);
572 // get tofs from the original subset of pws
573 IPeaksWorkspace_sptr pwsBanki_original = selectPeaksByBankName(pws_original, bankname, pwsBankiName);
574 std::vector<double> tofs = captureTOF(pwsBanki_original);
575
576 // Do not attempt correct panels with less than 6 peaks as the system will
577 // be under-determined
578 int nBankPeaks = pwsBanki->getNumberPeaks();
579 if (nBankPeaks < MINIMUM_PEAKS_PER_BANK) {
580 // use ostringstream to prevent OPENMP breaks log info
581 std::ostringstream msg_npeakCheckFail;
582 msg_npeakCheckFail << "-- Bank " << bankname << " have only " << nBankPeaks << " (<" << MINIMUM_PEAKS_PER_BANK
583 << ") Peaks, skipping\n";
584 g_log.notice() << msg_npeakCheckFail.str();
585 continue;
586 }
587
588 //-- step 1: prepare a mocked workspace with QSample as its yValues
590
591 //-- step 2&3: invoke fit to find both traslation and rotation
592 auto fitBank_alg = createChildAlgorithm("Fit", -1, -1, false);
593 //---- setup obj fun def
594 auto objf = std::make_shared<SCDCalibratePanels2ObjFunc>();
595 objf->setPeakWorkspace(pwsBanki, bankname, tofs, m_waveFromUB);
596 fitBank_alg->setProperty("Function", std::dynamic_pointer_cast<IFunction>(objf));
597
598 //---- bounds&constraints def
599 //
600 double searchRadiusRotX = getProperty("SearchradiusRotXBank");
601 searchRadiusRotX = std::abs(searchRadiusRotX);
602 double searchRadiusRotY = getProperty("SearchradiusRotYBank");
603 searchRadiusRotY = std::abs(searchRadiusRotY);
604 double searchRadiusRotZ = getProperty("SearchradiusRotZBank");
605 searchRadiusRotZ = std::abs(searchRadiusRotZ);
606 //
607 double searchRadiusTran = getProperty("SearchRadiusTransBank");
608 searchRadiusTran = std::abs(searchRadiusTran);
609 //
610 std::ostringstream tie_str;
611 tie_str << "DeltaSampleX=0.0,DeltaSampleY=0.0,DeltaSampleZ=0.0,"
612 << "DeltaT0=" << m_T0;
613 std::ostringstream constraint_str;
614 // rot x
615 if (searchRadiusRotX < Tolerance) {
616 tie_str << ",RotX=0.0";
617 } else {
618 constraint_str << -searchRadiusRotX << "<RotX<" << searchRadiusRotX << ",";
619 }
620 // rot y
621 if (searchRadiusRotY < Tolerance) {
622 tie_str << ",RotY=0.0";
623 } else {
624 constraint_str << -searchRadiusRotY << "<RotY<" << searchRadiusRotY << ",";
625 }
626 // rot z
627 if (searchRadiusRotZ < Tolerance) {
628 tie_str << ",RotZ=0.0";
629 } else {
630 constraint_str << -searchRadiusRotZ << "<RotZ<" << searchRadiusRotZ << ","; // constrain rotation around Z-axis
631 }
632 // translation
633 if (searchRadiusTran < Tolerance) {
634 tie_str << ",DeltaX=0.0,DeltaY=0.0,DeltaZ=0.0";
635 } else {
636 constraint_str << -searchRadiusTran << "<DeltaX<" << searchRadiusTran << "," // restrict tranlastion along X
637 << -searchRadiusTran << "<DeltaY<" << searchRadiusTran << "," // restrict tranlastion along Y
638 << -searchRadiusTran << "<DeltaZ<" << searchRadiusTran; // restrict tranlastion along Z
639 }
640 // calibration of detector size
641 // docalibsize, sizesearchradius, fixdetxyratio
642 Geometry::Instrument_sptr inst = std::const_pointer_cast<Geometry::Instrument>(pws->getInstrument());
643 Geometry::IComponent_const_sptr comp = inst->getComponentByName(bankname);
644 std::shared_ptr<const Geometry::RectangularDetector> rectDet =
645 std::dynamic_pointer_cast<const Geometry::RectangularDetector>(comp);
646
647 std::pair<double, double> scales = getRectangularDetectorScaleFactors(inst, bankname, pws->instrumentParameters());
648
649 std::ostringstream scaleconstraints;
650 std::ostringstream scaleties;
651 if (rectDet && docalibsize) {
652 // set up constraints
653 scaleconstraints << scales.first - sizesearchradius << " <=ScaleX<" << scales.first + sizesearchradius;
654 if (fixdetxyratio) {
655 scaleties << "ScaleX=ScaleY";
656 } else {
657 scaleconstraints << "," << scales.second - sizesearchradius << " <=ScaleY<" << scales.second + sizesearchradius;
658 }
659 } else {
660 // fix the scalex and scaley to its
661 scaleties << "ScaleX=" << scales.first << ", ScaleY=" << scales.second;
662 }
663
664 // construct the final constraint and tie
665 std::string fitconstraint{constraint_str.str()};
666 if (scaleconstraints.str() != "") {
667 if (fitconstraint == "")
668 fitconstraint += scaleconstraints.str();
669 else
670 fitconstraint += "," + scaleconstraints.str();
671 }
672 std::string fittie{tie_str.str()};
673 if (scaleties.str() != "") {
674 if (fittie == "")
675 fittie += scaleties.str();
676 else
677 fittie += "," + scaleties.str();
678 }
679
680 g_log.information("Fitting " + bankname + ": constraint = " + fitconstraint + "\n\t tie = " + fittie);
681
682 //---- set&go
683 if (fittie != "")
684 fitBank_alg->setProperty("Ties", fittie);
685 if (fitconstraint != "")
686 fitBank_alg->setProperty("Constraints", fitconstraint);
687 fitBank_alg->setProperty("InputWorkspace", wsBankCali);
688 fitBank_alg->setProperty("CreateOutput", true);
689 fitBank_alg->setProperty("Output", "fit");
690 fitBank_alg->setProperty("MaxIterations", maxFitIterations);
691
692 fitBank_alg->executeAsChildAlg();
693
694 //---- cache results
695 double chi2OverDOF = fitBank_alg->getProperty("OutputChi2overDoF");
696 ITableWorkspace_sptr rstFitBank = fitBank_alg->getProperty("OutputParameters");
697 double dx = rstFitBank->getRef<double>("Value", 0);
698 double dy = rstFitBank->getRef<double>("Value", 1);
699 double dz = rstFitBank->getRef<double>("Value", 2);
700 double drx = rstFitBank->getRef<double>("Value", 3);
701 double dry = rstFitBank->getRef<double>("Value", 4);
702 double drz = rstFitBank->getRef<double>("Value", 5);
703 double scalex = rstFitBank->getRef<double>("Value", 10);
704 double scaley = rstFitBank->getRef<double>("Value", 11);
705
706 //-- step 4: update the instrument with optimization results
707 std::string bn = bankname;
708 std::ostringstream calilog;
709 // On CORELLI the panel sits one level deeper: bankNN is an empty grouping
710 // node (at the origin, identity rotation) whose sixteenpack child carries
711 // the actual position and orientation. The objective function perturbs
712 // bankNN/sixteenpack (see SCDCalibratePanels2ObjFunc::setPeakWorkspace),
713 // so the same component must be adjusted here for the applied correction
714 // to match the fitted one. Rotating bankNN instead would rotate the panel
715 // about the sample position rather than about its own centre.
716 if (pws->getInstrument()->getName().compare("CORELLI") == 0) {
717 bn.append("/sixteenpack");
718 }
719 // update instrument for output
720 if (rectDet && docalibsize) {
721 // adjust detector size only if it is to be set to refine
722 adjustComponent(dx, dy, dz, drx, dry, drz, scalex, scaley, bn, pws);
723 } else {
724 // (1) no rectangular det or (2) not to refine detector size:
725 // do not set any physically possible scalex or scaley
726 adjustComponent(dx, dy, dz, drx, dry, drz, EMPTY_DBL(), EMPTY_DBL(), bn, pws);
727 }
728 // logging
729 V3D dtrans(dx, dy, dz);
730 V3D drots(drx, dry, drz);
731 calilog << "-- Fit " << bn << " results using " << nBankPeaks << " peaks:\n"
732 << " d(x,y,z) = " << dtrans << "\n"
733 << " r(x,y,z) = " << drots << "\n"
734 << " scale(x, y) = " << scalex << ", " << scaley << " chi2/DOF = " << chi2OverDOF << "\n";
735 g_log.notice() << calilog.str();
736
737 // -- cleanup
739 }
741}
742
754 // create child Fit alg to optimize T0
755 auto fitT0_alg = createChildAlgorithm("Fit", -1, -1, false);
756 //-- obj func def
757 // dl;dr;
758 // Fit algorithm requires a IFunction1D to fit
759 // details
760 // Fit algorithm requires a class derived from IFunction1D as its
761 // input, so we have to implement the objective function as a separate
762 // class just to get Fit serving as an optimizer.
763 // For this particular case, we are constructing an objective function
764 // based on IFunction1D that outputs a fake histogram consist of
765 // qSample calculated based on perturbed instrument positions and
766 // orientations.
768
769 auto objf = std::make_shared<SCDCalibratePanels2ObjFunc>();
770 // NOTE: always use the original pws to get the tofs
771 std::vector<double> tofs = captureTOF(pws_original);
772 objf->setPeakWorkspace(pws, "none", tofs, m_waveFromUB);
773 fitT0_alg->setProperty("Function", std::dynamic_pointer_cast<IFunction>(objf));
774
775 //-- bounds&constraints def
776 std::ostringstream tie_str;
777 tie_str << "DeltaX=0.0,DeltaY=0.0,DeltaZ=0.0,"
778 << "RotX=0.0,RotY=0.0,RotZ=0.0,"
779 << "DeltaSampleX=0.0,DeltaSampleY=0.0,DeltaSampleZ=0.0";
780 std::ostringstream constraint_str;
781 double r_dT0 = getProperty("SearchRadiusT0");
782 r_dT0 = std::abs(r_dT0);
783 constraint_str << -r_dT0 << "<DeltaT0<" << r_dT0;
784
785 //-- set&go
786 fitT0_alg->setProperty("Ties", tie_str.str());
787 fitT0_alg->setProperty("Constraints", constraint_str.str());
788 fitT0_alg->setProperty("InputWorkspace", t0ws);
789 fitT0_alg->setProperty("CreateOutput", true);
790 fitT0_alg->setProperty("Output", "fit");
791 fitT0_alg->executeAsChildAlg();
792
793 //-- parse output
794 std::ostringstream calilog;
795 double chi2OverDOF = fitT0_alg->getProperty("OutputChi2overDoF");
796 ITableWorkspace_sptr rst = fitT0_alg->getProperty("OutputParameters");
797 double dT0_optimized = rst->getRef<double>("Value", 6);
798
799 // apply calibration results (for output file and caliTable)
800 m_T0 = dT0_optimized;
801 int npks = pws->getNumberPeaks();
802 // logging
803 calilog << "-- Fit T0 results using " << npks << " peaks:\n"
804 << " dT0 = " << m_T0 << " (ms)\n"
805 << " chi2/DOF = " << chi2OverDOF << "\n";
806 g_log.notice() << calilog.str();
807}
808
816 // create child Fit alg to optimize T0
817 auto fitSamplePos_alg = createChildAlgorithm("Fit", -1, -1, false);
818
819 // creat input 1DHist from qSample
821
822 auto objf = std::make_shared<SCDCalibratePanels2ObjFunc>();
823 // NOTE: always use the original pws to get the tofs
824 std::vector<double> tofs = captureTOF(pws_original);
825 objf->setPeakWorkspace(pws, "none", tofs, m_waveFromUB);
826 fitSamplePos_alg->setProperty("Function", std::dynamic_pointer_cast<IFunction>(objf));
827
828 //-- bounds&constraints def
829 std::ostringstream tie_str;
830 tie_str << "DeltaX=0.0,DeltaY=0.0,DeltaZ=0.0,"
831 << "RotX=0.0,RotY=0.0,RotZ=0.0,"
832 << "DeltaT0=" << m_T0;
833 std::ostringstream constraint_str;
834 double r_dsp = getProperty("SearchRadiusSamplePos");
835 r_dsp = std::abs(r_dsp);
836 constraint_str << -r_dsp << "<DeltaSampleX<" << r_dsp << "," << -r_dsp << "<DeltaSampleY<" << r_dsp << "," << -r_dsp
837 << "<DeltaSampleZ<" << r_dsp;
838
839 //-- set&go
840 fitSamplePos_alg->setProperty("Ties", tie_str.str());
841 fitSamplePos_alg->setProperty("Constraints", constraint_str.str());
842 fitSamplePos_alg->setProperty("InputWorkspace", samplePosws);
843 fitSamplePos_alg->setProperty("CreateOutput", true);
844 fitSamplePos_alg->setProperty("Output", "fit");
845 fitSamplePos_alg->executeAsChildAlg();
846
847 //-- parse output
848 std::ostringstream calilog;
849 double chi2OverDOF = fitSamplePos_alg->getProperty("OutputChi2overDoF");
850 ITableWorkspace_sptr rst = fitSamplePos_alg->getProperty("OutputParameters");
851 double dsx_optimized = rst->getRef<double>("Value", 7);
852 double dsy_optimized = rst->getRef<double>("Value", 8);
853 double dsz_optimized = rst->getRef<double>("Value", 9);
854
855 // apply the calibration results to pws for ouptut file
856 adjustComponent(dsx_optimized, dsy_optimized, dsz_optimized, 0.0, 0.0, 0.0, EMPTY_DBL(), EMPTY_DBL(),
857 "sample-position", pws);
858 int npks = pws->getNumberPeaks();
859 // logging
860 calilog << "-- Tune SamplePos results using " << npks << " peaks:\n"
861 << " deltaSamplePos = (" << dsx_optimized << "," << dsy_optimized << "," << dsz_optimized << ")\n"
862 << " chi2/DOF = " << chi2OverDOF << "\n";
863 g_log.notice() << calilog.str();
864}
865
869
876 m_a = getProperty("a");
877 m_b = getProperty("b");
878 m_c = getProperty("c");
879 m_alpha = getProperty("alpha");
880 m_beta = getProperty("beta");
881 m_gamma = getProperty("gamma");
882 // if any one of the six lattice constants is missing, try to get
883 // one from the workspace
884 if ((m_a == EMPTY_DBL() || m_b == EMPTY_DBL() || m_c == EMPTY_DBL() || m_alpha == EMPTY_DBL() ||
885 m_beta == EMPTY_DBL() || m_gamma == EMPTY_DBL()) &&
886 (pws->sample().hasOrientedLattice())) {
887 OrientedLattice lattice = pws->mutableSample().getOrientedLattice();
888 m_a = lattice.a();
889 m_b = lattice.b();
890 m_c = lattice.c();
891 m_alpha = lattice.alpha();
892 m_beta = lattice.beta();
893 m_gamma = lattice.gamma();
894 }
895}
896
904 auto calcUB_alg = createChildAlgorithm("CalculateUMatrix", -1, -1, false);
905 calcUB_alg->setLogging(LOGCHILDALG);
906 calcUB_alg->setProperty("PeaksWorkspace", pws);
907 calcUB_alg->setProperty("a", m_a);
908 calcUB_alg->setProperty("b", m_b);
909 calcUB_alg->setProperty("c", m_c);
910 calcUB_alg->setProperty("alpha", m_alpha);
911 calcUB_alg->setProperty("beta", m_beta);
912 calcUB_alg->setProperty("gamma", m_gamma);
913 calcUB_alg->executeAsChildAlg();
914
915 double tol = getProperty("Tolerance");
916
917 // Since UB is updated, we need to redo the indexation
918 auto idxpks_alg = createChildAlgorithm("IndexPeaks", -1, -1, false);
919 idxpks_alg->setLogging(LOGCHILDALG);
920 idxpks_alg->setProperty("PeaksWorkspace", pws);
921 idxpks_alg->setProperty("RoundHKLs", true); // using default
922 idxpks_alg->setProperty("Tolerance", tol); // values
923 idxpks_alg->executeAsChildAlg();
924}
925
933 auto fltpk_alg = createChildAlgorithm("FilterPeaks");
934 fltpk_alg->setLogging(LOGCHILDALG);
935 fltpk_alg->setProperty("InputWorkspace", pws);
936 fltpk_alg->setProperty("FilterVariable", "h^2+k^2+l^2");
937 fltpk_alg->setProperty("Operator", ">");
938 fltpk_alg->setProperty("FilterValue", 0.0);
939 fltpk_alg->setProperty("OutputWorkspace", "pws_filtered");
940 fltpk_alg->executeAsChildAlg();
941
942 IPeaksWorkspace_sptr outWS = fltpk_alg->getProperty("OutputWorkspace");
943 return outWS;
944}
945
953 std::vector<double> tofs;
954
955 for (int i = 0; i < pws->getNumberPeaks(); ++i) {
956 tofs.emplace_back(pws->getPeak(i).getTOF());
957 }
958
959 return tofs;
960}
961
968 auto peaksWorkspace = std::dynamic_pointer_cast<DataObjects::PeaksWorkspace>(pws);
969 if (!peaksWorkspace)
970 throw std::invalid_argument("a PeaksWorkspace is required to retrieve bank names");
971 int npeaks = static_cast<int>(pws->getNumberPeaks());
972 for (int i = 0; i < npeaks; ++i) {
973 std::string bname = peaksWorkspace->getPeak(i).getBankName();
974 if (bname != "None")
975 m_BankNames.insert(bname);
976 }
977}
978
988 const std::string &bankname,
989 const std::string &outputwsn) {
990 auto fltpk_alg = createChildAlgorithm("FilterPeaks");
991 fltpk_alg->setLogging(LOGCHILDALG);
992 fltpk_alg->setProperty("InputWorkspace", pws);
993 fltpk_alg->setProperty("BankName", bankname);
994 fltpk_alg->setProperty("Criterion", "=");
995 fltpk_alg->setProperty("OutputWorkspace", outputwsn);
996 fltpk_alg->executeAsChildAlg();
997
998 IPeaksWorkspace_sptr outWS = fltpk_alg->getProperty("OutputWorkspace");
999 return outWS;
1000}
1001
1010 int npeaks = pws->getNumberPeaks();
1011
1012 // prepare workspace to store qSample as Histogram1D
1013 MatrixWorkspace_sptr mws = std::dynamic_pointer_cast<MatrixWorkspace>(
1014 WorkspaceFactory::Instance().create("Workspace2D", // use workspace 2D to mock a histogram
1015 1, // one vector
1016 3 * npeaks, // X :: anything is fine
1017 3 * npeaks)); // Y :: flattened Q vector
1018 auto &spectrum = mws->getSpectrum(0);
1019 auto &xvector = spectrum.mutableX();
1020 auto &yvector = spectrum.mutableY();
1021 auto &evector = spectrum.mutableE();
1022
1023 // quick check to see what kind of weighting we can use
1024 double totalSigmaInt = 0.0;
1025 for (int i = 0; i < npeaks; ++i) {
1026 totalSigmaInt += pws->getPeak(i).getSigmaIntensity();
1027 }
1028 double totalInt = 0.0;
1029 for (int i = 0; i < npeaks; ++i) {
1030 totalInt += pws->getPeak(i).getIntensity();
1031 }
1032 double totalCnt = 0.0;
1033 for (int i = 0; i < npeaks; ++i) {
1034 totalCnt += pws->getPeak(i).getBinCount();
1035 }
1036
1037 // directly compute qsample from UBmatrix and HKL
1038 auto ubmatrix = pws->sample().getOrientedLattice().getUB();
1039 for (int i = 0; i < npeaks; ++i) {
1040
1041 V3D qv = ubmatrix * pws->getPeak(i).getIntHKL();
1042 qv *= 2 * PI;
1043 // qv = qv / qv.norm();
1044 double wgt = 1.0;
1045 if (totalSigmaInt > 0.0) {
1046 wgt = 1.0 / pws->getPeak(i).getSigmaIntensity();
1047 } else if (totalInt > 0.0) {
1048 wgt = 1.0 / pws->getPeak(i).getIntensity();
1049 } else if (totalCnt > 0.0) {
1050 wgt = 1.0 / pws->getPeak(i).getBinCount();
1051 }
1052 // make 1dhist
1053 for (int j = 0; j < 3; ++j) {
1054 xvector[i * 3 + j] = i * 3 + j;
1055 yvector[i * 3 + j] = qv[j];
1056 evector[i * 3 + j] = wgt;
1057 }
1058 }
1059
1060 return mws;
1061}
1062
1077void SCDCalibratePanels2::adjustComponent(double dx, double dy, double dz, double drx, double dry, double drz,
1078 double scalex, double scaley, const std::string &cmptName,
1079 IPeaksWorkspace_sptr &pws) {
1080 // translation
1081 auto mv_alg = createChildAlgorithm("MoveInstrumentComponent", -1, -1, false);
1082 mv_alg->setLogging(LOGCHILDALG);
1083 mv_alg->setProperty<Workspace_sptr>("Workspace", pws);
1084 mv_alg->setProperty("ComponentName", cmptName);
1085 mv_alg->setProperty("X", dx);
1086 mv_alg->setProperty("Y", dy);
1087 mv_alg->setProperty("Z", dz);
1088 mv_alg->setProperty("RelativePosition", true);
1089 mv_alg->executeAsChildAlg();
1090
1091 // rotation
1092 auto rot_alg = createChildAlgorithm("RotateInstrumentComponent", -1, -1, false);
1093 rot_alg->setLogging(LOGCHILDALG);
1094 // - x-axis
1095 rot_alg->setProperty<Workspace_sptr>("Workspace", pws);
1096 rot_alg->setProperty("ComponentName", cmptName);
1097 rot_alg->setProperty("X", 1.0);
1098 rot_alg->setProperty("Y", 0.0);
1099 rot_alg->setProperty("Z", 0.0);
1100 rot_alg->setProperty("Angle", drx);
1101 rot_alg->setProperty("RelativeRotation", true);
1102 rot_alg->executeAsChildAlg();
1103 // - y-axis
1104 rot_alg->setProperty<Workspace_sptr>("Workspace", pws);
1105 rot_alg->setProperty("ComponentName", cmptName);
1106 rot_alg->setProperty("X", 0.0);
1107 rot_alg->setProperty("Y", 1.0);
1108 rot_alg->setProperty("Z", 0.0);
1109 rot_alg->setProperty("Angle", dry);
1110 rot_alg->setProperty("RelativeRotation", true);
1111 rot_alg->executeAsChildAlg();
1112 // - z-axis
1113 rot_alg->setProperty<Workspace_sptr>("Workspace", pws);
1114 rot_alg->setProperty("ComponentName", cmptName);
1115 rot_alg->setProperty("X", 0.0);
1116 rot_alg->setProperty("Y", 0.0);
1117 rot_alg->setProperty("Z", 1.0);
1118 rot_alg->setProperty("Angle", drz);
1119 rot_alg->setProperty("RelativeRotation", true);
1120 rot_alg->executeAsChildAlg();
1121
1122 // scale detector size
1124 auto resizeAlg = createChildAlgorithm("ResizeRectangularDetector", -1, -1, false);
1125 resizeAlg->initialize();
1126 resizeAlg->setProperty("Workspace", pws);
1127 resizeAlg->setProperty("ComponentName", cmptName);
1128 resizeAlg->setProperty("ScaleX", scalex);
1129 resizeAlg->setProperty("ScaleY", scaley);
1130 resizeAlg->execute();
1131
1132 g_log.notice() << "Resize " << cmptName << " by (absolute) " << scalex << ", " << scaley << "\n";
1133 }
1134}
1135
1143ITableWorkspace_sptr SCDCalibratePanels2::generateCalibrationTable(std::shared_ptr<Geometry::Instrument> &instrument,
1145 g_log.notice() << "Generate a TableWorkspace to store calibration results.\n";
1146
1147 // Create table workspace
1148 ITableWorkspace_sptr itablews = WorkspaceFactory::Instance().createTable();
1149 for (size_t i = 0; i < calibrationTableColumnNames.size(); ++i)
1150 itablews->addColumn(calibrationTableColumnTypes[i], calibrationTableColumnNames[i]);
1151
1152 // The first row is always the source
1153 IComponent_const_sptr source = instrument->getSource();
1154 V3D sourceRelPos = source->getRelativePos();
1155 Mantid::API::TableRow sourceRow = itablews->appendRow();
1156 // NOTE: source should not have any rotation, so we pass a zero
1157 // rotation with a fixed axis
1158 sourceRow << instrument->getSource()->getName() << sourceRelPos.X() << sourceRelPos.Y() << sourceRelPos.Z() << 1.0
1159 << 0.0 << 0.0 << 0.0 << 0.0 << 0.0;
1160
1161 // Loop through banks and set row values
1162 for (auto bankName : m_BankNames) {
1163 // CORELLLI instrument has one extra layer that pack tubes into
1164 // banks, which is what we need here
1165 if (instrument->getName().compare("CORELLI") == 0)
1166 bankName.append("/sixteenpack");
1167
1168 std::shared_ptr<const IComponent> bank = instrument->getComponentByName(bankName);
1169
1170 Quat relRot = bank->getRelativeRot();
1171 V3D pos1 = bank->getRelativePos();
1172
1173 // Calculate cosines using relRot
1174 double deg, xAxis, yAxis, zAxis;
1175 relRot.getAngleAxis(deg, xAxis, yAxis, zAxis);
1176
1177 // Detector scaling
1178 std::pair<double, double> scales = getRectangularDetectorScaleFactors(instrument, bankName, pmap);
1179
1180 // Append a new row
1181 Mantid::API::TableRow bankRow = itablews->appendRow();
1182 // Row and positions
1183 bankRow << bankName << pos1.X() << pos1.Y() << pos1.Z() << xAxis << yAxis << zAxis << deg << scales.first
1184 << scales.second;
1185 }
1186
1187 g_log.notice() << "finished generating tables\n";
1188 setProperty("OutputWorkspace", itablews);
1189
1190 return itablews;
1191}
1192
1209void SCDCalibratePanels2::saveXmlFile(const std::string &FileName,
1210 const boost::container::flat_set<std::string> &AllBankNames,
1211 std::shared_ptr<Instrument> &instrument, const Geometry::ParameterMap &pmap) {
1212 g_log.notice() << "Generating xml tree \n";
1213
1214 using boost::property_tree::ptree;
1215 ptree root;
1216 ptree parafile;
1217
1218 // configure root node
1219 parafile.put("<xmlattr>.instrument", instrument->getName());
1220 parafile.put("<xmlattr>.valid-from", pmap.instrumentMetadata().validFromDate().toISO8601String());
1221
1222 // get L1 info for source
1223 ptree src;
1224 ptree src_dx, src_dy, src_dz;
1225 ptree src_dx_val, src_dy_val, src_dz_val;
1226 // -- get positional data from source
1227 IComponent_const_sptr source = instrument->getSource();
1228 V3D sourceRelPos = source->getRelativePos();
1229 // -- add data to node
1230 src_dx_val.put("<xmlattr>.val", sourceRelPos.X());
1231 src_dy_val.put("<xmlattr>.val", sourceRelPos.Y());
1232 src_dz_val.put("<xmlattr>.val", sourceRelPos.Z());
1233 src_dx.put("<xmlattr>.name", "x");
1234 src_dy.put("<xmlattr>.name", "y");
1235 src_dz.put("<xmlattr>.name", "z");
1236 src.put("<xmlattr>.name", source->getName());
1237
1238 src_dx.add_child("value", src_dx_val);
1239 src_dy.add_child("value", src_dy_val);
1240 src_dz.add_child("value", src_dz_val);
1241 src.add_child("parameter", src_dx);
1242 src.add_child("parameter", src_dy);
1243 src.add_child("parameter", src_dz);
1244
1245 parafile.add_child("component-link", src);
1246
1247 // add node for T0
1248 // -- property_root is a dumping group for property type values that are not tied to particular
1249 // component (i.e. virtual properties)
1250 ptree property_root;
1251 property_root.put("<xmlattr>.name", instrument->getName());
1252 ptree tof0;
1253 ptree tof0_val;
1254 tof0.put("<xmlattr>.name", "T0");
1255 tof0_val.put("<xmlattr>.val", m_T0);
1256 tof0.add_child("value", tof0_val);
1257 property_root.add_child("parameter", tof0);
1258 parafile.add_child("component-link", property_root);
1259
1260 // save sample position as a standalone component-link
1261 ptree samplePos;
1262 ptree samplePos_dx, samplePos_dy, samplePos_dz;
1263 ptree samplePos_dx_val, samplePos_dy_val, samplePos_dz_val;
1264 // -- get positional data from sample
1265 std::shared_ptr<const IComponent> sp = instrument->getComponentByName("sample-position");
1266 V3D sppos = sp->getRelativePos();
1267 samplePos_dx_val.put("<xmlattr>.val", sppos.X());
1268 samplePos_dy_val.put("<xmlattr>.val", sppos.Y());
1269 samplePos_dz_val.put("<xmlattr>.val", sppos.Z());
1270 samplePos_dx.put("<xmlattr>.name", "x");
1271 samplePos_dy.put("<xmlattr>.name", "y");
1272 samplePos_dz.put("<xmlattr>.name", "z");
1273 samplePos.put("<xmlattr>.name", "sample-position");
1274
1275 samplePos_dx.add_child("value", samplePos_dx_val);
1276 samplePos_dy.add_child("value", samplePos_dy_val);
1277 samplePos_dz.add_child("value", samplePos_dz_val);
1278 samplePos.add_child("parameter", samplePos_dx);
1279 samplePos.add_child("parameter", samplePos_dy);
1280 samplePos.add_child("parameter", samplePos_dz);
1281
1282 parafile.add_child("component-link", samplePos);
1283
1284 // configure and add each bank
1285 for (auto bankName : AllBankNames) {
1286 // Prepare data for node
1287 if (instrument->getName().compare("CORELLI") == 0)
1288 bankName.append("/sixteenpack");
1289
1290 std::shared_ptr<const IComponent> bank = instrument->getComponentByName(bankName);
1291 auto bankFullName = bank->getFullName();
1292
1293 Quat relRot = bank->getRelativeRot();
1294 std::vector<double> relRotAngles = relRot.getEulerAngles("XYZ");
1295 V3D pos1 = bank->getRelativePos();
1296 std::pair<double, double> scales = getRectangularDetectorScaleFactors(instrument, bankName, pmap);
1297
1298 // prepare node
1299 ptree bank_root;
1300 ptree bank_dx, bank_dy, bank_dz;
1301 ptree bank_dx_val, bank_dy_val, bank_dz_val;
1302 ptree bank_drotx, bank_droty, bank_drotz;
1303 ptree bank_drotx_val, bank_droty_val, bank_drotz_val;
1304 ptree bank_sx, bank_sy;
1305 ptree bank_sx_val, bank_sy_val;
1306
1307 // add data to node
1308 bank_dx_val.put("<xmlattr>.val", pos1.X());
1309 bank_dy_val.put("<xmlattr>.val", pos1.Y());
1310 bank_dz_val.put("<xmlattr>.val", pos1.Z());
1311 bank_dx.put("<xmlattr>.name", "x");
1312 bank_dy.put("<xmlattr>.name", "y");
1313 bank_dz.put("<xmlattr>.name", "z");
1314
1315 bank_drotx_val.put("<xmlattr>.val", relRotAngles[0]);
1316 bank_droty_val.put("<xmlattr>.val", relRotAngles[1]);
1317 bank_drotz_val.put("<xmlattr>.val", relRotAngles[2]);
1318 bank_drotx.put("<xmlattr>.name", "rotx");
1319 bank_droty.put("<xmlattr>.name", "roty");
1320 bank_drotz.put("<xmlattr>.name", "rotz");
1321
1322 bank_sx_val.put("<xmlattr>.val", scales.first);
1323 bank_sy_val.put("<xmlattr>.val", scales.second);
1324 bank_sx.put("<xmlattr>.name", "scalex");
1325 bank_sy.put("<xmlattr>.name", "scaley");
1326
1327 bank_root.put("<xmlattr>.name", bankFullName); // avoid future expensive search in the instrument's tree
1328
1329 // configure structure
1330 bank_dx.add_child("value", bank_dx_val);
1331 bank_dy.add_child("value", bank_dy_val);
1332 bank_dz.add_child("value", bank_dz_val);
1333
1334 bank_drotx.add_child("value", bank_drotx_val);
1335 bank_droty.add_child("value", bank_droty_val);
1336 bank_drotz.add_child("value", bank_drotz_val);
1337
1338 bank_sx.add_child("value", bank_sx_val);
1339 bank_sy.add_child("value", bank_sy_val);
1340
1341 bank_root.add_child("parameter", bank_drotx);
1342
1343 bank_root.add_child("parameter", bank_droty);
1344 bank_root.add_child("parameter", bank_drotz);
1345 bank_root.add_child("parameter", bank_dx);
1346 bank_root.add_child("parameter", bank_dy);
1347 bank_root.add_child("parameter", bank_dz);
1348 bank_root.add_child("parameter", bank_sx);
1349 bank_root.add_child("parameter", bank_sy);
1350
1351 parafile.add_child("component-link", bank_root);
1352 }
1353
1354 // give everything to root
1355 root.add_child("parameter-file", parafile);
1356 // write the xml tree to disk
1357 g_log.notice() << "\tSaving parameter file as " << FileName << "\n";
1358 boost::property_tree::write_xml(FileName, root, std::locale(),
1359 boost::property_tree::xml_writer_settings<std::string>(' ', 2));
1360}
1361
1372void SCDCalibratePanels2::saveIsawDetCal(const std::string &filename,
1373 boost::container::flat_set<std::string> &AllBankName,
1374 std::shared_ptr<Instrument> &instrument, double T0) {
1375 g_log.notice() << "Saving DetCal file in " << filename << "\n";
1376
1377 bool tuneSamplePos = getProperty("TuneSamplePosition");
1378 if (tuneSamplePos) {
1379 g_log.warning() << "!!!WARNING!!!\n"
1380 << "DetCal format cannot retain sample position info, therefore the calibrated "
1381 << "sample position will be lost if DetCal format is the only output!\n";
1382 }
1383
1384 // create a workspace to pass to SaveIsawDetCal
1385 const size_t number_spectra = instrument->getNumberDetectors();
1386 Workspace2D_sptr wksp =
1387 std::dynamic_pointer_cast<Workspace2D>(WorkspaceFactory::Instance().create("Workspace2D", number_spectra, 2, 1));
1388 wksp->setInstrument(instrument);
1389 wksp->rebuildSpectraMapping(true /* include monitors */);
1390
1391 // convert the bank names into a vector
1392 std::vector<std::string> banknames(AllBankName.begin(), AllBankName.end());
1393
1394 // call SaveIsawDetCal
1395 auto alg = createChildAlgorithm("SaveIsawDetCal");
1396 alg->setProperty("InputWorkspace", wksp);
1397 alg->setProperty("Filename", filename);
1398 alg->setProperty("TimeOffset", T0);
1399 alg->setProperty("BankNames", banknames);
1400 alg->executeAsChildAlg();
1401}
1402
1409void SCDCalibratePanels2::saveCalibrationTable(const std::string &FileName,
1411 auto alg = createChildAlgorithm("SaveAscii");
1412 alg->setProperty("InputWorkspace", tws);
1413 alg->setProperty("Filename", FileName);
1414 alg->setPropertyValue("CommentIndicator", "#");
1415 alg->setPropertyValue("Separator", "CSV");
1416 alg->setProperty("ColumnHeader", true);
1417 alg->setProperty("AppendToFile", false);
1418 alg->executeAsChildAlg();
1419}
1420
1428 Mantid::API::IPeaksWorkspace_sptr pws_original) {
1429 g_log.notice() << "START of profiling objective func along L1\n";
1430
1431 // control option
1432 bool verbose = getProperty("VerboseOutput");
1433 if (verbose) {
1434 // header to console
1435 g_log.notice() << "deltaL1 -- residual\n";
1436 }
1437
1438 // prepare container for profile information
1439 std::ostringstream msgrst;
1440 msgrst.precision(12);
1441 msgrst << "dL1\tresidual\n";
1442
1443 // setting up as if we are doing optimization
1444 auto objf = std::make_shared<SCDCalibratePanels2ObjFunc>();
1445 // NOTE: always use the original pws to get the tofs
1446 std::vector<double> tofs = captureTOF(pws_original);
1447 objf->setPeakWorkspace(pws, "moderator", tofs, m_waveFromUB);
1448
1449 // call the obj to perform evaluation
1450 const int n_peaks = pws->getNumberPeaks();
1451 std::unique_ptr<double[]> target(new double[n_peaks * 3]);
1452
1453 // generate the target
1454 auto ubmatrix = pws->sample().getOrientedLattice().getUB();
1455 for (int i = 0; i < n_peaks; ++i) {
1456 V3D qv = ubmatrix * pws->getPeak(i).getIntHKL();
1457 qv *= 2 * PI;
1458 for (int j = 0; j < 3; ++j) {
1459 target[i * 3 + j] = qv[j];
1460 }
1461 }
1462
1463 const double xValues[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // xValues is not used
1464
1465 // scan from -4cm to 4cm along dL1 where the minimum is supposed to be at 0 for null
1466 // case with instrument at the engineering position
1467 double deltaL1 = -4e-2;
1468 while (deltaL1 < 4e-2) {
1469 std::unique_ptr<double[]> out(new double[n_peaks * 3]);
1470 objf->setParameter("DeltaZ", deltaL1);
1471 objf->setParameter("DeltaT0", 0.0); // need to set dT0 to 0.0 if we are not cali it
1472 objf->function1D(out.get(), xValues, 1);
1473
1474 // calc residual
1475 double residual = 0.0;
1476 for (int i = 0; i < n_peaks * 3; ++i) {
1477 residual += (out[i] - target[i]) * (out[i] - target[i]);
1478 }
1479 residual = std::sqrt(residual) / (n_peaks - 1); // only 1 deg of freedom here
1480 // log rst
1481 msgrst << deltaL1 << "\t" << residual << "\n";
1482
1483 if (verbose) {
1484 g_log.notice() << deltaL1 << " -- " << residual << "\n";
1485 }
1486
1487 // increment
1488 deltaL1 += 1e-4; // 0.1mm step size
1489 }
1490
1491 // output to file
1492 auto filenamebase = std::filesystem::temp_directory_path() / "profileSCDCalibratePanels2_L1.csv";
1493 std::ofstream profL1File;
1494 profL1File.open(filenamebase.string());
1495 profL1File << msgrst.str();
1496 profL1File.close();
1497 g_log.notice() << "Profile data is saved at:\n"
1498 << filenamebase << "\n"
1499 << "END of profiling objective func along L1\n";
1500}
1501
1509 const Mantid::API::IPeaksWorkspace_sptr &pws_original) {
1510 g_log.notice() << "START of profiling all banks along six degree of freedom\n";
1511
1512 // control option
1513 bool verbose = getProperty("VerboseOutput");
1514 if (verbose) {
1515 // header to console
1516 g_log.notice() << "--bankname: residual\n";
1517 }
1518 // Use OPENMP to speed up the profiling
1520 for (int bankIndex = 0; bankIndex < static_cast<int>(m_BankNames.size()); ++bankIndex) {
1522 // prepare local copies to work with
1523 const std::string bankname = *std::next(m_BankNames.begin(), bankIndex);
1524 const std::string pwsBankiName = "_pws_" + bankname;
1525
1526 //-- step 0: extract peaks that lies on the current bank
1527 IPeaksWorkspace_sptr pwsBanki = selectPeaksByBankName(pws, bankname, pwsBankiName);
1528 // get tofs from the original subset of pws
1529 IPeaksWorkspace_sptr pwsBanki_original = selectPeaksByBankName(pws_original, bankname, pwsBankiName);
1530 std::vector<double> tofs = captureTOF(pwsBanki_original);
1531
1532 // Do not attempt correct panels with less than 6 peaks as the system will
1533 // be under-determined
1534 int nBankPeaks = pwsBanki->getNumberPeaks();
1535 if (nBankPeaks < MINIMUM_PEAKS_PER_BANK) {
1536 // use ostringstream to prevent OPENMP breaks log info
1537 std::ostringstream msg_npeakCheckFail;
1538 msg_npeakCheckFail << "-- Cannot profile Bank " << bankname << " have only " << nBankPeaks << " (<"
1539 << MINIMUM_PEAKS_PER_BANK << ") Peaks, skipping\n";
1540 g_log.notice() << msg_npeakCheckFail.str();
1541 continue;
1542 }
1543
1544 //
1546 std::ostringstream msgrst;
1547 msgrst.precision(12);
1548 msgrst << "dx\tdy\tdz\ttheta\tphi\trogang\tresidual\n";
1549 //
1550 auto objf = std::make_shared<SCDCalibratePanels2ObjFunc>();
1551 objf->setPeakWorkspace(pwsBanki, bankname, tofs, m_waveFromUB);
1552 //
1553 const int n_peaks = pwsBanki->getNumberPeaks();
1554 std::unique_ptr<double[]> target(new double[n_peaks * 3]);
1555 // generate the target
1556 auto ubmatrix = pwsBanki->sample().getOrientedLattice().getUB();
1557 for (int i = 0; i < n_peaks; ++i) {
1558 V3D qv = ubmatrix * pwsBanki->getPeak(i).getIntHKL();
1559 qv *= 2 * PI;
1560 for (int j = 0; j < 3; ++j) {
1561 target[i * 3 + j] = qv[j];
1562 }
1563 }
1564
1565 const double xValues[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // xValues is not used
1566
1567 // NOTE: very expensive scan of the parameter space
1568 for (double dx = -1e-2; dx < 1e-2; dx += 2e-2 / 20.0) {
1569 // deltaX: meter
1570 for (double dy = -1e-2; dy < 1e-2; dy += 2e-2 / 20.0) {
1571 // deltaY: meter
1572 for (double dz = -1e-2; dz < 1e-2; dz += 2e-2 / 20.0) {
1573 // deltaZ: meter
1574 for (double theta = 0.0; theta < PI; theta += PI / 20.0) {
1575 // theta: rad
1576 for (double phi = 0.0; phi < 2 * PI; phi += 2 * PI / 20.0) {
1577 // phi: rad
1578 for (double ang = -5.0; ang < 5.0; ang += 5.0 / 20.0) {
1579 // ang: degrees
1580 // configure the objfunc
1581 std::unique_ptr<double[]> out(new double[n_peaks * 3]);
1582 objf->setParameter("DeltaX", dx);
1583 objf->setParameter("DeltaY", dy);
1584 objf->setParameter("DeltaZ", dz);
1585 objf->setParameter("Theta", theta);
1586 objf->setParameter("Phi", phi);
1587 objf->setParameter("DeltaRotationAngle", ang);
1588 objf->setParameter("DeltaT0", 0.0); // need to set dT0 to 0.0 if we are not cali it
1589 objf->function1D(out.get(), xValues, 1);
1590 // calc residual
1591 double residual = 0.0;
1592 for (int i = 0; i < n_peaks * 3; ++i) {
1593 residual += (out[i] - target[i]) * (out[i] - target[i]);
1594 }
1595 residual = std::sqrt(residual) / (n_peaks - 6);
1596 // record
1597 msgrst << dx << "\t" << dy << "\t" << dz << "\t" << theta << "\t" << phi << "\t" << ang << "\t"
1598 << residual << "\n";
1599
1600 if (verbose) {
1601 g_log.notice() << "--" << bankname << ": " << residual << "\n";
1602 }
1603 }
1604 }
1605 }
1606 }
1607 }
1608 }
1609
1610 // output to file
1611 const std::string csvname = "profileSCDCalibratePanels2_" + bankname + ".csv";
1612 auto filenamebase = std::filesystem::temp_directory_path() / csvname;
1613 std::ofstream profBankFile;
1614 profBankFile.open(filenamebase.string());
1615 profBankFile << msgrst.str();
1616 profBankFile.close();
1617
1618 // notify at the terminal
1619 std::ostringstream msg;
1620 msg << "Profile of " << bankname << " is saved at:\n"
1621 << filenamebase << "\n"
1622 << "END of profiling objective func for " << bankname << "\n";
1623 g_log.notice() << msg.str();
1625 }
1627}
1628
1636 Mantid::API::IPeaksWorkspace_sptr pws_original) {
1637 g_log.notice() << "START of profiling objective func along T0\n";
1638
1639 // control option
1640 bool verbose = getProperty("VerboseOutput");
1641 if (verbose) {
1642 // print the header to console
1643 g_log.notice() << "deltaT0 -- residual\n";
1644 }
1645
1646 // prepare container for profile information
1647 std::ostringstream msgrst;
1648 msgrst.precision(12);
1649 msgrst << "dT0\tresidual\n";
1650
1651 // setting up as if we are doing optimization
1652 auto objf = std::make_shared<SCDCalibratePanels2ObjFunc>();
1653 // NOTE: always use the original pws to get the tofs
1654 std::vector<double> tofs = captureTOF(pws_original);
1655 objf->setPeakWorkspace(pws, "none", tofs, m_waveFromUB);
1656
1657 // generate the target
1658 const int n_peaks = pws->getNumberPeaks();
1659 std::unique_ptr<double[]> target(new double[n_peaks * 3]);
1660 auto ubmatrix = pws->sample().getOrientedLattice().getUB();
1661 for (int i = 0; i < n_peaks; ++i) {
1662 V3D qv = ubmatrix * pws->getPeak(i).getIntHKL();
1663 qv *= 2 * PI;
1664 for (int j = 0; j < 3; ++j) {
1665 target[i * 3 + j] = qv[j];
1666 }
1667 }
1668
1669 const double xValues[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // xValues is not used
1670
1671 // scan from -10 ~ 10 ms along dT0
1672 double deltaT0 = -10;
1673 while (deltaT0 < 10) {
1674 std::unique_ptr<double[]> out(new double[n_peaks * 3]);
1675 objf->setParameter("DeltaT0", deltaT0);
1676 objf->function1D(out.get(), xValues, 1);
1677
1678 // calc residual
1679 double residual = 0.0;
1680 for (int i = 0; i < n_peaks * 3; ++i) {
1681 residual += (out[i] - target[i]) * (out[i] - target[i]);
1682 }
1683 residual = std::sqrt(residual) / (n_peaks - 1); // only 1 deg of freedom here
1684 // log rst
1685 msgrst << deltaT0 << "\t" << residual << "\n";
1686
1687 if (verbose) {
1688 g_log.notice() << deltaT0 << " -- " << residual << "\n";
1689 }
1690
1691 // increment
1692 deltaT0 += 0.01; // 20/2000.0
1693 }
1694
1695 // output to file
1696 auto filenamebase = std::filesystem::temp_directory_path() / "profileSCDCalibratePanels2_T0.csv";
1697 std::ofstream profL1File;
1698 profL1File.open(filenamebase.string());
1699 profL1File << msgrst.str();
1700 profL1File.close();
1701 g_log.notice() << "Profile data is saved at:\n"
1702 << filenamebase << "\n"
1703 << "END of profiling objective func along T0\n";
1704}
1705
1713 Mantid::API::IPeaksWorkspace_sptr pws_original) {
1714 g_log.notice() << "START of profiling objective func along L1 and T0\n";
1715
1716 // control option
1717 bool verbose = getProperty("VerboseOutput");
1718 if (verbose) {
1719 // print the header to console
1720 g_log.notice() << "deltaL1 -- deltaT0 -- residual\n";
1721 }
1722
1723 // prepare container for profile information
1724 std::ostringstream msgrst;
1725 msgrst.precision(12);
1726 msgrst << "dL1\tdT0\tresidual\n";
1727
1728 // setting up as if we are doing optimization
1729 auto objf = std::make_shared<SCDCalibratePanels2ObjFunc>();
1730 // NOTE: always use the original pws to get the tofs
1731 std::vector<double> tofs = captureTOF(pws_original);
1732 objf->setPeakWorkspace(pws, "moderator", tofs, m_waveFromUB);
1733
1734 // generate the target
1735 const int n_peaks = pws->getNumberPeaks();
1736 std::unique_ptr<double[]> target(new double[n_peaks * 3]);
1737 auto ubmatrix = pws->sample().getOrientedLattice().getUB();
1738 for (int i = 0; i < n_peaks; ++i) {
1739 V3D qv = ubmatrix * pws->getPeak(i).getIntHKL();
1740 qv *= 2 * PI;
1741 for (int j = 0; j < 3; ++j) {
1742 target[i * 3 + j] = qv[j];
1743 }
1744 }
1745
1746 const double xValues[7] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0}; // xValues is not used
1747
1748 // profile begin
1749 for (double deltaL1 = -4e-2; deltaL1 < 4e-2; deltaL1 += 1e-4) {
1750 for (double deltaT0 = -4.0; deltaT0 < 4.0; deltaT0 += 1e-2) {
1751 std::unique_ptr<double[]> out(new double[n_peaks * 3]);
1752 objf->setParameter("DeltaZ", deltaL1);
1753 objf->setParameter("DeltaT0", deltaT0);
1754 objf->function1D(out.get(), xValues, 1);
1755
1756 // calc residual
1757 double residual = 0.0;
1758 for (int i = 0; i < n_peaks * 3; ++i) {
1759 residual += (out[i] - target[i]) * (out[i] - target[i]);
1760 }
1761 residual = std::sqrt(residual) / (n_peaks - 2); // only 1 deg of freedom here
1762
1763 if (verbose) {
1764 g_log.notice() << deltaL1 << " -- " << deltaT0 << " -- " << residual << "\n";
1765 }
1766 // log rst
1767 msgrst << deltaL1 << "\t" << deltaT0 << "\t" << residual << "\n";
1768 }
1769 }
1770
1771 // output to file
1772 auto filenamebase = std::filesystem::temp_directory_path() / "profileSCDCalibratePanels2_L1T0.csv";
1773 std::ofstream profL1File;
1774 profL1File.open(filenamebase.string());
1775 profL1File << msgrst.str();
1776 profL1File.close();
1777
1778 // log
1779 g_log.notice() << "Profile data is saved at:\n"
1780 << filenamebase << "\n"
1781 << "END of profiling objective func along L1 and T0\n";
1782}
1783
1796std::pair<double, double>
1797SCDCalibratePanels2::getRectangularDetectorScaleFactors(std::shared_ptr<Geometry::Instrument> &instrument,
1798 const std::string &bankname,
1800
1801 std::pair<double, double> scales{1.0, 1.0};
1802
1803 // docalibsize, sizesearchradius, fixdetxyratio
1804 Geometry::IComponent_const_sptr comp = instrument->getComponentByName(bankname);
1805 std::shared_ptr<const Geometry::RectangularDetector> rectDet =
1806 std::dynamic_pointer_cast<const Geometry::RectangularDetector>(comp);
1807
1808 if (rectDet) {
1809 // retrieve the (scalex, scaley) stored in the workspace for this bank/component
1810 auto scalexparams = pmap.getDouble(rectDet->getName(), "scalex");
1811 auto scaleyparams = pmap.getDouble(rectDet->getName(), "scaley");
1812 if (!scalexparams.empty())
1813 scales.first = scalexparams[0];
1814 if (!scaleyparams.empty())
1815 scales.second = scaleyparams[0];
1816 }
1817
1818 return scales;
1819}
1820
1821} // namespace Mantid::Crystal
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:542
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
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:423
@ OptionalSave
to specify a file to write to but an empty string is
TableRow represents a row in a TableWorkspace.
Definition TableRow.h:39
A property class for workspaces.
SCDCalibratePanels2 : Using input peakworkspace with indexation results to calibrate each individual ...
Mantid::API::IPeaksWorkspace_sptr removeUnindexedPeaks(const Mantid::API::IPeaksWorkspace_sptr &pws)
Remove unindexed peaks from workspace.
void adjustComponent(double dx, double dy, double dz, double drx, double dry, double drz, double scalex, double scaley, const std::string &cmptName, Mantid::API::IPeaksWorkspace_sptr &pws)
Helper functions for adjusting components.
void saveIsawDetCal(const std::string &filename, boost::container::flat_set< std::string > &AllBankName, std::shared_ptr< Geometry::Instrument > &instrument, double T0)
Save to ISAW type det calibration output for backward compatiblity.
Mantid::API::ITableWorkspace_sptr generateCalibrationTable(std::shared_ptr< Geometry::Instrument > &instrument, const Geometry::ParameterMap &pmap)
Generate a Table workspace to store the calibration results.
void profileBanks(Mantid::API::IPeaksWorkspace_sptr const &pws, const Mantid::API::IPeaksWorkspace_sptr &pws_original)
Profiling obj func along six degree of freedom, which can be very slow.
void optimizeT0(Mantid::API::IPeaksWorkspace_sptr pws, Mantid::API::IPeaksWorkspace_sptr pws_original)
Private function for calibrating T0.
void optimizeL1(Mantid::API::IPeaksWorkspace_sptr pws, Mantid::API::IPeaksWorkspace_sptr pws_original)
Private function for calibrating L1.
double m_a
unique vars for a given instance of calibration
void optimizeSamplePos(Mantid::API::IPeaksWorkspace_sptr pws, Mantid::API::IPeaksWorkspace_sptr pws_original)
Private function for fine tunning sample position.
const std::vector< std::string > calibrationTableColumnTypes
void getBankNames(const Mantid::API::IPeaksWorkspace_sptr &pws)
Private function for getting names of banks to be calibrated.
boost::container::flat_set< std::string > m_BankNames
void updateUBMatrix(const Mantid::API::IPeaksWorkspace_sptr &pws)
Update the UB matrix.
void optimizeBanks(Mantid::API::IPeaksWorkspace_sptr pws, const Mantid::API::IPeaksWorkspace_sptr &pws_original, const bool &docalibsize, const double &sizesearchradius, const bool &fixdetxyratio)
Private function for calibrating banks.
void parseLatticeConstant(const Mantid::API::IPeaksWorkspace_sptr &pws)
Private function dedicated for parsing lattice constant.
const std::vector< std::string > calibrationTableColumnNames
void saveXmlFile(const std::string &FileName, const boost::container::flat_set< std::string > &AllBankNames, std::shared_ptr< Geometry::Instrument > &instrument, const Geometry::ParameterMap &pmap)
Save to xml file for Mantid to load by manual crafting.
void exec() override
Overwrites Algorithm method.
void profileT0(Mantid::API::IPeaksWorkspace_sptr &pws, Mantid::API::IPeaksWorkspace_sptr pws_original)
Profile obj func along T0 axis.
Mantid::API::IPeaksWorkspace_sptr selectPeaksByBankName(const Mantid::API::IPeaksWorkspace_sptr &pws, const std::string &bankname, const std::string &outputwsn)
Helper function for selecting peaks based on given bank name.
std::map< std::string, std::string > validateInputs() override
Private validator for inputs.
std::pair< double, double > getRectangularDetectorScaleFactors(std::shared_ptr< Geometry::Instrument > &instrument, const std::string &bankname, const Geometry::ParameterMap &pmap)
Retrieve "scalex" and "scaley" from a workspace's parameter map if the component is rectangular detec...
bool m_waveFromUB
if true, wavelength is derived from Bragg's law using the UB matrix instead of from the measured TOF;...
void saveCalibrationTable(const std::string &FileName, Mantid::API::ITableWorkspace_sptr const &tws)
Save the calibration table to a CSV file.
Mantid::API::MatrixWorkspace_sptr getIdealQSampleAsHistogram1D(const Mantid::API::IPeaksWorkspace_sptr &pws)
Helper function that calculates the ideal qSample based on integer HKL.
void profileL1(Mantid::API::IPeaksWorkspace_sptr &pws, Mantid::API::IPeaksWorkspace_sptr pws_original)
Profile related functions.
void profileL1T0(Mantid::API::IPeaksWorkspace_sptr &pws, Mantid::API::IPeaksWorkspace_sptr pws_original)
Profile obj func along L1 and T0 axis.
std::vector< double > captureTOF(const Mantid::API::IPeaksWorkspace_sptr &pws)
Cache TOF equivalent to those measured from experiment.
Class to implement UB matrix.
Parameter map iterator typedef.
double alpha() const
Get lattice parameter.
Definition UnitCell.cpp:133
double a(int nd) const
Get lattice parameter a1-a3 as function of index (0-2)
Definition UnitCell.cpp:94
double c() const
Get lattice parameter.
Definition UnitCell.cpp:128
double beta() const
Get lattice parameter.
Definition UnitCell.cpp:138
double b() const
Get lattice parameter.
Definition UnitCell.cpp:123
double gamma() const
Get lattice parameter.
Definition UnitCell.cpp:143
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void notice(const std::string &msg)
Logs at notice level.
Definition Logger.cpp:126
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
Class for quaternions.
Definition Quat.h:39
void getAngleAxis(double &_deg, double &_ax0, double &_ax1, double &ax2) const
Extracts the angle of roatation and axis.
Definition Quat.cpp:135
std::vector< double > getEulerAngles(const std::string &convention) const
Calculate the Euler angles that are equivalent to this Quaternion.
Definition Quat.cpp:729
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 X() const noexcept
Get x.
Definition V3D.h:238
constexpr double Y() const noexcept
Get y.
Definition V3D.h:239
constexpr double Z() const noexcept
Get z.
Definition V3D.h:240
std::shared_ptr< IPeaksWorkspace > IPeaksWorkspace_sptr
shared pointer to Mantid::API::IPeaksWorkspace
std::shared_ptr< ITableWorkspace > ITableWorkspace_sptr
shared pointer to Mantid::API::ITableWorkspace
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< Workspace2D > Workspace2D_sptr
shared pointer to Mantid::DataObjects::Workspace2D
std::unique_ptr< T > create(const P &parent, const IndexArg &indexArg, const HistArg &histArg)
This is the create() method that all the other create() methods call.
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition IComponent.h:165
std::shared_ptr< Instrument > Instrument_sptr
Shared pointer to an instrument object.
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
Generate a tableworkspace to store the calibration results.
adjust instrument component position and orientation
: detector size scale at y-direction
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54