Mantid
Loading...
Searching...
No Matches
SANSBeamFinder.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
18
19#include "Poco/NumberFormatter.h"
20#include "Poco/String.h"
21
22#include <filesystem>
23
25
26// Register the algorithm into the AlgorithmFactory
27DECLARE_ALGORITHM(SANSBeamFinder)
28
29using namespace Kernel;
30using namespace API;
31using namespace Geometry;
32using namespace DataObjects;
33
35 const std::vector<std::string> fileExts{"_event.nxs", ".xml"};
36 declareProperty(std::make_unique<API::FileProperty>("Filename", "", API::FileProperty::Load, fileExts),
37 "Data filed used to find beam center");
38
39 declareProperty("BeamCenterX", EMPTY_DBL(), "Beam position in X pixel coordinates");
40 declareProperty("BeamCenterY", EMPTY_DBL(), "Beam position in Y pixel coordinates");
41
42 declareProperty("UseDirectBeamMethod", true, "If true, the direct beam method will be used");
43 declareProperty("BeamRadius", 3.0, "Beam radius in pixels, used with the scattered beam method");
44
45 declareProperty("FoundBeamCenterX", EMPTY_DBL(), Direction::Output);
46 declareProperty("FoundBeamCenterY", EMPTY_DBL(), Direction::Output);
47
48 declareProperty("PersistentCorrection", true,
49 "If true, the algorithm will be persistent and re-used when "
50 "other data sets are processed");
51 declareProperty("ReductionProperties", "__sans_reduction_properties", Direction::Input);
52 declareProperty("OutputMessage", "", Direction::Output);
53}
54
55MatrixWorkspace_sptr SANSBeamFinder::loadBeamFinderFile(const std::string &beamCenterFile) {
56 std::filesystem::path path(beamCenterFile);
57 const std::string entryName = "SANSBeamFinder" + path.stem().string();
58 const std::string reductionManagerName = getProperty("ReductionProperties");
59
60 MatrixWorkspace_sptr finderWS;
61
62 if (m_reductionManager->existsProperty(entryName)) {
63 finderWS = m_reductionManager->getProperty(entryName);
64 m_output_message += " |Using existing workspace: " + finderWS->getName() + '\n';
65 } else {
66 // Load the dark current if we don't have it already
67 std::string finderWSName = "__beam_finder_" + path.stem().string();
68
69 if (!m_reductionManager->existsProperty("LoadAlgorithm")) {
70 auto loadAlg = createChildAlgorithm("EQSANSLoad", 0.1, 0.3);
71 loadAlg->setProperty("Filename", beamCenterFile);
72 loadAlg->setProperty("NoBeamCenter", true);
73 loadAlg->setProperty("BeamCenterX", EMPTY_DBL());
74 loadAlg->setProperty("BeamCenterY", EMPTY_DBL());
75 loadAlg->setProperty("ReductionProperties", reductionManagerName);
76 loadAlg->executeAsChildAlg();
77 finderWS = loadAlg->getProperty("OutputWorkspace");
78 m_output_message += " |Loaded " + beamCenterFile + "\n";
79 std::string msg = loadAlg->getPropertyValue("OutputMessage");
80 m_output_message += " |" + Poco::replace(msg, "\n", "\n |") + "\n";
81 } else {
82 // Get load algorithm as a string so that we can create a completely
83 // new proxy and ensure that we don't overwrite existing properties
84 IAlgorithm_sptr loadAlg0 = m_reductionManager->getProperty("LoadAlgorithm");
85 const std::string loadString = loadAlg0->toString();
86 auto loadAlg = Algorithm::fromString(loadString);
87
88 loadAlg->setProperty("Filename", beamCenterFile);
89 if (loadAlg->existsProperty("NoBeamCenter"))
90 loadAlg->setProperty("NoBeamCenter", true);
91 if (loadAlg->existsProperty("BeamCenterX"))
92 loadAlg->setProperty("BeamCenterX", EMPTY_DBL());
93 if (loadAlg->existsProperty("BeamCenterY"))
94 loadAlg->setProperty("BeamCenterY", EMPTY_DBL());
95 if (loadAlg->existsProperty("ReductionProperties"))
96 loadAlg->setProperty("ReductionProperties", reductionManagerName);
97 loadAlg->setPropertyValue("OutputWorkspace", finderWSName);
98 loadAlg->execute();
99 std::shared_ptr<Workspace> wks = AnalysisDataService::Instance().retrieve(finderWSName);
100 finderWS = std::dynamic_pointer_cast<MatrixWorkspace>(wks);
101
102 m_output_message += " |Loaded " + beamCenterFile + "\n";
103 if (loadAlg->existsProperty("OutputMessage")) {
104 std::string msg = loadAlg->getPropertyValue("OutputMessage");
105 m_output_message += " |" + Poco::replace(msg, "\n", "\n |") + "\n";
106 }
107 }
108 m_reductionManager->declareProperty(std::make_unique<WorkspaceProperty<>>(entryName, "", Direction::Output));
109 m_reductionManager->setPropertyValue(entryName, finderWSName);
110 m_reductionManager->setProperty(entryName, finderWS);
111 }
112 return finderWS;
113}
114
116 // Reduction property manager
117 const std::string reductionManagerName = getProperty("ReductionProperties");
118 if (PropertyManagerDataService::Instance().doesExist(reductionManagerName)) {
119 m_reductionManager = PropertyManagerDataService::Instance().retrieve(reductionManagerName);
120 } else {
121 m_reductionManager = std::make_shared<PropertyManager>();
122 PropertyManagerDataService::Instance().addOrReplace(reductionManagerName, m_reductionManager);
123 }
124
125 const bool persistent = getProperty("PersistentCorrection");
126 if (!m_reductionManager->existsProperty("SANSBeamFinderAlgorithm") && persistent) {
127 auto algProp = std::make_unique<AlgorithmProperty>("SANSBeamFinderAlgorithm");
128 algProp->setValue(toString());
129 m_reductionManager->declareProperty(std::move(algProp));
130 }
131
132 m_output_message = "Beam center determination\n";
133
134 // Pixel coordinate to real-space coordinate mapping scheme
135 bool specialMapping = false;
136 if (m_reductionManager->existsProperty("InstrumentName")) {
137 const std::string instrumentName = m_reductionManager->getPropertyValue("InstrumentName");
138 specialMapping = instrumentName == "HFIRSANS";
139 }
140
141 // Find beam center
142 double center_x = getProperty("BeamCenterX");
143 double center_y = getProperty("BeamCenterY");
144
145 // Check whether we already know the position
146 const std::string beamCenterFile = getProperty("Filename");
147 std::filesystem::path path(beamCenterFile);
148 const std::string entryNameX = "SANSBeamFinder_X_" + path.stem().string();
149 const std::string entryNameY = "SANSBeamFinder_Y_" + path.stem().string();
150
151 // If the beam enter was provided, simply add it to the reduction
152 // property manager for other algorithms to find it
153 if (!isEmpty(center_x) && !isEmpty(center_y)) {
154 m_output_message += " |Using supplied beam center: ";
155 } else if (m_reductionManager->existsProperty(entryNameX) && m_reductionManager->existsProperty(entryNameY)) {
156 center_x = m_reductionManager->getProperty(entryNameX);
157 center_y = m_reductionManager->getProperty(entryNameY);
158 } else {
159 // Load the beam center file
160 MatrixWorkspace_sptr beamCenterWS = loadBeamFinderFile(beamCenterFile);
161
162 // HFIR reduction masks the first pixels on each edge of the detector
163 if (specialMapping)
164 // int high, int low, int left, int right
165 maskEdges(beamCenterWS, 1, 1, 1, 1);
166
167 auto ctrAlg = createChildAlgorithm("FindCenterOfMassPosition");
168 ctrAlg->setProperty("InputWorkspace", beamCenterWS);
169
170 const bool directBeam = getProperty("UseDirectBeamMethod");
171 ctrAlg->setProperty("DirectBeam", directBeam);
172
173 double beamRadius = getProperty("BeamRadius");
174 if (!directBeam && !isEmpty(beamRadius)) {
175 std::vector<double> pars = beamCenterWS->getInstrument()->getNumberParameter("x-pixel-size");
176 if (pars.empty()) {
177 g_log.error() << "Could not read pixel size from instrument "
178 "parameters: using default\n";
179 } else {
180 ctrAlg->setProperty("BeamRadius", beamRadius * pars[0] / 1000.0);
181 }
182 }
183 ctrAlg->execute();
184 std::vector<double> centerOfMass = ctrAlg->getProperty("CenterOfMass");
185
186 if (specialMapping) {
187 HFIRInstrument::getPixelFromCoordinate(centerOfMass[0], centerOfMass[1], beamCenterWS, center_x, center_y);
188 } else {
189 EQSANSInstrument::getPixelFromCoordinate(centerOfMass[0], centerOfMass[1], beamCenterWS, center_x, center_y);
190 }
191
192 m_output_message += " |Found beam center: ";
193 }
194
195 // Store for later use
196 if (persistent) {
197 if (!m_reductionManager->existsProperty("LatestBeamCenterX"))
198 m_reductionManager->declareProperty(std::make_unique<PropertyWithValue<double>>("LatestBeamCenterX", center_x));
199 if (!m_reductionManager->existsProperty("LatestBeamCenterY"))
200 m_reductionManager->declareProperty(std::make_unique<PropertyWithValue<double>>("LatestBeamCenterY", center_y));
201
202 m_reductionManager->setProperty("LatestBeamCenterX", center_x);
203 m_reductionManager->setProperty("LatestBeamCenterY", center_y);
204 }
205
207 "[" + Poco::NumberFormatter::format(center_x, 3) + ", " + Poco::NumberFormatter::format(center_y, 3) + "]\n";
208
209 // Workflow algorithms can use the LatestBeamCenterX/Y entries, but to be
210 // compatible with the old ReductionSteps we also set output properties
211 // with the beam center position
212 setProperty("FoundBeamCenterX", center_x);
213 setProperty("FoundBeamCenterY", center_y);
214 setProperty("OutputMessage", m_output_message);
215}
216
217/*
218 * The standard HFIR reduction masks the edges of the detector
219 * This is here mostly to allow a direct comparison with old HFIR code
220 * and ensure that we reproduce the same results
221 *
222 * 2016/05/06 : this only works for RectangularDetector
223 *
224 */
225void SANSBeamFinder::maskEdges(const MatrixWorkspace_sptr &beamCenterWS, int high, int low, int left, int right,
226 const std::string &componentName) {
227
228 auto instrument = beamCenterWS->getInstrument();
229
230 std::shared_ptr<Mantid::Geometry::RectangularDetector> component;
231 try {
232 component = std::const_pointer_cast<Mantid::Geometry::RectangularDetector>(
233 std::dynamic_pointer_cast<const Mantid::Geometry::RectangularDetector>(
234 instrument->getComponentByName(componentName)));
235 } catch (std::exception &) {
236 g_log.warning("Expecting the component " + componentName + " to be a RectangularDetector. maskEdges not executed.");
237 return;
238 }
239
240 if (!component) {
241 g_log.warning("Expecting the component " + componentName + " to be a RectangularDetector. maskEdges not executed.");
242 return;
243 }
244
245 std::vector<int> IDs;
246
247 // right
248 for (int i = 0; i < right * component->idstep(); i++) {
249 IDs.emplace_back(component->idstart() + i);
250 }
251 // left
252 for (int i = component->maxDetectorID(); i > (component->maxDetectorID() - left * component->idstep()); i--) {
253 IDs.emplace_back(i);
254 }
255 // low
256 // 0,256,512,768,..,1,257,513
257 for (int row = 0; row < low; row++) { // 0,1
258 for (int i = row + component->idstart();
259 i < component->nelements() * component->idstep() - component->idstep() + low + component->idstart();
260 i += component->idstep()) {
261 IDs.emplace_back(i);
262 }
263 }
264 // high
265 // 255, 511, 767..
266 for (int row = 0; row < high; row++) {
267 for (int i = component->idstep() + component->idstart() - row - 1;
268 i < component->nelements() * component->idstep() + component->idstart(); i += component->idstep()) {
269 IDs.emplace_back(i);
270 }
271 }
272
273 g_log.debug() << "SANSBeamFinder::maskEdges Detector Ids to Mask:" << std::endl;
274 for (auto id : IDs) {
275 g_log.debug() << id << " ";
276 }
277 g_log.debug() << std::endl;
278
279 auto maskAlg = createChildAlgorithm("MaskDetectors");
280 maskAlg->setChild(true);
281 maskAlg->setProperty("Workspace", beamCenterWS);
282 maskAlg->setProperty("DetectorList", IDs);
283 maskAlg->execute();
284
285 // HAcking to mask Wing Detector when finding the beam center
286 if (instrument->getComponentByName("wing_detector")) {
287 maskAlg = createChildAlgorithm("SANSMask");
288 maskAlg->setProperty("Workspace", beamCenterWS);
289 maskAlg->setPropertyValue("Facility", "HFIR");
290 maskAlg->setProperty("MaskedFullComponent", "wing_detector");
291 maskAlg->execute();
292 }
293}
294
295} // namespace Mantid::WorkflowAlgorithms
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double left
double right
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
std::string toString() const override
Serialize an object to a string.
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.
static IAlgorithm_sptr fromString(const std::string &input)
De-serialize an object from a string.
Kernel::Logger & g_log
Definition Algorithm.h:422
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
@ Load
allowed here which will be passed to the algorithm
A property class for workspaces.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
Definition Logger.cpp:145
void error(const std::string &msg)
Logs at error level.
Definition Logger.cpp:108
void warning(const std::string &msg)
Logs at warning level.
Definition Logger.cpp:117
The concrete, templated class for properties.
std::shared_ptr< Kernel::PropertyManager > m_reductionManager
API::MatrixWorkspace_sptr loadBeamFinderFile(const std::string &beamCenterFile)
void maskEdges(const API::MatrixWorkspace_sptr &beamCenterWS, int high, int low, int left, int right, const std::string &componentName="detector1")
void init() override
Initialisation code.
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
void getPixelFromCoordinate(const double &x, const double &y, const API::MatrixWorkspace_sptr &dataWS, double &pixel_x, double &pixel_y)
void getPixelFromCoordinate(const double &x, const double &y, const API::MatrixWorkspace_sptr &dataWS, double &pixel_x, double &pixel_y)
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54