Mantid
Loading...
Searching...
No Matches
SCDCalibratePanels2ObjFunc.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
13#include "MantidAPI/Run.h"
14#include "MantidAPI/Sample.h"
19
20#include <boost/math/special_functions/round.hpp>
21#include <cmath>
22
23namespace Mantid::Crystal {
24
25using namespace Mantid::API;
26using namespace Mantid::CurveFitting;
27using namespace Mantid::DataObjects;
28using namespace Mantid::Geometry;
29using namespace Mantid::Kernel;
30
31namespace {
32// static logger
33Logger g_log("SCDCalibratePanels2ObjFunc");
34} // namespace
35
36DECLARE_FUNCTION(SCDCalibratePanels2ObjFunc)
37
38
42 // parameters for translation
43 declareParameter("DeltaX", 0.0, "relative shift along X in meter");
44 declareParameter("DeltaY", 0.0, "relative shift along Y in meter");
45 declareParameter("DeltaZ", 0.0, "relative shift along Z in meter");
46 // parameters for rotation
47 declareParameter("RotX", 0.0, "relative rotation around X in degree");
48 declareParameter("RotY", 0.0, "relative rotation around Y in degree");
49 declareParameter("RotZ", 0.0, "relative rotation around Z in degree");
50 // TOF offset for all peaks
51 // NOTE: need to have a non-zero value here
52 declareParameter("DeltaT0", 0.1, "delta of TOF");
53 // This part is for fine tuning the sample position
54 declareParameter("DeltaSampleX", 0.0, "relative shift of sample position along X.");
55 declareParameter("DeltaSampleY", 0.0, "relative shift of sample position along Y.");
56 declareParameter("DeltaSampleZ", 0.0, "relative shift of sample position along Z.");
57 // Detector size scale factors
58 declareParameter("ScaleX", 1.0, "Scale of detector along X-direction (i.e., width).");
59 declareParameter("ScaleY", 1.0, "Scale of detector along Y-direction (i.e., height).");
60}
61
62void SCDCalibratePanels2ObjFunc::setPeakWorkspace(IPeaksWorkspace_sptr &pws, const std::string &componentName,
63 const std::vector<double> &tofs, bool waveFromUB) {
64 m_pws = pws->clone();
65 m_cmpt = componentName;
66
67 // Special adjustment for CORELLI
68 Instrument_sptr inst = std::const_pointer_cast<Instrument>(m_pws->getInstrument());
69 if (inst->getName().compare("CORELLI") == 0 && m_cmpt != "moderator")
70 // the second check is just to ensure that no accidental passing in
71 // a bank name with sixteenpack already appended
72 if (!m_cmpt.ends_with("/sixteenpack"))
73 m_cmpt.append("/sixteenpack");
74
75 // Get the experimentally measured TOFs
76 m_tofs = tofs;
77
78 // If true, function1D derives wavelength from Bragg's law using the UB
79 // matrix instead of from m_tofs
80 m_waveFromUB = waveFromUB;
81
82 // Set the iteration count
83 n_iter = 0;
84}
85
94void SCDCalibratePanels2ObjFunc::function1D(double *out, const double *xValues, const size_t order) const {
95 // Get the feature vector component (numeric type)
96 //-- delta in translation
97 const double dx = getParameter("DeltaX");
98 const double dy = getParameter("DeltaY");
99 const double dz = getParameter("DeltaZ");
100 //-- delta in rotation
101 const double drx = getParameter("RotX");
102 const double dry = getParameter("RotY");
103 const double drz = getParameter("RotZ");
104 //-- delta in TOF
105 // NOTE: The T0 here is a universal offset for all peaks
106 double dT0 = getParameter("DeltaT0");
107 //-- delta of sample position
108 const double dsx = getParameter("DeltaSampleX");
109 const double dsy = getParameter("DeltaSampleY");
110 const double dsz = getParameter("DeltaSampleZ");
111 //-- scale of the detector size
112 const double scalex = getParameter("ScaleX");
113 const double scaley = getParameter("ScaleY");
114
115 //-- NOTE: given that these components are never used as
116 // one vector, there is no need to construct a
117 // xValues
118 UNUSED_ARG(xValues);
119 UNUSED_ARG(order);
120
121 // -- always working on a copy only
122 IPeaksWorkspace_sptr pws = m_pws->clone();
123
124 // NOTE: when optimizing T0, a none component will be passed in.
125 // -- For Corelli, this will be none/sixteenpack
126 // -- For others, this will be none
127 bool calibrateT0 = (m_cmpt == "none/sixteenpack") || (m_cmpt == "none");
128 // we don't need to move the instrument if we are calibrating T0
129 if (!calibrateT0) {
131
132 // translation
133 pws = moveInstruentComponentBy(dx, dy, dz, m_cmpt, pws);
134
135 // rotation
136 pws = rotateInstrumentComponentBy(drx, dry, drz, m_cmpt, pws);
137 }
138
139 // tweak sample position
140 pws = moveInstruentComponentBy(dsx, dsy, dsz, "sample-position", pws);
141
142 // calculate residual
143 // double residual = 0.0;
144 for (int i = 0; i < pws->getNumberPeaks(); ++i) {
145 Peak pk = Peak(pws->getPeak(i));
146 // update instrument
147 // - this will update the instrument position attached to the peak
148 // - this will update the sample position attached to the peak
149 pk.setInstrument(pws->getInstrument());
150 // update detector ID
152
153 if (m_waveFromUB && pk.getIntHKL() != UNSET_HKL) {
154 // Derive the wavelength from Bragg's law using the UB matrix and the
155 // peak's integer HKL, rather than from the measured TOF. This decouples
156 // the calibration from the TOF-to-wavelength conversion, which matters
157 // for quasi-Laue workflows (where a peak's TOF-derived wavelength can
158 // be unreliable) and is required for pure Laue data, which has no
159 // meaningful per-peak TOF at all.
160 //
161 // NOTE: for a cubic standard (commonly used for calibration), UB = U*B
162 // with B = (1/a)*I, so the target Q = 2*pi*U*B*hkl points along U*hkl
163 // regardless of the lattice constant a -- a only rescales the magnitude,
164 // which is exactly what gets absorbed into the wavelength solved here.
165 // So for cubic samples this calibration is driven purely by the integer
166 // HKL indexing and the sample/goniometer orientation U, independent of
167 // the actual lattice constant (i.e. independent of the calibration
168 // standard's material). This does not hold for lower-symmetry cells,
169 // where B is not isotropic and the cell shape does affect Q's direction.
170 const DblMatrix &ubm = pws->sample().getOrientedLattice().getUB();
171 V3D qv_target = ubm * pk.getIntHKL();
172 qv_target *= 2 * M_PI;
173 const double dSpacing = 2 * M_PI / qv_target.norm();
174 const double theta = 0.5 * pk.getScattering();
175 pk.setWavelength(2 * dSpacing * std::sin(theta));
176 } else {
177 // use the provided cached tofs
178 const double tof = m_tofs[i];
179 // calculate&set wavelength based on new instrument
181 wl.initialize(pk.getL1(), 0,
182 {{UnitParams::l2, pk.getL2()},
183 {UnitParams::twoTheta, pk.getScattering()},
184 {UnitParams::efixed, pk.getInitialEnergy()}});
185 pk.setWavelength(wl.singleFromTOF(tof + dT0));
186 }
187
188 V3D qv = pk.getQSampleFrame();
189 for (int j = 0; j < 3; ++j)
190 out[i * 3 + j] = qv[j];
191
192 // check the difference between n and target
193 // auto ubm = pws->sample().getOrientedLattice().getUB();
194 // V3D qv_target = ubm * pws->getPeak(i).getIntHKL();
195 // qv_target *= 2 * PI;
196 // V3D delta_qv = qv - qv_target;
197 // residual += delta_qv.norm2();
198 }
199
200 n_iter += 1;
201
202 // V3D dtrans = V3D(dx, dy, dz);
203 // V3D drots = V3D(drx, dry, drz);
204 // residual /= pws->getNumberPeaks();
205 // std::ostringstream msgiter;
206 // msgiter.precision(8);
207 // msgiter << "residual@iter_" << n_iter << ": " << residual << "\n"
208 // << "-- (dx, dy, dz) = " << dtrans << "\n"
209 // << "-- (drx, dry, drz) = " << drots << "\n"
210 // << "-- dT0 = " << dT0 << "\n\n";
211 // g_log.notice() << msgiter.str();
212}
213
214// -------///
215// Helper ///
216// -------///
217
227IPeaksWorkspace_sptr SCDCalibratePanels2ObjFunc::moveInstruentComponentBy(double deltaX, double deltaY, double deltaZ,
228 const std::string &componentName,
229 IPeaksWorkspace_sptr &pws) const {
230 // Workspace_sptr inputws = std::dynamic_pointer_cast<Workspace>(pws);
231
232 // move instrument is really fast, even with zero input
233 auto mv_alg = Mantid::API::AlgorithmFactory::Instance().create("MoveInstrumentComponent", -1);
234 mv_alg->initialize();
235 mv_alg->setChild(true);
236 mv_alg->setLogging(LOGCHILDALG);
237 mv_alg->setProperty("Workspace", pws);
238 mv_alg->setProperty("ComponentName", componentName);
239 mv_alg->setProperty("X", deltaX);
240 mv_alg->setProperty("Y", deltaY);
241 mv_alg->setProperty("Z", deltaZ);
242 mv_alg->setProperty("RelativePosition", true);
243 mv_alg->executeAsChildAlg();
244
245 return pws;
246}
247
258IPeaksWorkspace_sptr SCDCalibratePanels2ObjFunc::rotateInstrumentComponentBy(double rotX, double rotY, double rotZ,
259 const std::string &componentName,
260 IPeaksWorkspace_sptr &pws) const {
261 // rotate
262 auto rot_alg = Mantid::API::AlgorithmFactory::Instance().create("RotateInstrumentComponent", -1);
263 // around X
264 rot_alg->initialize();
265 rot_alg->setChild(true);
266 rot_alg->setLogging(LOGCHILDALG);
267 rot_alg->setProperty("Workspace", pws);
268 rot_alg->setProperty("ComponentName", componentName);
269 rot_alg->setProperty("X", 1.0);
270 rot_alg->setProperty("Y", 0.0);
271 rot_alg->setProperty("Z", 0.0);
272 rot_alg->setProperty("Angle", rotX);
273 rot_alg->setProperty("RelativeRotation", true);
274 rot_alg->executeAsChildAlg();
275 // around Y
276 rot_alg->initialize();
277 rot_alg->setChild(true);
278 rot_alg->setLogging(LOGCHILDALG);
279 rot_alg->setProperty("Workspace", pws);
280 rot_alg->setProperty("ComponentName", componentName);
281 rot_alg->setProperty("X", 0.0);
282 rot_alg->setProperty("Y", 1.0);
283 rot_alg->setProperty("Z", 0.0);
284 rot_alg->setProperty("Angle", rotY);
285 rot_alg->setProperty("RelativeRotation", true);
286 rot_alg->executeAsChildAlg();
287 // around Z
288 rot_alg->initialize();
289 rot_alg->setChild(true);
290 rot_alg->setLogging(LOGCHILDALG);
291 rot_alg->setProperty("Workspace", pws);
292 rot_alg->setProperty("ComponentName", componentName);
293 rot_alg->setProperty("X", 0.0);
294 rot_alg->setProperty("Y", 0.0);
295 rot_alg->setProperty("Z", 1.0);
296 rot_alg->setProperty("Angle", rotZ);
297 rot_alg->setProperty("RelativeRotation", true);
298 rot_alg->executeAsChildAlg();
299
300 return pws;
301}
302
304SCDCalibratePanels2ObjFunc::scaleRectagularDetectorSize(const double &scalex, const double &scaley,
305 const std::string &componentName,
307
308 Geometry::Instrument_sptr inst = std::const_pointer_cast<Geometry::Instrument>(pws->getInstrument());
309 Geometry::IComponent_const_sptr comp = inst->getComponentByName(componentName);
310 std::shared_ptr<const Geometry::RectangularDetector> rectDet =
311 std::dynamic_pointer_cast<const Geometry::RectangularDetector>(comp);
312 if (rectDet) {
313 // get instrument parameter map and find out whether the
314 const Geometry::ParameterMap &pmap = pws->instrumentParameters();
315 auto oldscalex = pmap.getDouble(rectDet->getName(), "scalex");
316 auto oldscaley = pmap.getDouble(rectDet->getName(), "scaley");
317 double relscalex{scalex}, relscaley{scaley};
318 if (!oldscalex.empty())
319 relscalex /= oldscalex[0];
320 if (!oldscaley.empty())
321 relscaley /= oldscaley[0];
322 applyRectangularDetectorScaleToComponentInfo(pws->mutableComponentInfo(), rectDet->getComponentID(), relscalex,
323 relscaley);
324 }
325
326 return pws;
327}
328
329} // namespace Mantid::Crystal
#define DECLARE_FUNCTION(classname)
Macro for declaring a new type of function to be used with the FunctionFactory.
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Definition System.h:44
double getParameter(size_t i) const override
Get i-th parameter.
SCDCalibratePanels2ObjFunc : TODO: DESCRIPTION.
bool m_waveFromUB
if true, wavelength is computed from Bragg's law using the UB matrix and each peak's integer HKL inst...
Mantid::API::IPeaksWorkspace_sptr rotateInstrumentComponentBy(double rotX, double rotY, double rotZ, const std::string &componentName, Mantid::API::IPeaksWorkspace_sptr &pws) const
Rotate the instrument by angle axis.
Mantid::API::IPeaksWorkspace_sptr scaleRectagularDetectorSize(const double &scalex, const double &scaley, const std::string &componentName, Mantid::API::IPeaksWorkspace_sptr &pws) const
void function1D(double *out, const double *xValues, const size_t order) const override
base objective function
void setPeakWorkspace(Mantid::API::IPeaksWorkspace_sptr &pws, const std::string &componentName, const std::vector< double > &tofs, bool waveFromUB=false)
Mantid::API::IPeaksWorkspace_sptr moveInstruentComponentBy(double deltaX, double deltaY, double deltaZ, const std::string &componentName, Mantid::API::IPeaksWorkspace_sptr &pws) const
helper functions
Mantid::Kernel::V3D getIntHKL() const override
Return the int HKL vector.
Definition BasePeak.cpp:112
Structure describing a single-crystal peak.
Definition Peak.h:34
void setWavelength(double wavelength) override
Set the incident wavelength of the neutron.
Definition Peak.cpp:188
double getL1() const override
Return the L1 flight path length (source to sample), in meters.
Definition Peak.cpp:724
void setDetectorID(int id)
Set the detector ID of the pixel at the centre of the peak and look up and cache values related to it...
Definition Peak.cpp:206
int getDetectorID() const override
Get the ID of the detector at the center of the peak
Definition Peak.cpp:265
void setInstrument(const Geometry::Instrument_const_sptr &inst)
Set the instrument (and save the source/sample pos).
Definition Peak.cpp:294
double getScattering() const override
Calculate the scattering angle of the peak
Definition Peak.cpp:391
The Logger class is in charge of the publishing messages from the framework through various channels.
Definition Logger.h:51
void initialize(const double &_l1, const int &_emode, const UnitParametersMap &params)
Initialize the unit to perform conversion using singleToTof() and singleFromTof()
Definition Unit.cpp:133
Wavelength in Angstrom.
Definition Unit.h:267
Class for 3D vectors.
Definition V3D.h:34
double norm() const noexcept
Definition V3D.h:269
std::shared_ptr< IPeaksWorkspace > IPeaksWorkspace_sptr
shared pointer to Mantid::API::IPeaksWorkspace
Kernel::Logger g_log("ExperimentInfo")
static logger object
MANTID_API_DLL void applyRectangularDetectorScaleToComponentInfo(Geometry::ComponentInfo &componentInfo, Geometry::IComponent *componentId, const double scaleX, const double scaleY)
Helpers for resizing RectangularDetectors.
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.
Generate a tableworkspace to store the calibration results.
adjust instrument component position and orientation
: detector size scale at y-direction