Mantid
Loading...
Searching...
No Matches
EditInstrumentGeometry.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 +
15
16#include <sstream>
17
18using namespace Mantid::Geometry;
19using namespace Mantid::Kernel;
20using namespace Mantid::API;
21
22using namespace std;
23
24namespace Mantid::Algorithms {
25
26DECLARE_ALGORITHM(EditInstrumentGeometry)
27
28const std::string EditInstrumentGeometry::name() const { return "EditInstrumentGeometry"; }
29
30const std::string EditInstrumentGeometry::category() const {
31 return "Diffraction\\DataHandling;DataHandling\\Instrument";
32}
33
34int EditInstrumentGeometry::version() const { return 1; }
35
36//----------------------------------------------------------------------------------------------
37
38//----------------------------------------------------------------------------------------------
42 // Input workspace
43 declareProperty(std::make_unique<WorkspaceProperty<>>("Workspace", "", Direction::InOut),
44 "Workspace to edit the detector information");
45
46 // L1
47 declareProperty("PrimaryFlightPath", EMPTY_DBL(), "Primary flight path L1 of the powder diffractometer. ");
48
49 // Spectrum numbers from the input workspace
50 declareProperty(std::make_unique<ArrayProperty<int32_t>>("SpectrumIDs"),
51 "Spectrum Numbers from the input workspace (note that it is not detector ID or workspace "
52 "indices). The number of specified spectrum numbers must be either zero or "
53 "the same as the number of histograms.");
54
55 auto required = std::make_shared<MandatoryValidator<std::vector<double>>>();
56
57 // Vector for L2
58 declareProperty(std::make_unique<ArrayProperty<double>>("L2", required),
59 "Secondary flight (L2) paths for each detector. Number of L2 "
60 "given must be same as the number of histograms.");
61
62 // Vector for 2Theta
63 declareProperty(std::make_unique<ArrayProperty<double>>("Polar", required),
64 "Polar angles (two thetas) for detectors. Number of 2theta "
65 "given must be the same as the number of histograms.");
66
67 // Vector for Azimuthal angle
68 declareProperty(std::make_unique<ArrayProperty<double>>("Azimuthal"),
69 "Azimuthal angles (out-of-plane) for detectors. "
70 "The number of azimuthal angles given must be the same as number of histograms.");
71
72 // Detector IDs
73 declareProperty(std::make_unique<ArrayProperty<int>>("DetectorIDs"),
74 "User specified detector IDs of the spectra. "
75 "The number of specified detector IDs must be either zero or "
76 "the same as the number of histograms.");
77
78 // Instrument Name
79 declareProperty("InstrumentName", "",
80 "Name of the newly built instrument. If left empty, "
81 "the original instrument will be used. ");
82}
83
84template <typename NumT> std::string checkValues(const std::vector<NumT> &thingy, const size_t numHist) {
85 if ((!thingy.empty()) && thingy.size() != numHist) {
86 stringstream msg;
87 msg << "Must equal number of spectra or be empty (" << numHist << " != " << thingy.size() << ")";
88 return msg.str();
89 } else {
90 return "";
91 }
92}
93
94std::map<std::string, std::string> EditInstrumentGeometry::validateInputs() {
95 std::map<std::string, std::string> result;
96
97 // everything depends on being parallel to the workspace # histo
98 size_t numHist(0);
99 bool hasWorkspacePtr(false);
100 {
102 // this is to guard against WorkspaceGroups
103 // fallthrough is to skip workspace check and make sure
104 if (bool(workspace)) {
105 hasWorkspacePtr = true;
106 numHist = workspace->getNumberHistograms();
107 }
108 }
109
110 std::string error;
111
112 const std::vector<int32_t> specids = this->getProperty("SpectrumIDs");
113 if (!hasWorkspacePtr) {
114 // use the number of spectra for the number of histograms
115 numHist = specids.size();
116 // give up if it is empty
117 if (numHist == 0) {
118 return result;
119 }
120 }
121 error = checkValues(specids, numHist);
122 if (!error.empty())
123 result["SpectrumIDs"] = error;
124
125 const std::vector<double> l2 = this->getProperty("L2");
126 error = checkValues(l2, numHist);
127 if (!error.empty())
128 result["L2"] = error;
129
130 const std::vector<double> tth = this->getProperty("Polar");
131 error = checkValues(tth, numHist);
132 if (!error.empty())
133 result["Polar"] = error;
134
135 const std::vector<double> phi = this->getProperty("Azimuthal");
136 error = checkValues(phi, numHist);
137 if (!error.empty())
138 result["Azimuthal"] = error;
139
140 const vector<int> detids = getProperty("DetectorIDs");
141 error = checkValues(detids, numHist);
142 if (!error.empty())
143 result["DetectorIDs"] = error;
144
145 // TODO verify that SpectrumIDs, L2, Polar, Azimuthal, and DetectorIDs are
146 // parallel or not specified
147 return result;
148}
149
150//----------------------------------------------------------------------------------------------
154 // Lots of things have to do with the input workspace
156 Geometry::Instrument_const_sptr originstrument = workspace->getInstrument();
157
158 // Get and check the primary flight path
159 double l1 = this->getProperty("PrimaryFlightPath");
160 if (isEmpty(l1)) {
161 // Use the original L1
162 if (!originstrument) {
163 std::string errmsg("It is not supported that L1 is not given, ",
164 "while there is no instrument associated to input workspace.");
165 g_log.error(errmsg);
166 throw std::runtime_error(errmsg);
167 }
168 Geometry::IComponent_const_sptr source = originstrument->getSource();
169 Geometry::IComponent_const_sptr sample = originstrument->getSample();
170 l1 = source->getDistance(*sample);
171 g_log.information() << "Retrieve L1 from input data workspace. \n";
172 }
173 g_log.information() << "Using L1 = " << l1 << "\n";
174
175 // Get spectra number in case they are in a funny order
176 std::vector<int32_t> specids = this->getProperty("SpectrumIDs");
177 if (specids.empty()) // they are using the order of the input workspace
178 {
179 size_t numHist = workspace->getNumberHistograms();
180 for (size_t i = 0; i < numHist; ++i) {
181 specids.emplace_back(workspace->getSpectrum(i).getSpectrumNo());
182 g_log.information() << "Add spectrum " << workspace->getSpectrum(i).getSpectrumNo() << ".\n";
183 }
184 }
185
186 // Get the detector ids - empty means ignore it
187 const vector<int> vec_detids = getProperty("DetectorIDs");
188 const bool renameDetID(!vec_detids.empty());
189
190 // Get individual detector geometries ordered by input spectrum Numbers
191 const std::vector<double> l2s = this->getProperty("L2");
192 const std::vector<double> tths = this->getProperty("Polar");
193 std::vector<double> phis = this->getProperty("Azimuthal");
194
195 // empty list of L2 and 2-theta is not allowed
196 if (l2s.empty()) {
197 throw std::runtime_error("User must specify L2 for all spectra. ");
198 }
199 if (tths.empty()) {
200 throw std::runtime_error("User must specify 2theta for all spectra.");
201 }
202
203 // empty list of phi means that they are all zero
204 if (phis.empty()) {
205 phis.assign(l2s.size(), 0.);
206 }
207
208 // Validate
209 for (size_t ib = 0; ib < l2s.size(); ib++) {
210 g_log.information() << "Detector " << specids[ib] << " L2 = " << l2s[ib] << " 2Theta = " << tths[ib] << '\n';
211 if (specids[ib] < 0) {
212 // Invalid spectrum Number : less than 0.
213 stringstream errmsgss;
214 errmsgss << "Spectrum ID[" << ib << "] = " << specids[ib] << ": cannot be less than 0.";
215 throw std::invalid_argument(errmsgss.str());
216 }
217 if (l2s[ib] <= 0.0) {
218 throw std::invalid_argument("L2 cannot be less or equal to 0");
219 }
220 }
221
222 // Keep original instrument and set the new instrument, if necessary
223 const auto spec2indexmap = workspace->getSpectrumToWorkspaceIndexMap();
224
225 // Condition on output workspace: each spectrum has 1 and only 1 detector
226 size_t nspec = workspace->getNumberHistograms();
227
228 // Initialize another set of L2/2-theta/Phi/DetectorIDs vector ordered by
229 // workspace index
230 std::vector<double> storL2s(nspec, 0.);
231 std::vector<double> stor2Thetas(nspec, 0.);
232 std::vector<double> storPhis(nspec, 0.);
233 vector<int> storDetIDs(nspec, 0);
234
235 // Map the properties from spectrum Number to workspace index
236 for (size_t i = 0; i < specids.size(); i++) {
237 // Find spectrum's workspace index
238 auto it = spec2indexmap.find(specids[i]);
239 if (it == spec2indexmap.end()) {
240 stringstream errss;
241 errss << "Spectrum Number " << specids[i] << " is not present in the input workspace.";
242 g_log.error(errss.str());
243 throw std::runtime_error(errss.str());
244 }
245
246 // Store and set value
247 size_t workspaceindex = it->second;
248
249 storL2s[workspaceindex] = l2s[i];
250 stor2Thetas[workspaceindex] = tths[i];
251 storPhis[workspaceindex] = phis[i];
252 if (renameDetID)
253 storDetIDs[workspaceindex] = vec_detids[i];
254
255 g_log.debug() << "workspace index = " << workspaceindex << " is for Spectrum " << specids[i] << '\n';
256 }
257
258 // Generate a new instrument
259 // Name of the new instrument
260 std::string instrumentName = std::string(getProperty("InstrumentName"));
261 if (instrumentName.empty()) {
262 // Use the name from the original instrument.
263 if (!originstrument) {
264 std::string errmsg("If there's no instrument associated with the input workspace, "
265 "then an instrument name must be specified.");
266 g_log.error(errmsg);
267 throw std::runtime_error(errmsg);
268 }
269 instrumentName = originstrument->getName();
270 }
271
272 // Create a new instrument from scratch.
273 auto instrument = std::make_shared<Geometry::Instrument>(instrumentName);
274
275 // Set up source and sample information
276 Geometry::Component *samplepos = new Geometry::Component("Sample", instrument.get());
277 instrument->add(samplepos);
278 instrument->markAsSamplePos(samplepos);
279 samplepos->setPos(0.0, 0.0, 0.0);
280
281 Geometry::ObjComponent *source = new Geometry::ObjComponent("Source", instrument.get());
282 instrument->add(source);
283 instrument->markAsSource(source);
284 source->setPos(0.0, 0.0, -1.0 * l1);
285
286 // Make a new bank, to add detectors to.
287 // Adding detectors to a bank, rather than directly to the root, allows this instrument
288 // to be successfully saved and reloaded by `NexusGeometrySave` and `NexusGeometryParser`.
289 // (Instrument will take ownership of the pointer.)
290 Geometry::CompAssembly *bank = new CompAssembly("Bank_1");
291
292 // Add/copy detector information
293 for (size_t i = 0; i < workspace->getNumberHistograms(); i++) {
294 // Create a new detector.
295 // (Instrument will take ownership of pointer so no need to delete.)
296 detid_t newdetid;
297 if (renameDetID)
298 newdetid = storDetIDs[i];
299 else
300 newdetid = detid_t(i) + 100;
301 Geometry::Detector *detector = new Geometry::Detector("det", newdetid, samplepos);
302
303 // Set up new detector parameters related to new instrument
304 double l2 = storL2s[i];
305 double tth = stor2Thetas[i];
306 double phi = storPhis[i];
307
308 Kernel::V3D pos;
309 pos.spherical(l2, tth, phi);
310 detector->setPos(pos);
311
312 // Add new detector to spectrum and instrument
313 auto &spectrum = workspace->getSpectrum(i);
314 // Debug processing-status output
315 g_log.debug() << "Original spectrum " << spectrum.getSpectrumNo() << "has " << spectrum.getDetectorIDs().size()
316 << " detectors. \n";
317
318 spectrum.clearDetectorIDs();
319 spectrum.addDetectorID(newdetid);
320 bank->add(detector);
321 instrument->markAsDetector(detector);
322 } // ENDFOR workspace index
323
324 // Add the bank to the instrument
325 instrument->add(bank);
326
327 // Add the instrument to the workspace
328 workspace->setInstrument(instrument);
329}
330
331} // namespace Mantid::Algorithms
std::string name
Definition Run.cpp:60
#define DECLARE_ALGORITHM(classname)
Definition Algorithm.h:538
double error
IPeaksWorkspace_sptr workspace
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.
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.
A property class for workspaces.
EditInstrumentGeometry : TODO: DESCRIPTION.
int version() const override
Algorithm's version for identification overriding a virtual method.
std::map< std::string, std::string > validateInputs() override
Validate the inputs that must be parallel.
void init() override
Initialise the properties.
const std::string category() const override
Algorithm's category for identification overriding a virtual method.
Class for Assembly of geometric components.
int add(IComponent *) override
Add a component to the assembly.
Component is a wrapper for a Component which can modify some of its parameters, e....
Definition Component.h:42
void setPos(double, double, double) override
Set the IComponent position, x, y, z respective to parent (if present)
This class represents a detector - i.e.
Definition Detector.h:30
Object Component class, this class brings together the physical attributes of the component to the po...
Support for a property that holds an array of values.
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 information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
Class for 3D vectors.
Definition V3D.h:34
void spherical(const double R, const double theta, const double phi) noexcept
Sets the vector position based on spherical coordinates.
Definition V3D.cpp:56
std::shared_ptr< const MatrixWorkspace > MatrixWorkspace_const_sptr
shared pointer to the matrix workspace base class (const version)
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::string checkValues(const std::vector< NumT > &thingy, const size_t numHist)
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition IComponent.h:167
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
int32_t detid_t
Typedef for a detector ID.
constexpr double EMPTY_DBL() noexcept
Returns what we consider an "empty" double within a property.
Definition EmptyValues.h:42
STL namespace.
@ InOut
Both an input & output workspace.
Definition Property.h:55