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