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