Mantid
Loading...
Searching...
No Matches
InstrumentDefinitionParser.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 +
7#include <fstream>
8#include <sstream>
9
22#include "MantidKernel/Logger.h"
26#include "MantidTypes/Core/DateAndTime.h"
27#include "MantidTypes/Core/DateAndTimeHelpers.h"
28
29#include <Poco/DOM/DOMParser.h>
30#include <Poco/DOM/DOMWriter.h>
31#include <Poco/DOM/Document.h>
32#include <Poco/DOM/Element.h>
33#include <Poco/DOM/NodeFilter.h>
34#include <Poco/DOM/NodeIterator.h>
35#include <Poco/DOM/NodeList.h>
36#include <Poco/SAX/AttributesImpl.h>
37#include <Poco/String.h>
38#include <Poco/XML/XMLWriter.h>
39
40#include <boost/regex.hpp>
41#include <filesystem>
42#include <memory>
43#include <unordered_set>
44#include <utility>
45
46using namespace Mantid;
47using namespace Mantid::Kernel;
48using namespace Mantid::Types::Core;
49using Poco::XML::Document;
50using Poco::XML::DOMParser;
51using Poco::XML::Element;
52using Poco::XML::Node;
53using Poco::XML::NodeFilter;
54using Poco::XML::NodeIterator;
55using Poco::XML::NodeList;
56
57namespace Mantid::Geometry {
58namespace {
59// initialize the static logger
60Kernel::Logger g_log("InstrumentDefinitionParser");
61} // namespace
62//----------------------------------------------------------------------------------------------
66 : m_xmlFile(std::make_shared<NullIDFObject>()), m_cacheFile(std::make_shared<NullIDFObject>()), m_pDoc(nullptr),
67 m_hasParameterElement_beenSet(false), m_haveDefaultFacing(false), m_deltaOffsets(false), m_angleConvertConst(1.0),
68 m_indirectPositions(false), m_cachingOption(NoneApplied) {
69 initialise("", "", "", "");
70}
71//----------------------------------------------------------------------------------------------
78InstrumentDefinitionParser::InstrumentDefinitionParser(const std::string &filename, const std::string &instName,
79 const std::string &xmlText)
80 : m_xmlFile(std::make_shared<NullIDFObject>()), m_cacheFile(std::make_shared<NullIDFObject>()), m_pDoc(nullptr),
81 m_hasParameterElement_beenSet(false), m_haveDefaultFacing(false), m_deltaOffsets(false), m_angleConvertConst(1.0),
82 m_indirectPositions(false), m_cachingOption(NoneApplied) {
83 initialise(filename, instName, xmlText, "");
84}
85
86//----------------------------------------------------------------------------------------------
95 const IDFObject_const_sptr &expectedCacheFile,
96 const std::string &instName, const std::string &xmlText)
97 : m_xmlFile(std::make_shared<NullIDFObject>()), m_cacheFile(std::make_shared<NullIDFObject>()), m_pDoc(nullptr),
98 m_hasParameterElement_beenSet(false), m_haveDefaultFacing(false), m_deltaOffsets(false), m_angleConvertConst(1.0),
99 m_indirectPositions(false), m_cachingOption(NoneApplied) {
100 initialise(xmlFile->getFileFullPathStr(), instName, xmlText, expectedCacheFile->getFileFullPathStr());
101
102 m_cacheFile = expectedCacheFile;
103}
104
105//----------------------------------------------------------------------------------------------
114void InstrumentDefinitionParser::initialise(const std::string &filename, const std::string &instName,
115 const std::string &xmlText, const std::string &vtpFilename) {
116
117 IDFObject_const_sptr xmlFile = std::make_shared<const IDFObject>(filename);
118
119 // Handle the parameters
120 m_instName = instName;
121 m_xmlFile = xmlFile;
122
123 // do quick check for side-by-side-view-location string, if it doesn't exist we can skip checking every element,
124 // thereby speeding up processing
125 m_sideBySideViewLocation_exists = xmlText.find("side-by-side-view-location") != std::string::npos;
126
127 // Create our new instrument
128 // We don't want the instrument name taken out of the XML file itself, it
129 // should come from the filename (or the property)
130 m_instrument = std::make_shared<Instrument>(m_instName);
131
132 // Save the XML file path and contents
133 m_instrument->setFilename(filename);
134 m_instrument->setXmlText(xmlText);
135
136 // Use the filename to construct the cachefile name so that there is a 1:1 map
137 // between a definition file & cache
138 if (vtpFilename.empty()) {
139 m_cacheFile = std::make_shared<const IDFObject>(createVTPFileName());
140 } else {
141 m_cacheFile = std::make_shared<const IDFObject>(vtpFilename);
142 }
143}
144
145//----------------------------------------------------------------------------------------------
157
158 std::string retVal;
159 // use the xml in preference if available
160 auto xml = Poco::trim(m_instrument->getXmlText());
161 if (!(xml.empty())) {
162 std::string checksum = Kernel::ChecksumHelper::sha1FromString(xml);
163 retVal = m_instName + checksum;
164 } else if (this->m_xmlFile->exists()) { // Use the file
165 retVal = m_xmlFile->getMangledName();
166 }
167
168 return retVal;
169}
170
171//----------------------------------------------------------------------------------------------
176Poco::AutoPtr<Poco::XML::Document> InstrumentDefinitionParser::getDocument() {
177 if (!m_pDoc) {
178 // instantiate if not created
179 if (m_instrument->getXmlText().empty()) {
180 throw std::invalid_argument("Instrument XML string is empty");
181 }
182 // Set up the DOM parser and parse xml file
183 DOMParser pParser;
184 try {
185 m_pDoc = pParser.parseString(m_instrument->getXmlText());
186 } catch (Poco::Exception &exc) {
187 throw std::invalid_argument(exc.displayText() + ". Unable to parse XML");
188 } catch (...) {
189 throw std::invalid_argument("Unable to parse XML");
190 }
191 }
192 return m_pDoc;
193}
194
202 const std::string &typeName) const {
203 if (getTypeElement.find(typeName) != getTypeElement.end()) {
204 g_log.error(std::string("XML file: ")
205 .append(filename)
206 .append("contains more than one type element named ")
207 .append(typeName));
209 std::string("XML instrument file contains more than one type element named ")
210 .append(typeName)
211 .append(filename));
212 }
213}
214
215//----------------------------------------------------------------------------------------------
223 auto pDoc = getDocument();
224
225 // Get pointer to root element
226 Poco::XML::Element *pRootElem = pDoc->documentElement();
227
228 if (!pRootElem->hasChildNodes()) {
229 g_log.error("Instrument XML contains no root element.");
230 throw Kernel::Exception::InstrumentDefinitionError("No root element in XML instrument");
231 }
232
233 setValidityRange(pRootElem);
234 readDefaults(pRootElem->getChildElement("defaults"));
235 Geometry::ShapeFactory shapeCreator;
236
237 const std::string filename = m_xmlFile->getFileFullPathStr();
238
239 std::vector<Element *> typeElems;
240 std::vector<Element *> compElems;
241 getTypeAndComponentPointers(pRootElem, typeElems, compElems);
242
243 if (typeElems.empty()) {
244 g_log.error("XML file: " + filename + "contains no type elements.");
245 throw Kernel::Exception::InstrumentDefinitionError("No type elements in XML instrument file", filename);
246 }
247
248 collateTypeInformation(filename, typeElems, shapeCreator);
249
250 // Populate m_hasParameterElement
252
253 // See if any parameters set at instrument level
254 setLogfile(m_instrument.get(), pRootElem, m_instrument->getLogfileCache());
255
256 parseLocationsForEachTopLevelComponent(progressReporter, filename, compElems);
257
258 // Don't need this anymore (if it was even used) so empty it out to save
259 // memory
260 m_tempPosHolder.clear();
261
262 // Read in or create the geometry cache file
264
265 // Add/overwrite any instrument params with values specified in
266 // <component-link> XML elements
268
269 // Instrument::markAsDetector is slow unless the detector IDs in the IDF are
270 // sorted. To circumvent this we use the 2-part interface,
271 // markAsDetectorIncomplete (which does not sort) and markAsDetectorFinalize
272 // (which does the final sorting).
273 m_instrument->markAsDetectorFinalize();
274
276 // NOTE this MUST be called on a finalized instrument, as it calls the copy constructor
278 }
279
280 // And give back what we created
281 return m_instrument;
282}
283
298 const std::vector<Element *> &typeElems,
299 ShapeFactory &shapeCreator) {
300 const size_t numberOfTypes = typeElems.size();
301 for (size_t iType = 0; iType < numberOfTypes; ++iType) {
302 Element *pTypeElem = typeElems[iType];
303 std::string typeName = pTypeElem->getAttribute("name");
304
305 // If type contains <combine-components-into-one-shape> then make adjustment
306 // after this loop has completed
307 Poco::AutoPtr<NodeList> pNL_type_combine_into_one_shape =
308 pTypeElem->getElementsByTagName("combine-components-into-one-shape");
309 if (pNL_type_combine_into_one_shape->length() > 0) {
310 continue;
311 }
312
313 throwIfTypeNameNotUnique(filename, typeName);
314 getTypeElement[typeName] = pTypeElem;
315 createShapeIfTypeIsNotAnAssembly(shapeCreator, iType, pTypeElem, typeName);
316 }
317
318 adjustTypesContainingCombineComponentsElement(shapeCreator, filename, typeElems, numberOfTypes);
319}
320
329 const std::string &filename,
330 const std::vector<Element *> &compElems) {
331 if (progressReporter)
332 progressReporter->resetNumSteps(compElems.size(), 0.0, 1.0);
333
334 for (auto pElem : compElems) {
335 if (progressReporter)
336 progressReporter->report("Loading instrument Definition");
337
338 {
339 IdList idList; // structure to possibly be populated with detector IDs
340
342
343 // Loop through all children of this component and see if any
344 // are a <location> or <locations>. Done this way, the
345 // order they are processed is the order they are listed in the
346 // IDF. This is necessary to match the order of the detector IDs.
347 for (Node *pNode = pElem->firstChild(); pNode != nullptr; pNode = pNode->nextSibling()) {
348 auto pChildElem = dynamic_cast<Element *>(pNode);
349 if (!pChildElem)
350 continue;
351 if (pChildElem->tagName() == "location") {
352 // process differently depending on whether component is and
353 // assembly or leaf
354 if (isAssembly(pElem->getAttribute("type"))) {
355 appendAssembly(m_instrument.get(), pChildElem, pElem, idList);
356 } else {
357 appendLeaf(m_instrument.get(), pChildElem, pElem, idList);
358 }
359 } else if (pChildElem->tagName() == "locations") {
360 // append <locations> elements in <locations>
361 appendLocations(m_instrument.get(), pChildElem, pElem, idList);
362 }
363 } // finished looping over all children of this component
364
365 checkIdListExistsAndDefinesEnoughIDs(idList, pElem, filename);
366 idList.reset();
367 }
368 }
369}
370
379 const std::string &filename) const {
380 Poco::AutoPtr<NodeList> pNL_location = pElem->getElementsByTagName("location");
381 Poco::AutoPtr<NodeList> pNL_locations = pElem->getElementsByTagName("locations");
382
383 if (pNL_location->length() == 0 && pNL_locations->length() == 0) {
384 g_log.error(std::string("A component element must contain at least one "
385 "<location> or <locations> element") +
386 " even if it is just an empty location element of the form "
387 "<location />");
388 throw Kernel::Exception::InstrumentDefinitionError(std::string("A component element must contain at least one "
389 "<location> or <locations> element") +
390 " even if it is just an empty location element of the form "
391 "<location />",
392 filename);
393 }
394}
395
405 const std::string &filename) const {
406 if (idList.counted != static_cast<int>(idList.vec.size())) {
407 std::stringstream ss1, ss2;
408 ss1 << idList.vec.size();
409 ss2 << idList.counted;
410 if (!pElem->hasAttribute("idlist")) {
411 g_log.error("No detector ID list found for detectors of type " + pElem->getAttribute("type"));
412 } else if (idList.vec.empty()) {
413 g_log.error("No detector IDs found for detectors in list " + pElem->getAttribute("idlist") +
414 "for detectors of type" + pElem->getAttribute("type"));
415 } else {
416 g_log.error("The number of detector IDs listed in idlist named " + pElem->getAttribute("idlist") +
417 " is larger than the number of detectors listed in type = " + pElem->getAttribute("type"));
418 }
420 "Number of IDs listed in idlist (=" + ss1.str() + ") is larger than the number of detectors listed in type = " +
421 pElem->getAttribute("type") + " (=" + ss2.str() + ").",
422 filename);
423 }
424}
425
432 Poco::AutoPtr<NodeList> pNL_parameter = pRootElem->getElementsByTagName("parameter");
433 unsigned long numParameter = pNL_parameter->length();
434 m_hasParameterElement.reserve(numParameter);
435
436 // It turns out that looping over all nodes and checking if their nodeName is
437 // equal to "parameter" is much quicker than looping over the pNL_parameter
438 // NodeList.
439 NodeIterator it(pRootElem, NodeFilter::SHOW_ELEMENT);
440 Node *pNode = it.nextNode();
441 while (pNode) {
442 if (pNode->nodeName() == "parameter") {
443 auto pParameterElem = dynamic_cast<Element *>(pNode);
444 m_hasParameterElement.emplace_back(dynamic_cast<Element *>(pParameterElem->parentNode()));
445 }
446 pNode = it.nextNode();
447 }
448
450}
451
462 const std::string &filename,
463 const std::vector<Element *> &typeElems,
464 const size_t numberOfTypes) {
465 for (size_t iType = 0; iType < numberOfTypes; ++iType) {
466 Element *pTypeElem = typeElems[iType];
467 std::string typeName = pTypeElem->getAttribute("name");
468
469 // In this loop only interested in types containing
470 // <combine-components-into-one-shape>
471 Poco::AutoPtr<NodeList> pNL_type_combine_into_one_shape =
472 pTypeElem->getElementsByTagName("combine-components-into-one-shape");
473 if (pNL_type_combine_into_one_shape->length() == 0)
474 continue;
475
476 throwIfTypeNameNotUnique(filename, typeName);
477 getTypeElement[typeName] = pTypeElem;
478
480 helper.adjust(pTypeElem, isTypeAssembly, getTypeElement);
481
482 isTypeAssembly[typeName] = false;
483
484 mapTypeNameToShape[typeName] = shapeCreator.createShape(pTypeElem);
485 // Only CSGObjects can be combined into one shape.
486 if (auto csgObj = std::dynamic_pointer_cast<CSGObject>(mapTypeNameToShape[typeName])) {
487 csgObj->setName(static_cast<int>(iType));
488 }
489 }
490}
491
502 Element *pTypeElem, const std::string &typeName) {
503 Poco::AutoPtr<NodeList> pNL_local = pTypeElem->getElementsByTagName("component");
504 if (pNL_local->length() == 0) {
505 isTypeAssembly[typeName] = false;
506
507 // for now try to create a geometry shape associated with every type
508 // that does not contain any component elements
509 mapTypeNameToShape[typeName] = shapeCreator.createShape(pTypeElem);
510 // Name can be set only for a CSGObject.
511 if (auto csgObj = std::dynamic_pointer_cast<CSGObject>(mapTypeNameToShape[typeName])) {
512 csgObj->setName(static_cast<int>(iType));
513 }
514 } else {
515 isTypeAssembly[typeName] = true;
516 if (pTypeElem->hasAttribute("outline")) {
517 pTypeElem->setAttribute("object_created", "no");
518 }
519 }
520}
521
530 std::vector<Element *> &typeElems,
531 std::vector<Element *> &compElems) const {
532 for (auto pNode = pRootElem->firstChild(); pNode != nullptr; pNode = pNode->nextSibling()) {
533 auto pElem = dynamic_cast<Element *>(pNode);
534 if (pElem) {
535 if (pElem->tagName() == "type")
536 typeElems.emplace_back(pElem);
537 else if (pElem->tagName() == "component")
538 compElems.emplace_back(pElem);
539 }
540 }
541}
542
543//-----------------------------------------------------------------------------------------------------------------------
558void InstrumentDefinitionParser::appendLocations(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElems,
559 const Poco::XML::Element *pCompElem, IdList &idList) {
560 // create detached <location> elements from <locations> element
561 Poco::AutoPtr<Document> pLocationsDoc = convertLocationsElement(pLocElems);
562
563 // Get pointer to root element
564 const Element *pRootLocationsElem = pLocationsDoc->documentElement();
565 const bool assembly = isAssembly(pCompElem->getAttribute("type"));
566
567 auto *pElem = dynamic_cast<Poco::XML::Element *>(pRootLocationsElem->firstChild());
568
569 while (pElem) {
570 if (pElem->tagName() != "location") {
571 pElem = dynamic_cast<Poco::XML::Element *>(pElem->nextSibling());
572 continue;
573 }
574
575 if (assembly) {
576 appendAssembly(parent, pElem, pCompElem, idList);
577 } else {
578 appendLeaf(parent, pElem, pCompElem, idList);
579 }
580
581 pElem = dynamic_cast<Poco::XML::Element *>(pElem->nextSibling());
582 }
583}
584
585//-----------------------------------------------------------------------------------------------------------------------
595void InstrumentDefinitionParser::saveDOM_Tree(const std::string &outFilename) {
596 Poco::XML::DOMWriter writer;
597 writer.setNewLine("\n");
598 writer.setOptions(Poco::XML::XMLWriter::PRETTY_PRINT);
599
600 auto pDoc = getDocument();
601 std::ofstream outFile(outFilename.c_str());
602 writer.writeNode(outFile, pDoc);
603 outFile.close();
604}
605
606double InstrumentDefinitionParser::attrToDouble(const Poco::XML::Element *pElem, const std::string &name) {
607 if (pElem->hasAttribute(name)) {
608 const std::string &value = pElem->getAttribute(name);
609 if (!value.empty()) {
610 try {
611 return std::stod(value);
612 } catch (...) {
613 std::stringstream msg;
614 msg << "failed to convert \"" << value << "\" to double for xml attribute \"" << name
615 << "\" - using 0. instead";
616 g_log.warning(msg.str());
617 return 0.;
618 }
619 }
620 }
621 return 0.;
622}
623
624//-----------------------------------------------------------------------------------------------------------------------
637void InstrumentDefinitionParser::setLocation(Geometry::IComponent *comp, const Poco::XML::Element *pElem,
638 const double angleConvertConst, const bool deltaOffsets) {
639 comp->setPos(getRelativeTranslation(comp, pElem, angleConvertConst, deltaOffsets));
640
641 // Rotate coordinate system of this component
642 if (pElem->hasAttribute("rot")) {
643 double rotAngle = angleConvertConst * attrToDouble(pElem, "rot"); // assumed to be in degrees
644
645 double axis_x = 0.0;
646 double axis_y = 0.0;
647 double axis_z = 1.0;
648
649 if (pElem->hasAttribute("axis-x"))
650 axis_x = std::stod(pElem->getAttribute("axis-x"));
651 if (pElem->hasAttribute("axis-y"))
652 axis_y = std::stod(pElem->getAttribute("axis-y"));
653 if (pElem->hasAttribute("axis-z"))
654 axis_z = std::stod(pElem->getAttribute("axis-z"));
655
656 comp->rotate(Kernel::Quat(rotAngle, Kernel::V3D(axis_x, axis_y, axis_z)));
657 }
658
659 // Check if sub-elements <trans> or <rot> of present - for now ignore these if
660 // m_deltaOffset = true
661
662 Element *pRecursive = nullptr;
663 Element *tElem = pElem->getChildElement("trans");
664 Element *rElem = pElem->getChildElement("rot");
665 bool stillTransElement = true;
666 bool firstRound = true; // during first round below pRecursive has not been set up front
667 while (stillTransElement) {
668 // figure out if child element is <trans> or <rot> or none of these
669
670 if (firstRound) {
671 firstRound = false;
672 } else if (pRecursive != nullptr) {
673 tElem = pRecursive->getChildElement("trans");
674 rElem = pRecursive->getChildElement("rot");
675 }
676
677 if (tElem && rElem) {
678 // if both a <trans> and <rot> child element present. Ignore <rot> element
679 rElem = nullptr;
680 }
681
682 if (!tElem && !rElem) {
683 stillTransElement = false;
684 }
685
686 Kernel::V3D posTrans;
687
688 if (tElem) {
689 posTrans = getRelativeTranslation(comp, tElem, angleConvertConst, deltaOffsets);
690
691 // to get the change in translation relative to current rotation of comp
692 Geometry::CompAssembly compToGetRot;
694 compRot.setRot(comp->getRotation());
695 compToGetRot.setParent(&compRot);
696 compToGetRot.setPos(posTrans);
697
698 // Apply translation
699 comp->translate(compToGetRot.getPos());
700
701 // for recursive action
702 pRecursive = tElem;
703 } // end translation
704
705 if (rElem) {
706 double rotAngle = angleConvertConst * attrToDouble(rElem, "val"); // assumed to be in degrees
707
708 double axis_x = 0.0;
709 double axis_y = 0.0;
710 double axis_z = 1.0;
711
712 if (rElem->hasAttribute("axis-x"))
713 axis_x = std::stod(rElem->getAttribute("axis-x"));
714 if (rElem->hasAttribute("axis-y"))
715 axis_y = std::stod(rElem->getAttribute("axis-y"));
716 if (rElem->hasAttribute("axis-z"))
717 axis_z = std::stod(rElem->getAttribute("axis-z"));
718
719 comp->rotate(Kernel::Quat(rotAngle, Kernel::V3D(axis_x, axis_y, axis_z)));
720
721 // for recursive action
722 pRecursive = rElem;
723 }
724
725 } // end while
726}
727
729 const Poco::XML::Element *pCompElem) {
730 // return if no elements contain side-by-side-view-location parameter
732 return;
733
734 auto pViewLocElem = pCompElem->getChildElement("side-by-side-view-location");
735 if (pViewLocElem) {
736 double x = attrToDouble(pViewLocElem, "x");
737 double y = attrToDouble(pViewLocElem, "y");
738 comp->setSideBySideViewPos(V2D(x, y));
739 }
740}
741
742//-----------------------------------------------------------------------------------------------------------------------
757 const Poco::XML::Element *pElem,
758 const double angleConvertConst,
759 const bool deltaOffsets) {
760 Kernel::V3D retVal; // position relative to parent
761
762 // Polar coordinates can be labelled as (r,t,p) or (R,theta,phi)
763 if (pElem->hasAttribute("r") || pElem->hasAttribute("t") || pElem->hasAttribute("p") || pElem->hasAttribute("R") ||
764 pElem->hasAttribute("theta") || pElem->hasAttribute("phi")) {
765
766 double R = attrToDouble(pElem, "r");
767 double theta = angleConvertConst * attrToDouble(pElem, "t");
768 double phi = angleConvertConst * attrToDouble(pElem, "p");
769
770 if (pElem->hasAttribute("R"))
771 R = attrToDouble(pElem, "R");
772 if (pElem->hasAttribute("theta"))
773 theta = angleConvertConst * attrToDouble(pElem, "theta");
774 if (pElem->hasAttribute("phi"))
775 phi = angleConvertConst * attrToDouble(pElem, "phi");
776
777 if (deltaOffsets) {
778 // In this case, locations given are radial offsets to the (radial)
779 // position of the parent,
780 // so need to do some extra calculation before they're stored internally
781 // as x,y,z offsets.
782
783 // Temporary vector to hold the parent's absolute position (will be 0,0,0
784 // if no parent)
785 Kernel::V3D parentPos;
786 // Get the parent's absolute position (if the component has a parent)
787 if (comp->getParent()) {
788 std::map<const Geometry::IComponent *, SphVec>::iterator it;
789 it = m_tempPosHolder.find(comp);
790 SphVec parent;
791 if (it == m_tempPosHolder.end())
792 parent = m_tempPosHolder[comp->getParent().get()];
793 else
794 parent = it->second;
795
796 // Add to the current component to get its absolute position
797 R += parent.r;
798 theta += parent.theta;
799 phi += parent.phi;
800 // Set the temporary V3D with the parent's absolute position
801 parentPos.spherical(parent.r, parent.theta, parent.phi);
802 }
803
804 // Create a temporary vector that holds the absolute r,theta,phi position
805 // Needed to make things work in situation when a parent object has a phi
806 // value but a theta of zero
807 SphVec tmp(R, theta, phi);
808 // Add it to the map with the pointer to the Component object as key
809 m_tempPosHolder[comp] = tmp;
810
811 // Create a V3D and set its position to be the child's absolute position
812 Kernel::V3D absPos;
813 absPos.spherical(R, theta, phi);
814
815 // Subtract the two V3D's to get what we want (child's relative position
816 // in x,y,z)
817 retVal = absPos - parentPos;
818 } else {
819 // In this case, the value given represents a vector from the parent to
820 // the child
821 retVal.spherical(R, theta, phi);
822 }
823
824 } else {
825 double x = attrToDouble(pElem, "x");
826 double y = attrToDouble(pElem, "y");
827 double z = attrToDouble(pElem, "z");
828
829 retVal(x, y, z);
830 }
831
832 return retVal;
833}
834
835//-----------------------------------------------------------------------------------------------------------------------
844Poco::XML::Element *InstrumentDefinitionParser::getParentComponent(const Poco::XML::Element *pLocElem) {
845 if (((pLocElem->tagName()) != "location") && ((pLocElem->tagName()) != "locations")) {
846 const std::string &tagname = pLocElem->tagName();
847 g_log.error("Argument to function getParentComponent must be a pointer to "
848 "an XML element with tag name location or locations.");
849 throw std::logic_error(std::string("Argument to function getParentComponent must be a pointer "
850 "to an XML element") +
851 "with tag name location or locations." + " The tag name is " + tagname);
852 }
853
854 // The location element is required to be a child of a component element. Get
855 // this component element
856
857 Node *pCompNode = pLocElem->parentNode();
858
859 Element *pCompElem;
860 if (pCompNode->nodeType() == 1) {
861 pCompElem = static_cast<Element *>(pCompNode);
862 if ((pCompElem->tagName()) != "component") {
863 g_log.error("Argument to function getParentComponent must be a XML "
864 "element sitting inside a component element.");
865 throw std::logic_error("Argument to function getParentComponent must be "
866 "a XML element sitting inside a component "
867 "element.");
868 }
869 } else {
870 g_log.error("Argument to function getParentComponent must be a XML element "
871 "whos parent is an element.");
872 throw std::logic_error("Argument to function getParentComponent must be a "
873 "XML element whos parent is an element.");
874 }
875
876 return pCompElem;
877}
878
879//-----------------------------------------------------------------------------------------------------------------------
891std::string InstrumentDefinitionParser::getNameOfLocationElement(const Poco::XML::Element *pElem,
892 const Poco::XML::Element *pCompElem) {
893 std::string retVal;
894
895 if (pElem->hasAttribute("name"))
896 retVal = pElem->getAttribute("name");
897 else if (pCompElem->hasAttribute("name")) {
898 retVal = pCompElem->getAttribute("name");
899 } else {
900 retVal = pCompElem->getAttribute("type");
901 }
902
903 return retVal;
904}
905
906//------------------------------------------------------------------------------------------------------------------------------
910void InstrumentDefinitionParser::setValidityRange(const Poco::XML::Element *pRootElem) {
911 const std::string filename = m_xmlFile->getFileFullPathStr();
912 // check if IDF has valid-from and valid-to tags defined
913 if (!pRootElem->hasAttribute("valid-from")) {
914 throw Kernel::Exception::InstrumentDefinitionError("<instrument> element must contain a valid-from tag", filename);
915 } else {
916 try {
917 DateAndTime d(pRootElem->getAttribute("valid-from"));
918 m_instrument->setValidFromDate(d);
919 } catch (...) {
920 throw Kernel::Exception::InstrumentDefinitionError("The valid-from <instrument> tag must be a ISO8601 string",
921 filename);
922 }
923 }
924
925 if (!pRootElem->hasAttribute("valid-to")) {
926 DateAndTime d = DateAndTime::getCurrentTime();
927 m_instrument->setValidToDate(d);
928 // Ticket #2335: no required valid-to date.
929 // throw Kernel::Exception::InstrumentDefinitionError("<instrument> element
930 // must contain a valid-to tag", filename);
931 } else {
932 try {
933 DateAndTime d(pRootElem->getAttribute("valid-to"));
934 m_instrument->setValidToDate(d);
935 } catch (...) {
936 throw Kernel::Exception::InstrumentDefinitionError("The valid-to <instrument> tag must be a ISO8601 string",
937 filename);
938 }
939 }
940}
941
942PointingAlong axisNameToAxisType(const std::string &label, const std::string &input) {
943 PointingAlong direction;
944 if (input == "x") {
945 direction = X;
946 } else if (input == "y") {
947 direction = Y;
948 } else if (input == "z") {
949 direction = Z;
950 } else {
951 std::stringstream msg;
952 msg << "Cannot create \"" << label << "\" with axis direction other than \"x\", \"y\", or \"z\", found \"" << input
953 << "\"";
955 }
956 return direction;
957}
958
959//-----------------------------------------------------------------------------------------------------------------------
965void InstrumentDefinitionParser::readDefaults(Poco::XML::Element *defaults) {
966 // Return without complaint, if there are no defaults
967 if (!defaults)
968 return;
969
970 // Check whether spherical coordinates should be treated as offsets to parents
971 // position
972 std::string offsets;
973 Element *offsetElement = defaults->getChildElement("offsets");
974 if (offsetElement)
975 offsets = offsetElement->getAttribute("spherical");
976 if (offsets == "delta")
977 m_deltaOffsets = true;
978
979 // Check whether default facing is set
980 Element *defaultFacingElement = defaults->getChildElement("components-are-facing");
981 if (defaultFacingElement) {
982 m_haveDefaultFacing = true;
983 m_defaultFacing = parseFacingElementToV3D(defaultFacingElement);
984 }
985
986 // the default view is used by the instrument viewer to decide the angle to
987 // display the instrument from on start up
988 Element *defaultView = defaults->getChildElement("default-view");
989 if (defaultView) {
990 m_instrument->setDefaultViewAxis(defaultView->getAttribute("axis-view"));
991 if (defaultView->hasAttribute("view")) {
992 m_instrument->setDefaultView(defaultView->getAttribute("view"));
993 }
994 }
995
996 // check if angle=radian has been set
997 Element *angleUnit = defaults->getChildElement("angle");
998 if (angleUnit) {
999 if (angleUnit->getAttribute("unit") == "radian") {
1000 m_angleConvertConst = 180.0 / M_PI;
1001 std::map<std::string, std::string> &units = m_instrument->getLogfileUnit();
1002 units["angle"] = "radian";
1003 }
1004 }
1005
1006 // Check if the IDF specifies that this is an indirect geometry instrument
1007 // that includes
1008 // both physical and 'neutronic' postions.
1009 // Any neutronic position tags will be ignored if this tag is missing
1010 if (defaults->getChildElement("indirect-neutronic-positions"))
1011 m_indirectPositions = true;
1012
1013 /*
1014 Try to extract the reference frame information.
1015 */
1016 // Get the target xml element.
1017 Element *referenceFrameElement = defaults->getChildElement("reference-frame");
1018 // Extract if available
1019 if (referenceFrameElement) {
1020 using Poco::XML::XMLString;
1021 // Get raw xml values
1022 Element *upElement = referenceFrameElement->getChildElement("pointing-up");
1023 Element *alongElement = referenceFrameElement->getChildElement("along-beam");
1024 Element *handednessElement = referenceFrameElement->getChildElement("handedness");
1025 Element *originElement = referenceFrameElement->getChildElement("origin");
1026 Element *thetaSignElement = referenceFrameElement->getChildElement("theta-sign");
1027
1028 // Defaults
1029 XMLString s_alongBeam("z");
1030 XMLString s_pointingUp("y");
1031 XMLString s_handedness("right");
1032 XMLString s_origin;
1033
1034 // Make extractions from sub elements where possible.
1035 if (alongElement) {
1036 s_alongBeam = alongElement->getAttribute("axis");
1037 }
1038 if (upElement) {
1039 s_pointingUp = upElement->getAttribute("axis");
1040 }
1041 if (handednessElement) {
1042 s_handedness = handednessElement->getAttribute("val");
1043 }
1044 if (originElement) {
1045 s_origin = originElement->getAttribute("val");
1046 }
1047
1048 // Extract theta sign axis if specified.
1049 XMLString s_thetaSign(s_pointingUp);
1050 if (thetaSignElement) {
1051 s_thetaSign = thetaSignElement->getAttribute("axis");
1052 }
1053
1054 // Convert to input types
1055 PointingAlong alongBeam = axisNameToAxisType("along-beam", s_alongBeam);
1056 PointingAlong pointingUp = axisNameToAxisType("pointing-up", s_pointingUp);
1057 PointingAlong thetaSign = axisNameToAxisType("theta-sign", s_thetaSign);
1058 Handedness handedness = s_handedness == "right" ? Right : Left;
1059
1060 // Overwrite the default reference frame.
1061 m_instrument->setReferenceFrame(
1062 std::make_shared<ReferenceFrame>(pointingUp, alongBeam, thetaSign, handedness, s_origin));
1063 }
1064}
1065
1066std::vector<std::string> InstrumentDefinitionParser::buildExcludeList(const Poco::XML::Element *const location) {
1067 // check if <exclude> sub-elements for this location and create new exclude
1068 // list to pass on
1069 Poco::AutoPtr<NodeList> pNLexclude = location->getElementsByTagName("exclude");
1070 unsigned long numberExcludeEle = pNLexclude->length();
1071 std::vector<std::string> newExcludeList;
1072 for (unsigned long i = 0; i < numberExcludeEle; i++) {
1073 auto *pExElem = static_cast<Element *>(pNLexclude->item(i));
1074 if (pExElem->hasAttribute("sub-part"))
1075 newExcludeList.emplace_back(pExElem->getAttribute("sub-part"));
1076 }
1077
1078 return newExcludeList;
1079}
1080
1081//-----------------------------------------------------------------------------------------------------------------------
1096void InstrumentDefinitionParser::appendAssembly(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem,
1097 const Poco::XML::Element *pCompElem, IdList &idList) {
1098 const std::string filename = m_xmlFile->getFileFullPathStr();
1099 // The location element is required to be a child of a component element. Get
1100 // this component element
1101 // Element* pCompElem =
1102 // InstrumentDefinitionParser::getParentComponent(pLocElem);
1103
1104 // Read detector IDs into idlist if required
1105 // Note idlist may be defined for any component
1106 // Note any new idlist found will take precedence.
1107
1108 if (pCompElem->hasAttribute("idlist")) {
1109 std::string idlist = pCompElem->getAttribute("idlist");
1110
1111 if (idlist != idList.idname) {
1112 Element *pFound = pCompElem->ownerDocument()->getElementById(idlist, "idname");
1113
1114 if (pFound == nullptr) {
1116 "No <idlist> with name idname=\"" + idlist + "\" present in instrument definition file.", filename);
1117 }
1118 idList.reset();
1119 populateIdList(pFound, idList);
1120 }
1121 }
1122
1123 // Create the assembly that will be appended into the parent.
1125 // The newly added component is required to have a type. Find out what this
1126 // type is and find all the location elements of this type. Finally loop over
1127 // these
1128 // location elements
1129
1130 Element *pType = getTypeElement[pCompElem->getAttribute("type")];
1131 std::string category;
1132 if (pType->hasAttribute("is"))
1133 category = pType->getAttribute("is");
1134 if (category == "SamplePos" || category == "samplePos") {
1136 } else if (pType->hasAttribute("outline") && pType->getAttribute("outline") != "no") {
1138 parent);
1139 } else {
1141 }
1142
1143 // set location for this newly added comp and set facing if specified in
1144 // instrument def. file. Also
1145 // check if any logfiles are referred to through the <parameter> element.
1146
1148 setSideBySideViewLocation(ass, pCompElem);
1149 setFacing(ass, pLocElem);
1150 setLogfile(ass, pCompElem,
1151 m_instrument->getLogfileCache()); // params specified within <component>
1152 setLogfile(ass, pLocElem,
1153 m_instrument->getLogfileCache()); // params specified within specific <location>
1154
1155 // check if special Component
1156 if (category == "SamplePos" || category == "samplePos") {
1157 m_instrument->markAsSamplePos(ass);
1158 }
1159 if (category == "Source" || category == "source") {
1160 m_instrument->markAsSource(ass);
1161 }
1162
1163 // If enabled, check for a 'neutronic position' tag and add to cache if found
1164 if (m_indirectPositions) {
1165 Element *neutronic = pLocElem->getChildElement("neutronic");
1166 if (neutronic)
1167 m_neutronicPos[ass] = neutronic;
1168 }
1169
1170 // Check for <exclude> tags for this location
1171 const std::vector<std::string> excludeList = buildExcludeList(pLocElem);
1172
1173 NodeIterator it(pType, NodeFilter::SHOW_ELEMENT);
1174
1175 Node *pNode = it.nextNode();
1176 while (pNode) {
1177 if (pNode->nodeName() == "location") {
1178 // pLocElem is the location of a type. This type is here an assembly and
1179 // pElem below is a <location> within this type
1180 const Element *pElem = static_cast<Element *>(pNode);
1181
1182 // get the parent of pElem, i.e. a pointer to the <component> element that
1183 // contains pElem
1184 const Element *pParentElem = InstrumentDefinitionParser::getParentComponent(pElem);
1185
1186 // check if this location is in the exclude list
1187 auto inExcluded = find(excludeList.cbegin(), excludeList.cend(),
1189 if (inExcluded == excludeList.end()) {
1190
1191 std::string typeName = (InstrumentDefinitionParser::getParentComponent(pElem))->getAttribute("type");
1192
1193 if (isAssembly(typeName)) {
1194 appendAssembly(ass, pElem, pParentElem, idList);
1195 } else {
1196 appendLeaf(ass, pElem, pParentElem, idList);
1197 }
1198 }
1199 }
1200 if (pNode->nodeName() == "locations") {
1201 const Element *pLocationsElems = static_cast<Element *>(pNode);
1202 const Element *pParentLocationsElem = InstrumentDefinitionParser::getParentComponent(pLocationsElems);
1203
1204 // append <locations> elements in <locations>
1205 appendLocations(ass, pLocationsElems, pParentLocationsElem, idList);
1206 }
1207 pNode = it.nextNode();
1208 }
1209
1210 // create outline object for the assembly
1211 if (pType->hasAttribute("outline") && pType->getAttribute("outline") != "no") {
1212 auto *objAss = dynamic_cast<Geometry::ObjCompAssembly *>(ass);
1213 if (!objAss) {
1214 throw std::logic_error("Failed to cast ICompAssembly object to ObjCompAssembly");
1215 }
1216 if (pType->getAttribute("object_created") == "no") {
1217 pType->setAttribute("object_created", "yes");
1218 std::shared_ptr<Geometry::IObject> obj = objAss->createOutline();
1219 if (obj) {
1220 mapTypeNameToShape[pType->getAttribute("name")] = obj;
1221 } else { // object failed to be created
1222 pType->setAttribute("outline", "no");
1223 g_log.warning() << "Failed to create outline object for assembly " << pType->getAttribute("name") << '\n';
1224 }
1225 } else {
1226 objAss->setOutline(mapTypeNameToShape[pType->getAttribute("name")]);
1227 }
1228 }
1229}
1230
1232 const Poco::XML::Element *pLocElem,
1233 const Poco::XML::Element *pCompElem,
1234 const std::string &filename, IdList &idList,
1235 const std::string &category) {
1236
1237 //-------------- Create a Detector
1238 //------------------------------------------------
1239 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1240
1241 // before setting detector ID check that the IDF satisfies the following
1242
1243 if (idList.counted >= static_cast<int>(idList.vec.size())) {
1244 std::stringstream ss1, ss2;
1245 ss1 << idList.vec.size();
1246 ss2 << idList.counted;
1247 if (idList.idname.empty()) {
1248 g_log.error("No list of detector IDs found for location element " + name);
1249 throw Kernel::Exception::InstrumentDefinitionError("Detector location element " + name + " has no idlist.",
1250 filename);
1251 } else if (idList.vec.empty()) {
1252 g_log.error("No detector IDs found for detectors in list " + idList.idname);
1253 } else {
1254 g_log.error("The number of detector IDs listed in idlist named " + idList.idname +
1255 " is less then the number of detectors");
1256 }
1258 "Number of IDs listed in idlist (=" + ss1.str() + ") is less than the number of detectors.", filename);
1259 }
1260
1261 std::string typeName = pCompElem->getAttribute("type");
1262
1263 // Create detector and increment id. Finally add the detector to the parent
1264 Geometry::Detector *detector =
1265 new Geometry::Detector(name, idList.vec[idList.counted], mapTypeNameToShape[typeName], parent);
1266 idList.counted++;
1267 parent->add(detector);
1268
1269 // set location for this newly added comp and set facing if specified in
1270 // instrument def. file. Also
1271 // check if any logfiles are referred to through the <parameter> element.
1272 setLocation(detector, pLocElem, m_angleConvertConst, m_deltaOffsets);
1273 setFacing(detector, pLocElem);
1274 setLogfile(detector, pCompElem,
1275 m_instrument->getLogfileCache()); // params specified within <component>
1276 setLogfile(detector, pLocElem,
1277 m_instrument->getLogfileCache()); // params specified within specific <location>
1278
1279 // If enabled, check for a 'neutronic position' tag and add to cache
1280 // (null pointer added INTENTIONALLY if not found)
1281 if (m_indirectPositions) {
1282 m_neutronicPos[detector] = pLocElem->getChildElement("neutronic");
1283 }
1284
1285 // mark-as is a deprecated attribute used before is="monitor" was introduced
1286 if (pCompElem->hasAttribute("mark-as") || pLocElem->hasAttribute("mark-as")) {
1287 g_log.warning() << "Attribute 'mark-as' is a deprecated attribute in "
1288 "Instrument Definition File."
1289 << " Please see the deprecated section of "
1290 "docs.mantidproject.org/concepts/InstrumentDefinitionFile for how to remove this "
1291 "warning message\n";
1292 }
1293
1294 try {
1295 if (category == "Monitor" || category == "monitor") {
1296 m_instrument->markAsMonitorIncomplete(detector);
1297 } else {
1298 // for backwards compatibility look for mark-as="monitor"
1299 if ((pCompElem->hasAttribute("mark-as") && pCompElem->getAttribute("mark-as") == "monitor") ||
1300 (pLocElem->hasAttribute("mark-as") && pLocElem->getAttribute("mark-as") == "monitor")) {
1301 m_instrument->markAsMonitorIncomplete(detector);
1302 } else {
1303 m_instrument->markAsDetectorIncomplete(detector);
1304 }
1305 }
1306
1307 } catch (Kernel::Exception::ExistsError &) {
1308 std::stringstream convert;
1309 convert << detector->getID();
1311 "Detector with ID = " + convert.str() + " present more then once in XML instrument file", filename);
1312 }
1313
1314 // Add all monitors and detectors to 'facing component' container. This is
1315 // only used if the
1316 // "facing" elements are defined in the instrument definition file
1317 m_facingComponent.emplace_back(detector);
1318
1319 setSideBySideViewLocation(detector, pCompElem);
1320}
1321
1322void InstrumentDefinitionParser::createGridDetector(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem,
1323 const Poco::XML::Element *pCompElem, const std::string &filename,
1324 const Poco::XML::Element *pType) {
1325
1326 //-------------- Create a GridDetector
1327 //------------------------------------------------
1328 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1329
1330 // Create the bank with the given parent.
1331 auto bank = new Geometry::GridDetector(name, parent);
1332
1333 // set location for this newly added comp and set facing if specified in
1334 // instrument def. file. Also
1335 // check if any logfiles are referred to through the <parameter> element.
1337 setFacing(bank, pLocElem);
1338 setLogfile(bank, pCompElem,
1339 m_instrument->getLogfileCache()); // params specified within <component>
1340 setLogfile(bank, pLocElem,
1341 m_instrument->getLogfileCache()); // params specified within specific <location>
1342
1343 // Extract all the parameters from the XML attributes
1344 int xpixels = 0;
1345 int ypixels = 0;
1346 int zpixels = 0;
1347 int idstart = 0;
1348 std::string idfillorder;
1349 int idstepbyrow = 0;
1350 int idstep = 1;
1351
1352 // The shape!
1353 // Given that this leaf component is actually an assembly, its constituent
1354 // component detector shapes comes from its type attribute.
1355 const std::string shapeType = pType->getAttribute("type");
1356 std::shared_ptr<Geometry::IObject> shape = mapTypeNameToShape[shapeType];
1357 // These parameters are in the TYPE defining RectangularDetector
1358 if (pType->hasAttribute("xpixels"))
1359 xpixels = std::stoi(pType->getAttribute("xpixels"));
1360 double xstart = attrToDouble(pType, "xstart");
1361 double xstep = attrToDouble(pType, "xstep");
1362
1363 if (pType->hasAttribute("ypixels"))
1364 ypixels = std::stoi(pType->getAttribute("ypixels"));
1365 double ystart = attrToDouble(pType, "ystart");
1366 double ystep = attrToDouble(pType, "ystep");
1367
1368 if (pType->hasAttribute("zpixels"))
1369 zpixels = std::stoi(pType->getAttribute("zpixels"));
1370 double zstart = attrToDouble(pType, "zstart");
1371 double zstep = attrToDouble(pType, "zstep");
1372
1373 // THESE parameters are in the INSTANCE of this type - since they will
1374 // change.
1375 if (pCompElem->hasAttribute("idstart"))
1376 idstart = std::stoi(pCompElem->getAttribute("idstart"));
1377 if (pCompElem->hasAttribute("idfillorder"))
1378 idfillorder = pCompElem->getAttribute("idfillorder");
1379 // Default ID row step size
1380 if (!idfillorder.empty() && idfillorder[0] == 'x')
1381 idstepbyrow = xpixels;
1382 else if (!idfillorder.empty() && idfillorder[0] == 'y')
1383 idstepbyrow = ypixels;
1384 else
1385 idstepbyrow = zpixels;
1386
1387 if (pCompElem->hasAttribute("idstepbyrow")) {
1388 idstepbyrow = std::stoi(pCompElem->getAttribute("idstepbyrow"));
1389 }
1390 // Default ID row step size
1391 if (pCompElem->hasAttribute("idstep"))
1392 idstep = std::stoi(pCompElem->getAttribute("idstep"));
1393
1394 setSideBySideViewLocation(bank, pCompElem);
1395
1396 // Now, initialize all the pixels in the bank
1397 bank->initialize(shape, xpixels, xstart, xstep, ypixels, ystart, ystep, zpixels, zstart, zstep, idstart, idfillorder,
1398 idstepbyrow, idstep);
1399
1400 // Loop through all detectors in the newly created bank and mark those in
1401 // the instrument.
1402 try {
1403 for (int z = 0; z < bank->nelements(); ++z) {
1404 auto zLayer = std::dynamic_pointer_cast<Geometry::ICompAssembly>((*bank)[z]);
1405 for (int x = 0; x < zLayer->nelements(); ++x) {
1406 auto xColumn = std::dynamic_pointer_cast<Geometry::ICompAssembly>((*zLayer)[x]);
1407 for (int y = 0; y < xColumn->nelements(); ++y) {
1408 std::shared_ptr<Geometry::Detector> detector = std::dynamic_pointer_cast<Geometry::Detector>((*xColumn)[y]);
1409 if (detector) {
1410 // Make default facing for the pixel
1411 auto *comp = static_cast<IComponent *>(detector.get());
1414 // Mark it as a detector (add to the instrument cache)
1415 m_instrument->markAsDetectorIncomplete(detector.get());
1416 }
1417 }
1418 }
1419 }
1420 } catch (Kernel::Exception::ExistsError &) {
1421 throw Kernel::Exception::InstrumentDefinitionError("Duplicate detector ID found when adding GridDetector " + name +
1422 " in XML instrument file" + filename);
1423 }
1424}
1425
1427 const Poco::XML::Element *pLocElem,
1428 const Poco::XML::Element *pCompElem,
1429 const std::string &filename,
1430 const Poco::XML::Element *pType) {
1431 //-------------- Create a RectangularDetector
1432 //------------------------------------------------
1433 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1434
1435 // Create the bank with the given parent.
1436 auto bank = new Geometry::RectangularDetector(name, parent);
1437
1438 // set location for this newly added comp and set facing if specified in
1439 // instrument def. file. Also
1440 // check if any logfiles are referred to through the <parameter> element.
1442 setFacing(bank, pLocElem);
1443 setLogfile(bank, pCompElem,
1444 m_instrument->getLogfileCache()); // params specified within <component>
1445 setLogfile(bank, pLocElem,
1446 m_instrument->getLogfileCache()); // params specified within specific <location>
1447
1448 // Extract all the parameters from the XML attributes
1449 int xpixels = 0;
1450 int ypixels = 0;
1451 int idstart = 0;
1452 bool idfillbyfirst_y = true;
1453 int idstepbyrow = 0;
1454 int idstep = 1;
1455
1456 // The shape!
1457 // Given that this leaf component is actually an assembly, its constituent
1458 // component detector shapes comes from its type attribute.
1459 const std::string shapeType = pType->getAttribute("type");
1460 std::shared_ptr<Geometry::IObject> shape = mapTypeNameToShape[shapeType];
1461
1462 // These parameters are in the TYPE defining RectangularDetector
1463 if (pType->hasAttribute("xpixels"))
1464 xpixels = std::stoi(pType->getAttribute("xpixels"));
1465 double xstart = attrToDouble(pType, "xstart");
1466 double xstep = attrToDouble(pType, "xstep");
1467
1468 if (pType->hasAttribute("ypixels"))
1469 ypixels = std::stoi(pType->getAttribute("ypixels"));
1470 double ystart = attrToDouble(pType, "ystart");
1471 double ystep = attrToDouble(pType, "ystep");
1472
1473 // THESE parameters are in the INSTANCE of this type - since they will
1474 // change.
1475 if (pCompElem->hasAttribute("idstart"))
1476 idstart = std::stoi(pCompElem->getAttribute("idstart"));
1477 if (pCompElem->hasAttribute("idfillbyfirst"))
1478 idfillbyfirst_y = (pCompElem->getAttribute("idfillbyfirst") == "y");
1479 // Default ID row step size
1480 if (idfillbyfirst_y)
1481 idstepbyrow = ypixels;
1482 else
1483 idstepbyrow = xpixels;
1484 if (pCompElem->hasAttribute("idstepbyrow")) {
1485 idstepbyrow = std::stoi(pCompElem->getAttribute("idstepbyrow"));
1486 }
1487 // Default ID row step size
1488 if (pCompElem->hasAttribute("idstep"))
1489 idstep = std::stoi(pCompElem->getAttribute("idstep"));
1490
1491 setSideBySideViewLocation(bank, pCompElem);
1492
1493 // Now, initialize all the pixels in the bank
1494 bank->initialize(shape, xpixels, xstart, xstep, ypixels, ystart, ystep, idstart, idfillbyfirst_y, idstepbyrow,
1495 idstep);
1496
1497 // Loop through all detectors in the newly created bank and mark those in
1498 // the instrument.
1499 try {
1500 for (int x = 0; x < bank->nelements(); x++) {
1501 std::shared_ptr<Geometry::ICompAssembly> xColumn = std::dynamic_pointer_cast<Geometry::ICompAssembly>((*bank)[x]);
1502 for (int y = 0; y < xColumn->nelements(); y++) {
1503 std::shared_ptr<Geometry::Detector> detector = std::dynamic_pointer_cast<Geometry::Detector>((*xColumn)[y]);
1504 if (detector) {
1505 // Make default facing for the pixel
1506 auto *comp = static_cast<IComponent *>(detector.get());
1509 // Mark it as a detector (add to the instrument cache)
1510 m_instrument->markAsDetectorIncomplete(detector.get());
1511 }
1512 }
1513 }
1514 } catch (Kernel::Exception::ExistsError &) {
1515 throw Kernel::Exception::InstrumentDefinitionError("Duplicate detector ID found when adding RectangularDetector " +
1516 name + " in XML instrument file" + filename);
1517 }
1518}
1519
1521 const Poco::XML::Element *pLocElem,
1522 const Poco::XML::Element *pCompElem,
1523 const std::string &filename,
1524 const Poco::XML::Element *pType) {
1525 //-------------- Create a StructuredDetector
1526 //------------------------------------------------
1527 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1528
1529 // Create the bank with the given parent.
1530 auto bank = new Geometry::StructuredDetector(name, parent);
1531
1532 // set location for this newly added comp and set facing if specified in
1533 // instrument def. file. Also
1534 // check if any logfiles are referred to through the <parameter> element.
1536 setLogfile(bank, pCompElem,
1537 m_instrument->getLogfileCache()); // params specified within <component>
1538 setLogfile(bank, pLocElem,
1539 m_instrument->getLogfileCache()); // params specified within specific <location>
1540
1541 // Extract all the parameters from the XML attributes
1542 int xpixels = 0;
1543 int ypixels = 0;
1544 int idstart = 0;
1545 bool idfillbyfirst_y = true;
1546 int idstepbyrow = 0;
1547 int idstep = 1;
1548 std::vector<double> xValues;
1549 std::vector<double> yValues;
1550
1551 std::string typeName = pType->getAttribute("name");
1552 // These parameters are in the TYPE defining StructuredDetector
1553 if (pType->hasAttribute("xpixels"))
1554 xpixels = std::stoi(pType->getAttribute("xpixels"));
1555 if (pType->hasAttribute("ypixels"))
1556 ypixels = std::stoi(pType->getAttribute("ypixels"));
1557
1558 // THESE parameters are in the INSTANCE of this type - since they will
1559 // change.
1560 if (pCompElem->hasAttribute("idstart"))
1561 idstart = std::stoi(pCompElem->getAttribute("idstart"));
1562 if (pCompElem->hasAttribute("idfillbyfirst"))
1563 idfillbyfirst_y = (pCompElem->getAttribute("idfillbyfirst") == "y");
1564 // Default ID row step size
1565 if (idfillbyfirst_y)
1566 idstepbyrow = ypixels;
1567 else
1568 idstepbyrow = xpixels;
1569 if (pCompElem->hasAttribute("idstepbyrow")) {
1570 idstepbyrow = std::stoi(pCompElem->getAttribute("idstepbyrow"));
1571 }
1572 // Default ID row step size
1573 if (pCompElem->hasAttribute("idstep"))
1574 idstep = std::stoi(pCompElem->getAttribute("idstep"));
1575
1576 // Access type element which defines structured detecor vertices
1577 Element *pElem = nullptr;
1578 NodeIterator tags(pCompElem->ownerDocument(), NodeFilter::SHOW_ELEMENT);
1579 Node *pNode = tags.nextNode();
1580
1581 while (pNode) {
1582 auto *check = static_cast<Element *>(pNode);
1583 if (pNode->nodeName() == "type" && check->hasAttribute("is")) {
1584 std::string is = check->getAttribute("is");
1585 if (StructuredDetector::compareName(is) && typeName == check->getAttribute("name")) {
1586 pElem = check;
1587 break;
1588 }
1589 }
1590
1591 pNode = tags.nextNode();
1592 }
1593
1594 if (pElem == nullptr)
1595 throw Kernel::Exception::InstrumentDefinitionError("No <type> with attribute is=\"StructuredDetector\"", filename);
1596
1597 // Ensure vertices are present within the IDF
1598 Poco::AutoPtr<NodeList> pNL = pElem->getElementsByTagName("vertex");
1599 if (pNL->length() == 0)
1600 throw Kernel::Exception::InstrumentDefinitionError("StructuredDetector must contain vertices.", filename);
1601
1602 NodeIterator it(pElem, NodeFilter::SHOW_ELEMENT);
1603
1604 pNode = it.nextNode();
1605
1606 while (pNode) {
1607 if (pNode->nodeName() == "vertex") {
1608 auto *pVertElem = static_cast<Element *>(pNode);
1609
1610 if (pVertElem->hasAttribute("x"))
1611 xValues.emplace_back(attrToDouble(pVertElem, "x"));
1612 if (pVertElem->hasAttribute("y"))
1613 yValues.emplace_back(attrToDouble(pVertElem, "y"));
1614 }
1615
1616 pNode = it.nextNode();
1617 }
1618
1619 V3D zVector(0, 0, 1); // Z aligned beam
1620 bool isZBeam = m_instrument->getReferenceFrame()->isVectorPointingAlongBeam(zVector);
1621 // Now, initialize all the pixels in the bank
1622 bank->initialize(xpixels, ypixels, std::move(xValues), std::move(yValues), isZBeam, idstart, idfillbyfirst_y,
1623 idstepbyrow, idstep);
1624
1625 // Loop through all detectors in the newly created bank and mark those in
1626 // the instrument.
1627 try {
1628 for (int x = 0; x < bank->nelements(); x++) {
1629 std::shared_ptr<Geometry::ICompAssembly> xColumn = std::dynamic_pointer_cast<Geometry::ICompAssembly>((*bank)[x]);
1630 for (int y = 0; y < xColumn->nelements(); y++) {
1631 std::shared_ptr<Geometry::Detector> detector = std::dynamic_pointer_cast<Geometry::Detector>((*xColumn)[y]);
1632 if (detector) {
1633 // Make default facing for the pixel
1634 auto *comp = static_cast<IComponent *>(detector.get());
1637 // Mark it as a detector (add to the instrument cache)
1638 m_instrument->markAsDetectorIncomplete(detector.get());
1639 }
1640 }
1641 }
1642 } catch (Kernel::Exception::ExistsError &) {
1643 throw Kernel::Exception::InstrumentDefinitionError("Duplicate detector ID found when adding StructuredDetector " +
1644 name + " in XML instrument file" + filename);
1645 }
1646}
1647
1648//-----------------------------------------------------------------------------------------------------------------------
1667void InstrumentDefinitionParser::appendLeaf(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem,
1668 const Poco::XML::Element *pCompElem, IdList &idList) {
1669 const std::string filename = m_xmlFile->getFileFullPathStr();
1670
1671 //--- Get the detector's X/Y pixel sizes (optional) ---
1672 // Read detector IDs into idlist if required
1673 // Note idlist may be defined for any component
1674 // Note any new idlist found will take precedence.
1675
1676 if (pCompElem->hasAttribute("idlist")) {
1677 std::string idlist = pCompElem->getAttribute("idlist");
1678
1679 if (idlist != idList.idname) {
1680 Element *pFound = pCompElem->ownerDocument()->getElementById(idlist, "idname");
1681
1682 if (pFound == nullptr) {
1684 "No <idlist> with name idname=\"" + idlist + "\" present in instrument definition file.", filename);
1685 }
1686
1687 idList.reset();
1688 populateIdList(pFound, idList);
1689 }
1690 }
1691
1692 // get the type element of the component element in order to determine if
1693 // the
1694 // type
1695 // belong to the category: "detector", "SamplePos or "Source".
1696
1697 std::string typeName = pCompElem->getAttribute("type");
1698 Element *pType = getTypeElement[typeName];
1699
1700 std::string category;
1701 if (pType->hasAttribute("is"))
1702 category = pType->getAttribute("is");
1703
1704 static const boost::regex exp("Detector|detector|Monitor|monitor");
1705
1706 // do stuff a bit differently depending on which category the type belong to
1707 if (GridDetector::compareName(category)) {
1708 createGridDetector(parent, pLocElem, pCompElem, filename, pType);
1709 } else if (RectangularDetector::compareName(category)) {
1710 createRectangularDetector(parent, pLocElem, pCompElem, filename, pType);
1711 } else if (StructuredDetector::compareName(category)) {
1712 createStructuredDetector(parent, pLocElem, pCompElem, filename, pType);
1713 } else if (boost::regex_match(category, exp)) {
1714 createDetectorOrMonitor(parent, pLocElem, pCompElem, filename, idList, category);
1715 } else {
1716 //-------------- Not a Detector, RectangularDetector or Structured Detector
1717 //------------------------------
1718 IComponent *comp;
1719 if (category == "SamplePos" || category == "samplePos") {
1720 // check if special SamplePos Component
1721 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1722 comp = new Geometry::Component(name, parent);
1723 m_instrument->markAsSamplePos(comp);
1724 } else {
1725 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1726
1727 comp = new Geometry::ObjComponent(name, mapTypeNameToShape[typeName], parent);
1728 }
1729 parent->add(comp);
1730
1731 // check if special Source Component
1732 if (category == "Source" || category == "source") {
1733 m_instrument->markAsSource(comp);
1734 }
1735
1736 // set location for this newly added comp and set facing if specified in
1737 // instrument def. file. Also
1738 // check if any logfiles are referred to through the <parameter> element.
1739
1741 setFacing(comp, pLocElem);
1742 setLogfile(comp, pCompElem,
1743 m_instrument->getLogfileCache()); // params specified within <component>
1744 setLogfile(comp, pLocElem,
1745 m_instrument->getLogfileCache()); // params specified within
1746 // specific <location>
1747 }
1748}
1749
1750//-----------------------------------------------------------------------------------------------------------------------
1760void InstrumentDefinitionParser::populateIdList(Poco::XML::Element *pE, IdList &idList) {
1761 const std::string filename = m_xmlFile->getFileFullPathStr();
1762
1763 if ((pE->tagName()) != "idlist") {
1764 g_log.error("Argument to function createIdList must be a pointer to an XML "
1765 "element with tag name idlist.");
1766 throw std::logic_error("Argument to function createIdList must be a "
1767 "pointer to an XML element with tag name idlist.");
1768 }
1769
1770 // set name of idlist
1771
1772 idList.idname = pE->getAttribute("idname");
1773
1774 // If idname element has start and end attributes then just use those to
1775 // populate idlist.
1776 // Otherwise id sub-elements
1777
1778 if (pE->hasAttribute("start")) {
1779 int startID = std::stoi(pE->getAttribute("start"));
1780
1781 int endID;
1782 if (pE->hasAttribute("end"))
1783 endID = std::stoi(pE->getAttribute("end"));
1784 else
1785 endID = startID;
1786
1787 int increment = 1;
1788 if (pE->hasAttribute("step"))
1789 increment = std::stoi(pE->getAttribute("step"));
1790
1791 if (0 == increment) {
1792 std::stringstream ss;
1793 ss << "The step element cannot be zero, got start: " << startID << ", end: " << endID << ", step: " << increment;
1794 throw Kernel::Exception::InstrumentDefinitionError(ss.str(), filename);
1795 }
1796
1797 // check the start end and increment values are sensible
1798 int steps = (endID - startID) / increment;
1799 if (steps < 0) {
1800 std::stringstream ss;
1801 ss << "The start, end, and step elements do not allow a single id in "
1802 "the "
1803 "idlist entry - ";
1804 ss << "start: " << startID << ", end: " << endID << ", step: " << increment;
1805
1806 throw Kernel::Exception::InstrumentDefinitionError(ss.str(), filename);
1807 }
1808
1809 idList.vec.reserve(steps);
1810 for (int i = startID; i != endID + increment; i += increment) {
1811 idList.vec.emplace_back(i);
1812 }
1813 } else {
1814 // test first if any <id> elements
1815
1816 Poco::AutoPtr<NodeList> pNL = pE->getElementsByTagName("id");
1817
1818 if (pNL->length() == 0) {
1819 throw Kernel::Exception::InstrumentDefinitionError("No id subelement of idlist element in XML instrument file",
1820 filename);
1821 }
1822
1823 // get id numbers
1824
1825 NodeIterator it(pE, NodeFilter::SHOW_ELEMENT);
1826
1827 Node *pNode = it.nextNode();
1828 while (pNode) {
1829 if (pNode->nodeName() == "id") {
1830 auto *pIDElem = static_cast<Element *>(pNode);
1831
1832 if (pIDElem->hasAttribute("val")) {
1833 int valID = std::stoi(pIDElem->getAttribute("val"));
1834 idList.vec.emplace_back(valID);
1835 } else if (pIDElem->hasAttribute("start")) {
1836 int startID = std::stoi(pIDElem->getAttribute("start"));
1837
1838 int endID;
1839 if (pIDElem->hasAttribute("end"))
1840 endID = std::stoi(pIDElem->getAttribute("end"));
1841 else
1842 endID = startID;
1843
1844 int increment = 1;
1845 if (pIDElem->hasAttribute("step"))
1846 increment = std::stoi(pIDElem->getAttribute("step"));
1847
1848 // check the start end and increment values are sensible
1849 if (0 == increment) {
1850 std::stringstream ss;
1851 ss << "The step element cannot be zero, found step: " << increment;
1852
1853 throw Kernel::Exception::InstrumentDefinitionError(ss.str(), filename);
1854 }
1855 int numSteps = (endID - startID) / increment;
1856 if (numSteps < 0) {
1857 std::stringstream ss;
1858 ss << "The start, end, and step elements do not allow a single "
1859 "id "
1860 "in the idlist entry - ";
1861 ss << "start: " << startID << ", end: " << endID << ", step: " << increment;
1862
1863 throw Kernel::Exception::InstrumentDefinitionError(ss.str(), filename);
1864 }
1865
1866 idList.vec.reserve(numSteps);
1867 for (int i = startID; i != endID + increment; i += increment) {
1868 idList.vec.emplace_back(i);
1869 }
1870 } else {
1872 "id subelement of idlist " + std::string("element wrongly specified in XML instrument file"), filename);
1873 }
1874 }
1875
1876 pNode = it.nextNode();
1877 } // end while loop
1878 }
1879}
1880
1881//-----------------------------------------------------------------------------------------------------------------------
1890bool InstrumentDefinitionParser::isAssembly(const std::string &type) const {
1891 const std::string filename = m_xmlFile->getFileFullPathStr();
1892 auto it = isTypeAssembly.find(type);
1893
1894 if (it == isTypeAssembly.end()) {
1895 throw Kernel::Exception::InstrumentDefinitionError("type with name = " + type + " not defined.", filename);
1896 }
1897
1898 return it->second;
1899}
1900
1901//-----------------------------------------------------------------------------------------------------------------------
1916
1917//-----------------------------------------------------------------------------------------------------------------------
1927 Kernel::V3D pos = in->getPos();
1928
1929 // vector from facing object to component we want to rotate
1930 Kernel::V3D facingDirection = pos - facingPoint;
1931 const auto facingDirLength = facingDirection.norm();
1932 if (facingDirLength == 0.0)
1933 return;
1934 facingDirection /= facingDirLength;
1935
1936 // now aim to rotate shape such that the z-axis of of the object we want to
1937 // rotate points in the direction of facingDirection. That way the XY plane
1938 // faces the 'facing object'.
1939 constexpr Kernel::V3D z(0, 0, 1);
1940 Kernel::Quat R = in->getRotation();
1941 R.inverse();
1942 R.rotate(facingDirection);
1943
1944 Kernel::V3D normal = facingDirection.cross_prod(z);
1945 const auto normalLength = normal.norm();
1946 if (normalLength == 0.) {
1947 normal = normalize(-facingDirection);
1948 } else {
1949 normal /= normalLength;
1950 }
1951 double theta = (180.0 / M_PI) * facingDirection.angle(z);
1952
1953 if (normal.norm() > 0.0)
1954 in->rotate(Kernel::Quat(-theta, normal));
1955 else {
1956 // To take into account the case where the facing direction is in the
1957 // (0,0,1) or (0,0,-1) direction.
1958 in->rotate(Kernel::Quat(-theta, Kernel::V3D(0, 1, 0)));
1959 }
1960}
1961
1962//-----------------------------------------------------------------------------------------------------------------------
1969 Kernel::V3D retV3D;
1970
1971 // Polar coordinates can be labelled as (r,t,p) or (R,theta,phi)
1972 if (pElem->hasAttribute("r") || pElem->hasAttribute("t") || pElem->hasAttribute("p") || pElem->hasAttribute("R") ||
1973 pElem->hasAttribute("theta") || pElem->hasAttribute("phi")) {
1974 double R = attrToDouble(pElem, "r");
1975 double theta = m_angleConvertConst * attrToDouble(pElem, "t");
1976 double phi = m_angleConvertConst * attrToDouble(pElem, "p");
1977
1978 if (pElem->hasAttribute("R"))
1979 R = attrToDouble(pElem, "R");
1980 if (pElem->hasAttribute("theta"))
1981 theta = m_angleConvertConst * attrToDouble(pElem, "theta");
1982 if (pElem->hasAttribute("phi"))
1983 phi = m_angleConvertConst * attrToDouble(pElem, "phi");
1984
1985 retV3D.spherical(R, theta, phi);
1986 } else {
1987 double x = attrToDouble(pElem, "x");
1988 double y = attrToDouble(pElem, "y");
1989 double z = attrToDouble(pElem, "z");
1990
1991 retV3D(x, y, z);
1992 }
1993
1994 return retV3D;
1995}
1996
1997//-----------------------------------------------------------------------------------------------------------------------
2011void InstrumentDefinitionParser::setFacing(Geometry::IComponent *comp, const Poco::XML::Element *pElem) {
2012 // Require that pElem points to an element with tag name 'location'
2013
2014 if ((pElem->tagName()) != "location") {
2015 g_log.error("Second argument to function setLocation must be a pointer to "
2016 "an XML element with tag name location.");
2017 throw std::logic_error("Second argument to function setLocation must be a "
2018 "pointer to an XML element with tag name location.");
2019 }
2020
2021 Element *facingElem = pElem->getChildElement("facing");
2022 if (facingElem) {
2023 // check if user want to rotate about z-axis before potentially applying
2024 // facing
2025
2026 if (facingElem->hasAttribute("rot")) {
2027 double rotAngle = m_angleConvertConst * attrToDouble(facingElem, "rot"); // assumed to be in degrees
2028 comp->rotate(Kernel::Quat(rotAngle, Kernel::V3D(0, 0, 1)));
2029 }
2030
2031 // For now assume that if has val attribute it means facing = none. This
2032 // option only has an
2033 // effect when a default facing setting is set. In which case this then
2034 // means "ignore the
2035 // default facing setting" for this component
2036
2037 if (facingElem->hasAttribute("val"))
2038 return;
2039
2040 // Face the component, i.e. rotate the z-axis of the component such that
2041 // it
2042 // points in the direction from
2043 // the point x,y,z (or r,t,p) specified by the <facing> xml element
2044 // towards
2045 // the component
2046
2048
2049 } else // so if no facing element associated with location element apply
2050 // default facing if set
2053}
2054
2055//-----------------------------------------------------------------------------------------------------------------------
2066void InstrumentDefinitionParser::setLogfile(const Geometry::IComponent *comp, const Poco::XML::Element *pElem,
2067 InstrumentParameterCache &logfileCache, const std::string &requestedDate) {
2068 const std::string filename = m_xmlFile->getFileFullPathStr();
2069
2070 // The purpose below is to have a quicker way to judge if pElem contains a
2071 // parameter, see
2072 // defintion of m_hasParameterElement for more info
2074 if (m_hasParameterElement.end() == std::find(m_hasParameterElement.begin(), m_hasParameterElement.end(), pElem))
2075 return;
2076
2077 Poco::AutoPtr<NodeList> pNL_comp = pElem->childNodes(); // here get all child nodes
2078 unsigned long pNL_comp_length = pNL_comp->length();
2079
2080 for (unsigned long i = 0; i < pNL_comp_length; i++) {
2081 // we are only interest in the top level parameter elements hence
2082 // the reason for the if statement below
2083 if (!((pNL_comp->item(i))->nodeType() == Node::ELEMENT_NODE && ((pNL_comp->item(i))->nodeName()) == "parameter"))
2084 continue;
2085
2086 auto *pParamElem = static_cast<Element *>(pNL_comp->item(i));
2087
2088 if (!pParamElem->hasAttribute("name"))
2090 "XML element with name or type = " + comp->getName() +
2091 " contain <parameter> element with no name attribute in XML "
2092 "instrument file",
2093 filename);
2094
2095 std::string paramName = pParamElem->getAttribute("name");
2096
2097 if (paramName == "rot" || paramName == "pos") {
2098 g_log.error() << "XML element with name or type = " << comp->getName()
2099 << " contains <parameter> element with name=\"" << paramName << "\"."
2100 << " This is a reserved Mantid keyword. Please use other name, "
2101 << "and see docs.mantidproject.org/concepts/InstrumentDefinitionFile for list of reserved "
2102 "keywords."
2103 << " This parameter is ignored";
2104 continue;
2105 }
2106
2107 std::string visible = "true";
2108 if (pParamElem->hasAttribute("visible")) {
2109 visible = pParamElem->getAttribute("visible");
2110 }
2111
2112 DateAndTime validityDate;
2113
2114 if (requestedDate.empty()) {
2115 validityDate = DateAndTime::getCurrentTime();
2116 } else {
2117 validityDate.setFromISO8601(requestedDate);
2118 }
2119
2120 std::string logfileID;
2121 std::string value;
2122
2123 DateAndTime validFrom;
2124 DateAndTime validTo;
2125
2126 std::string type = "double"; // default
2127 std::string extractSingleValueAs = "mean"; // default
2128 std::string eq;
2129
2130 Poco::AutoPtr<NodeList> pNLvalue = pParamElem->getElementsByTagName("value");
2131 size_t numberValueEle = pNLvalue->length();
2132 Element *pValueElem;
2133
2134 Poco::AutoPtr<NodeList> pNLlogfile = pParamElem->getElementsByTagName("logfile");
2135 size_t numberLogfileEle = pNLlogfile->length();
2136 Element *pLogfileElem;
2137
2138 Poco::AutoPtr<NodeList> pNLLookUp = pParamElem->getElementsByTagName("lookuptable");
2139 size_t numberLookUp = pNLLookUp->length();
2140
2141 Poco::AutoPtr<NodeList> pNLFormula = pParamElem->getElementsByTagName("formula");
2142 size_t numberFormula = pNLFormula->length();
2143
2144 if ((numberValueEle > 0 && numberLogfileEle + numberLookUp + numberFormula > 0) ||
2145 (numberValueEle == 0 && numberLogfileEle + numberLookUp + numberFormula > 1)) {
2146 g_log.warning() << "XML element with name or type = " << comp->getName()
2147 << " contains <parameter> element where the value of the "
2148 << "parameter has been specified more than once. See "
2149 << "docs.mantidproject.org/concepts/InstrumentDefinitionFile for how the value of the "
2150 << "parameter is set in this case.";
2151 }
2152
2153 if (numberValueEle + numberLogfileEle + numberLookUp + numberFormula == 0) {
2154 g_log.error() << "XML element with name or type = " << comp->getName()
2155 << " contains <parameter> for which no value is specified."
2156 << " See docs.mantidproject.org/concepts/InstrumentDefinitionFile for how to set the value"
2157 << " of a parameter. This parameter is ignored.";
2158 continue;
2159 }
2160
2161 DateAndTime currentValidFrom;
2162 DateAndTime currentValidTo;
2163 currentValidFrom.setToMinimum();
2164 currentValidTo.setToMaximum();
2165
2166 // if more than one <value> specified for a parameter, check the validity
2167 // range
2168 if (numberValueEle >= 1) {
2169 bool hasValue = false;
2170
2171 for (unsigned long j = 0; j < numberValueEle; ++j) {
2172 pValueElem = static_cast<Element *>(pNLvalue->item(j));
2173
2174 if (!pValueElem->hasAttribute(("val")))
2175 continue;
2176
2177 validFrom.setToMinimum();
2178 if (pValueElem->hasAttribute("valid-from"))
2179 validFrom.setFromISO8601(pValueElem->getAttribute("valid-from"));
2180
2181 validTo.setToMaximum();
2182 if (pValueElem->hasAttribute("valid-to"))
2183 validTo.setFromISO8601(pValueElem->getAttribute("valid-to"));
2184
2185 if (validFrom <= validityDate && validityDate <= validTo &&
2186 (validFrom > currentValidFrom || (validFrom == currentValidFrom && validTo <= currentValidTo))) {
2187
2188 currentValidFrom = validFrom;
2189 currentValidTo = validTo;
2190 } else
2191 continue;
2192 hasValue = true;
2193 value = pValueElem->getAttribute("val");
2194 }
2195
2196 if (!hasValue) {
2198 "XML element with name or type = " + comp->getName() +
2199 " contains <parameter> element with invalid syntax for its "
2200 "subelement <value>. Correct syntax is <value val=\"\"/>",
2201 filename);
2202 }
2203
2204 } else if (numberLogfileEle >= 1) {
2205 // <logfile > tag was used at least once.
2206 pLogfileElem = static_cast<Element *>(pNLlogfile->item(0));
2207 if (!pLogfileElem->hasAttribute("id"))
2209 "XML element with name or type = " + comp->getName() +
2210 " contains <parameter> element with invalid syntax for its "
2211 "subelement logfile>." +
2212 " Correct syntax is <logfile id=\"\"/>",
2213 filename);
2214 logfileID = pLogfileElem->getAttribute("id");
2215
2216 if (pLogfileElem->hasAttribute("eq"))
2217 eq = pLogfileElem->getAttribute("eq");
2218 if (pLogfileElem->hasAttribute("extract-single-value-as"))
2219 extractSingleValueAs = pLogfileElem->getAttribute("extract-single-value-as");
2220 }
2221
2222 if (pParamElem->hasAttribute("type"))
2223 type = pParamElem->getAttribute("type");
2224
2225 // check if <fixed /> element present
2226
2227 bool fixed = false;
2228 Poco::AutoPtr<NodeList> pNLFixed = pParamElem->getElementsByTagName("fixed");
2229 size_t numberFixed = pNLFixed->length();
2230 if (numberFixed >= 1) {
2231 fixed = true;
2232 }
2233
2234 // some processing
2235
2236 std::string fittingFunction;
2237 std::string tie;
2238
2239 if (type == "fitting") {
2240 size_t found = paramName.find(':');
2241 if (found != std::string::npos) {
2242 // check that only one : in name
2243 size_t index = paramName.find(':', found + 1);
2244 if (index != std::string::npos) {
2245 g_log.error() << "Fitting <parameter> in instrument definition file defined "
2246 "with"
2247 << " more than one column character :. One must used.\n";
2248 } else {
2249 fittingFunction = paramName.substr(0, found);
2250 paramName = paramName.substr(found + 1, paramName.size());
2251 }
2252 }
2253 }
2254
2255 if (fixed) {
2256 std::ostringstream str;
2257 str << paramName << "=" << value;
2258 tie = str.str();
2259 }
2260
2261 // check if <min> or <max> elements present
2262
2263 std::vector<std::string> constraint(2, "");
2264
2265 Poco::AutoPtr<NodeList> pNLMin = pParamElem->getElementsByTagName("min");
2266 size_t numberMin = pNLMin->length();
2267 Poco::AutoPtr<NodeList> pNLMax = pParamElem->getElementsByTagName("max");
2268 size_t numberMax = pNLMax->length();
2269
2270 if (numberMin >= 1) {
2271 auto *pMin = static_cast<Element *>(pNLMin->item(0));
2272 constraint[0] = pMin->getAttribute("val");
2273 }
2274 if (numberMax >= 1) {
2275 auto *pMax = static_cast<Element *>(pNLMax->item(0));
2276 constraint[1] = pMax->getAttribute("val");
2277 }
2278
2279 // check if penalty-factor> elements present
2280
2281 std::string penaltyFactor;
2282
2283 Poco::AutoPtr<NodeList> pNL_penaltyFactor = pParamElem->getElementsByTagName("penalty-factor");
2284 size_t numberPenaltyFactor = pNL_penaltyFactor->length();
2285
2286 if (numberPenaltyFactor >= 1) {
2287 auto *pPenaltyFactor = static_cast<Element *>(pNL_penaltyFactor->item(0));
2288 penaltyFactor = pPenaltyFactor->getAttribute("val");
2289 }
2290
2291 // Check if look up table is specified
2292
2293 std::vector<std::string> allowedUnits = UnitFactory::Instance().getKeys();
2294
2295 std::shared_ptr<Interpolation> interpolation = std::make_shared<Interpolation>();
2296
2297 if (numberLookUp >= 1) {
2298 auto *pLookUp = static_cast<Element *>(pNLLookUp->item(0));
2299
2300 if (pLookUp->hasAttribute("interpolation"))
2301 interpolation->setMethod(pLookUp->getAttribute("interpolation"));
2302 if (pLookUp->hasAttribute("x-unit")) {
2303 std::vector<std::string>::iterator it;
2304 it = find(allowedUnits.begin(), allowedUnits.end(), pLookUp->getAttribute("x-unit"));
2305 if (it == allowedUnits.end()) {
2306 g_log.warning() << "x-unit used with interpolation table must be "
2307 "one of the recognised units "
2308 << " see http://docs.mantidproject.org/concepts/UnitFactory";
2309 } else
2310 interpolation->setXUnit(pLookUp->getAttribute("x-unit"));
2311 }
2312 if (pLookUp->hasAttribute("y-unit")) {
2313 std::vector<std::string>::iterator it;
2314 it = find(allowedUnits.begin(), allowedUnits.end(), pLookUp->getAttribute("y-unit"));
2315 if (it == allowedUnits.end()) {
2316 g_log.warning() << "y-unit used with interpolation table must be "
2317 "one of the recognised units "
2318 << " see http://docs.mantidproject.org/concepts/UnitFactory";
2319 } else
2320 interpolation->setYUnit(pLookUp->getAttribute("y-unit"));
2321 }
2322
2323 Poco::AutoPtr<NodeList> pNLpoint = pLookUp->getElementsByTagName("point");
2324 unsigned long numberPoint = pNLpoint->length();
2325
2326 for (unsigned long j = 0; j < numberPoint; j++) {
2327 const auto *pPoint = static_cast<Element *>(pNLpoint->item(j));
2328 double x = attrToDouble(pPoint, "x");
2329 double y = attrToDouble(pPoint, "y");
2330 interpolation->addPoint(x, y);
2331 }
2332 }
2333
2334 // Check if formula is specified
2335
2336 std::string formula;
2337 std::string formulaUnit;
2338 std::string resultUnit;
2339
2340 if (numberFormula >= 1) {
2341 auto *pFormula = static_cast<Element *>(pNLFormula->item(0));
2342 formula = pFormula->getAttribute("eq");
2343 if (pFormula->hasAttribute("unit")) {
2344 std::vector<std::string>::iterator it;
2345 it = find(allowedUnits.begin(), allowedUnits.end(), pFormula->getAttribute("unit"));
2346 if (it == allowedUnits.end()) {
2347 g_log.warning() << "unit attribute used with formula must be one "
2348 "of the recognized units "
2349 << " see http://docs.mantidproject.org/concepts/UnitFactory";
2350 } else
2351 formulaUnit = pFormula->getAttribute("unit");
2352 }
2353 if (pFormula->hasAttribute("result-unit"))
2354 resultUnit = pFormula->getAttribute("result-unit");
2355 }
2356 // Check if parameter description is
2357 std::string description;
2358
2359 Poco::AutoPtr<NodeList> pNLDescription = pParamElem->getElementsByTagName("description");
2360 size_t numberDescription = pNLDescription->length();
2361
2362 if (numberDescription >= 1) {
2363 // use only first description from a list
2364 auto *pDescription = static_cast<Element *>(pNLDescription->item(0));
2365 description = pDescription->getAttribute("is");
2366 }
2367
2368 // Qualify the cache key with the fitting-function name (when present) so that two functions
2369 // attached to the same component sharing a parameter name (e.g. IkedaCarpenterPV:Alpha0 and
2370 // IkedaCarpenterMD:Alpha0) do not overwrite each other. The stored XMLInstrumentParameter
2371 // still keeps the unqualified paramName so downstream lookups by short name continue to work.
2372 const std::string cacheParamKey = fittingFunction.empty() ? paramName : fittingFunction + ":" + paramName;
2373 auto cacheKey = std::make_pair(cacheParamKey, comp);
2374 auto cacheValue = std::make_shared<XMLInstrumentParameter>(
2375 logfileID, value, interpolation, formula, formulaUnit, resultUnit, paramName, type, tie, constraint,
2376 penaltyFactor, fittingFunction, extractSingleValueAs, eq, comp, m_angleConvertConst, description, visible);
2377 auto inserted = logfileCache.emplace(cacheKey, cacheValue);
2378 if (!inserted.second) {
2379 logfileCache[cacheKey] = cacheValue;
2380 }
2381 } // end element loop
2382}
2383
2384//-----------------------------------------------------------------------------------------------------------------------
2396void InstrumentDefinitionParser::setComponentLinks(std::shared_ptr<Geometry::Instrument> &instrument,
2397 Poco::XML::Element *pRootElem, Kernel::ProgressBase *progress,
2398 const std::string &requestedDate) {
2399 // check if any logfile cache units set. As of this writing the only unit to
2400 // check is if "angle=radian"
2401 std::map<std::string, std::string> &units = instrument->getLogfileUnit();
2402 std::map<std::string, std::string>::iterator unit_it;
2403 unit_it = units.find("angle");
2404 if (unit_it != units.end())
2405 if (unit_it->second == "radian")
2406 m_angleConvertConst = 180.0 / M_PI;
2407
2408 const std::string elemName = "component-link";
2409 Poco::AutoPtr<NodeList> pNL_link = pRootElem->getElementsByTagName(elemName);
2410 unsigned long numberLinks = pNL_link->length();
2411
2412 if (progress)
2413 progress->resetNumSteps(static_cast<int64_t>(numberLinks), 0.0, 0.95);
2414
2415 Node *curNode = pRootElem->firstChild();
2416 while (curNode) {
2417 if (curNode->nodeType() == Node::ELEMENT_NODE && curNode->nodeName() == elemName) {
2418 auto *curElem = static_cast<Element *>(curNode);
2419
2420 if (progress) {
2421 if (progress->hasCancellationBeenRequested())
2422 return;
2423 progress->report("Loading parameters");
2424 }
2425
2426 std::string id = curElem->getAttribute("id");
2427 std::string name = curElem->getAttribute("name");
2428 std::vector<std::shared_ptr<const Geometry::IComponent>> sharedIComp;
2429
2430 // If available, use the detector id as it's the most specific.
2431 if (id.length() > 0) {
2432 int detid;
2433 std::stringstream(id) >> detid;
2434 std::shared_ptr<const Geometry::IComponent> detector = instrument->getDetector(static_cast<detid_t>(detid));
2435
2436 // If we didn't find anything with the detector id, explain why to the
2437 // user, and throw an exception.
2438 if (!detector) {
2439 g_log.error() << "Error whilst loading parameters. No detector "
2440 "found with id '"
2441 << detid << "'\n";
2442 g_log.error() << "Please check that your detectors' ids are correct.\n";
2443 throw Kernel::Exception::InstrumentDefinitionError("Invalid detector id in component-link tag.");
2444 }
2445
2446 sharedIComp.emplace_back(detector);
2447
2448 // If the user also supplied a name, make sure it's consistent with the detector id.
2449 if (name.length() > 0) {
2450 auto comp = std::dynamic_pointer_cast<const IComponent>(detector);
2451 if (comp) {
2452 bool consistent = (comp->getFullName() == name || comp->getName() == name);
2453 if (!consistent) {
2454 g_log.warning() << "Error whilst loading parameters. Name '" << name << "' does not match id '" << detid
2455 << "'.\n";
2456 g_log.warning() << "Parameters have been applied to detector with id '" << detid
2457 << "'. Please check the name is correct.\n";
2458 }
2459 }
2460 }
2461 } else {
2462 // No detector id given, fall back to using the name
2463 if (name.find('/', 0) == std::string::npos) { // Simple name, look for all components of that name.
2464 sharedIComp = instrument->getAllComponentsWithName(name);
2465 } else { // Pathname given. Assume it is unique.
2466 std::shared_ptr<const Geometry::IComponent> shared = instrument->getComponentByName(name);
2467 sharedIComp.emplace_back(shared);
2468 }
2469 }
2470
2471 for (auto &ptr : sharedIComp) {
2472 std::shared_ptr<const Geometry::Component> sharedComp =
2473 std::dynamic_pointer_cast<const Geometry::Component>(ptr);
2474 if (sharedComp) { // Not empty Component
2475 if (sharedComp->isParametrized()) {
2476 setLogfile(sharedComp->base(), curElem, instrument->getLogfileCache(), requestedDate);
2477 } else {
2478 setLogfile(ptr.get(), curElem, instrument->getLogfileCache(), requestedDate);
2479 }
2480 }
2481 }
2482 }
2483 curNode = curNode->nextSibling();
2484 }
2485}
2486
2492 const std::string cacheFullPath = cacheToApply->getFileFullPathStr();
2493 g_log.information("Loading geometry cache from " + cacheFullPath);
2494 // create a vtk reader
2495 std::map<std::string, std::shared_ptr<Geometry::IObject>>::iterator objItr;
2496 std::shared_ptr<Mantid::Geometry::vtkGeometryCacheReader> reader(
2497 new Mantid::Geometry::vtkGeometryCacheReader(cacheFullPath));
2498 for (objItr = mapTypeNameToShape.begin(); objItr != mapTypeNameToShape.end(); ++objItr) {
2499 // caching only applies to CSGObject
2500 if (auto csgObj = std::dynamic_pointer_cast<CSGObject>(((*objItr).second))) {
2501 csgObj->setVtkGeometryCacheReader(reader);
2502 }
2503 }
2504}
2505
2513 IDFObject_const_sptr fallBackCache) {
2514 IDFObject_const_sptr usedCache = std::move(firstChoiceCache);
2515 auto cachingOption = WroteGeomCache;
2516
2517 g_log.notice("Geometry cache is not available");
2518 try {
2519 std::filesystem::path dir = usedCache->getParentDirectory();
2520 if (!dir.empty() && !std::filesystem::exists(dir)) {
2521 usedCache = std::move(fallBackCache);
2522 cachingOption = WroteCacheTemp;
2523 g_log.information() << "Geometrycache directory does not exist, writing cache "
2524 "to system temp.\n";
2525 } else if (!dir.empty() && (std::filesystem::status(dir).permissions() & std::filesystem::perms::owner_write) ==
2526 std::filesystem::perms::none) {
2527 usedCache = std::move(fallBackCache);
2528 cachingOption = WroteCacheTemp;
2529 g_log.information() << "Geometrycache directory is read only, writing cache "
2530 "to system temp.\n";
2531 }
2532 } catch (std::filesystem::filesystem_error &) {
2533 g_log.error() << "Unable to find instrument definition while attempting to "
2534 "write cache.\n";
2535 throw std::runtime_error("Unable to find instrument definition while "
2536 "attempting to write cache.\n");
2537 }
2538 const std::string cacheFullPath = usedCache->getFileFullPathStr();
2539 g_log.notice() << "Creating cache in " << cacheFullPath << "\n";
2540 // create a vtk writer
2541 std::map<std::string, std::shared_ptr<Geometry::IObject>>::iterator objItr;
2542 std::shared_ptr<Mantid::Geometry::vtkGeometryCacheWriter> writer(
2543 new Mantid::Geometry::vtkGeometryCacheWriter(cacheFullPath));
2544 for (objItr = mapTypeNameToShape.begin(); objItr != mapTypeNameToShape.end(); ++objItr) {
2545 // caching only applies to CSGObject
2546 if (auto csgObj = std::dynamic_pointer_cast<CSGObject>(((*objItr).second))) {
2547 csgObj->setVtkGeometryCacheWriter(writer);
2548 }
2549 }
2550 writer->write();
2551 return cachingOption;
2552}
2553
2558 // Get cached file name
2559 // If the instrument directory is writable, put them there else use
2560 // temporary
2561 // directory.
2562 std::filesystem::path fallBackPath =
2563 std::filesystem::path(ConfigService::Instance().getTempDir()) / (this->getMangledName() + ".vtp");
2564 IDFObject_const_sptr fallBackCache = std::make_shared<const IDFObject>(fallBackPath.string());
2565 CachingOption cachingOption = NoneApplied;
2566 if (m_cacheFile->exists()) {
2568 cachingOption = ReadGeomCache;
2569 } else if (fallBackCache->exists()) {
2570 applyCache(fallBackCache);
2571 cachingOption = ReadFallBack;
2572 } else {
2573 cachingOption = writeAndApplyCache(m_cacheFile, fallBackCache);
2574 }
2575 return cachingOption;
2576}
2577
2585
2587 // Create a copy of the instrument
2588 auto physical = std::make_unique<Instrument>(*m_instrument);
2589 // Store the physical instrument 'inside' the neutronic instrument
2590 m_instrument->setPhysicalInstrument(std::move(physical));
2591
2592 // Now we manipulate the original instrument (m_instrument) to hold
2593 // neutronic positions
2594 for (const auto &component : m_neutronicPos) {
2595 if (component.second) {
2596 setLocation(component.first, component.second, m_angleConvertConst, m_deltaOffsets);
2597 // TODO: Do we need to deal with 'facing'???
2598
2599 // Check for a 'type' attribute, indicating that we want to set the
2600 // neutronic shape
2601 if (component.second->hasAttribute("type") && dynamic_cast<ObjComponent *>(component.first)) {
2602 const Poco::XML::XMLString shapeName = component.second->getAttribute("type");
2603 auto shapeIt = mapTypeNameToShape.find(shapeName);
2604 if (shapeIt != mapTypeNameToShape.end()) {
2605 // Change the shape on the current component to the one requested
2606 auto objCmpt = dynamic_cast<ObjComponent *>(component.first);
2607 if (objCmpt)
2608 objCmpt->setShape(shapeIt->second);
2609 } else {
2610 throw Exception::InstrumentDefinitionError("Requested type " + shapeName + " not defined in IDF");
2611 }
2612 }
2613 } else // We have a null Element*, which signals a detector with no neutronic position
2614 {
2615 // This should only happen for detectors
2616 auto *det = dynamic_cast<Detector *>(component.first);
2617 if (det)
2618 m_instrument->removeDetector(det);
2619 }
2620 }
2621}
2622
2640void InstrumentDefinitionParser::adjust(Poco::XML::Element *pElem, const std::map<std::string, bool> &isTypeAssembly,
2641 std::map<std::string, Poco::XML::Element *> &getTypeElement) {
2643 // check if pElem is an element with tag name 'type'
2644 if (pElem->tagName() != "type")
2645 throw Exception::InstrumentDefinitionError("Argument to function adjust() "
2646 "must be a pointer to an XML "
2647 "element with tag name type.");
2648
2649 // check that there is a <combine-components-into-one-shape> element in type
2650 Poco::AutoPtr<NodeList> pNLccioh = pElem->getElementsByTagName("combine-components-into-one-shape");
2651 if (pNLccioh->length() == 0) {
2652 throw Exception::InstrumentDefinitionError(std::string("Argument to function adjust() must be a pointer to an XML "
2653 "element with tag name type,") +
2654 " which contain a <combine-components-into-one-shape> element.");
2655 }
2656
2657 // check that there is a <algebra> element in type
2658 Poco::AutoPtr<NodeList> pNLalg = pElem->getElementsByTagName("algebra");
2659 if (pNLalg->length() == 0) {
2660 throw Exception::InstrumentDefinitionError(std::string("An <algebra> element must be part of a <type>, which") +
2661 " includes a <combine-components-into-one-shape> element. See "
2662 "docs.mantidproject.org/concepts/InstrumentDefinitionFile.");
2663 }
2664
2665 // check that there is a <location> element in type
2666 Poco::AutoPtr<NodeList> pNL = pElem->getElementsByTagName("location");
2667 unsigned long numLocation = pNL->length();
2668 if (numLocation == 0) {
2669 throw Exception::InstrumentDefinitionError(std::string("At least one <location> element must be part of a "
2670 "<type>, which") +
2671 " includes a <combine-components-into-one-shape> element. See "
2672 "docs.mantidproject.org/concepts/InstrumentDefinitionFile.");
2673 }
2674
2675 // check if a <translate-rotate-combined-shape-to> is defined
2676 Poco::AutoPtr<NodeList> pNL_TransRot = pElem->getElementsByTagName("translate-rotate-combined-shape-to");
2677 const Element *pTransRot = nullptr;
2678 if (pNL_TransRot->length() == 1) {
2679 pTransRot = static_cast<Element *>(pNL_TransRot->item(0));
2680 }
2681
2682 // to convert all <component>'s in type into <cuboid> elements, which are
2683 // added
2684 // to pElem, and these <component>'s are deleted after loop
2685
2686 std::unordered_set<Element *> allComponentInType; // used to hold <component>'s found
2687 std::vector<std::string> allLocationName; // used to check if loc names unique
2688 for (unsigned long i = 0; i < numLocation; i++) {
2689 auto *pLoc = static_cast<Element *>(pNL->item(i));
2690
2691 // The location element is required to be a child of a component element.
2692 // Get this component element
2693 Element *pCompElem = InstrumentDefinitionParser::getParentComponent(pLoc);
2694
2695 // get the name given to the <location> element in focus
2696 // note these names are required to be unique for the purpose of
2697 // constructing the <algebra>
2698 std::string locationElementName = pLoc->getAttribute("name");
2699 if (std::find(allLocationName.begin(), allLocationName.end(), locationElementName) == allLocationName.end())
2700 allLocationName.emplace_back(locationElementName);
2701 else
2703 std::string("Names in a <type> element containing ") +
2704 "a <combine-components-into-one-shape> element must be unique. " + "Here error is that " +
2705 locationElementName +
2706 " appears at least twice. See docs.mantidproject.org/concepts/InstrumentDefinitionFile.");
2707
2708 // create dummy component to hold coord. sys. of cuboid
2709 auto baseCoor = std::make_unique<CompAssembly>("base"); // dummy assembly used to get to end assembly if nested
2710 ICompAssembly *endComponent = nullptr; // end assembly, its purpose is to
2711 // hold the shape coordinate system
2712 // get shape coordinate system, returned as endComponent, as defined by
2713 // pLoc
2714 // and nested <location> elements
2715 // of pLoc
2716 std::string shapeTypeName = getShapeCoorSysComp(baseCoor.get(), pLoc, getTypeElement, endComponent);
2717
2718 // translate and rotate cuboid according to shape coordinate system in
2719 // endComponent
2720 std::string cuboidStr = translateRotateXMLcuboid(endComponent, getTypeElement[shapeTypeName], locationElementName);
2721
2722 // if <translate-rotate-combined-shape-to> is specified
2723 if (pTransRot) {
2724 baseCoor = std::make_unique<CompAssembly>("base");
2725
2726 setLocation(baseCoor.get(), pTransRot, m_angleConvertConst);
2727
2728 // Translate and rotate shape xml string according to
2729 // <translate-rotate-combined-shape-to>
2730 cuboidStr = translateRotateXMLcuboid(baseCoor.get(), cuboidStr, locationElementName);
2731 }
2732
2733 DOMParser pParser;
2734 Poco::AutoPtr<Document> pDoc;
2735 try {
2736 pDoc = pParser.parseString(cuboidStr);
2737 } catch (...) {
2738 throw Exception::InstrumentDefinitionError(std::string("Unable to parse XML string ") + cuboidStr);
2739 }
2740 // Get pointer to root element and add this element to pElem
2741 Element *pCuboid = pDoc->documentElement();
2742 Poco::AutoPtr<Node> fisse = (pElem->ownerDocument())->importNode(pCuboid, true);
2743 pElem->appendChild(fisse);
2744
2745 allComponentInType.insert(pCompElem);
2746 }
2747
2748 // delete all <component> found in pElem
2749 for (const auto &component : allComponentInType)
2750 pElem->removeChild(component);
2751}
2752
2762 const Poco::XML::Element *cuboidEle,
2763 const std::string &cuboidName) {
2764 Element *pElem_lfb = getShapeElement(cuboidEle, "left-front-bottom-point");
2765 Element *pElem_lft = getShapeElement(cuboidEle, "left-front-top-point");
2766 Element *pElem_lbb = getShapeElement(cuboidEle, "left-back-bottom-point");
2767 Element *pElem_rfb = getShapeElement(cuboidEle, "right-front-bottom-point");
2768
2769 V3D lfb = parsePosition(pElem_lfb); // left front bottom
2770 V3D lft = parsePosition(pElem_lft); // left front top
2771 V3D lbb = parsePosition(pElem_lbb); // left back bottom
2772 V3D rfb = parsePosition(pElem_rfb); // right front bottom
2773
2774 // translate and rotate cuboid according to coord. sys. of comp
2775 V3D p_lfb = getAbsolutPositionInCompCoorSys(comp, lfb);
2776 V3D p_lft = getAbsolutPositionInCompCoorSys(comp, lft);
2777 V3D p_lbb = getAbsolutPositionInCompCoorSys(comp, lbb);
2778 V3D p_rfb = getAbsolutPositionInCompCoorSys(comp, rfb);
2779
2780 // create output cuboid XML string
2781 std::ostringstream obj_str;
2782
2783 obj_str << "<cuboid id=\"" << cuboidName << "\">";
2784 obj_str << "<left-front-bottom-point ";
2785 obj_str << "x=\"" << p_lfb.X();
2786 obj_str << "\" y=\"" << p_lfb.Y();
2787 obj_str << "\" z=\"" << p_lfb.Z();
2788 obj_str << "\" />";
2789 obj_str << "<left-front-top-point ";
2790 obj_str << "x=\"" << p_lft.X();
2791 obj_str << "\" y=\"" << p_lft.Y();
2792 obj_str << "\" z=\"" << p_lft.Z();
2793 obj_str << "\" />";
2794 obj_str << "<left-back-bottom-point ";
2795 obj_str << "x=\"" << p_lbb.X();
2796 obj_str << "\" y=\"" << p_lbb.Y();
2797 obj_str << "\" z=\"" << p_lbb.Z();
2798 obj_str << "\" />";
2799 obj_str << "<right-front-bottom-point ";
2800 obj_str << "x=\"" << p_rfb.X();
2801 obj_str << "\" y=\"" << p_rfb.Y();
2802 obj_str << "\" z=\"" << p_rfb.Z();
2803 obj_str << "\" />";
2804 obj_str << "</cuboid>";
2805
2806 return obj_str.str();
2807}
2808
2815 Component *dummyComp = new Component("dummy", comp);
2816 comp->add(dummyComp);
2817
2818 dummyComp->setPos(pos); // set pos relative to comp coord. sys.
2819
2820 V3D retVal = dummyComp->getPos(); // get absolute position
2821
2822 return retVal;
2823}
2824
2833std::string InstrumentDefinitionParser::translateRotateXMLcuboid(ICompAssembly *comp, const std::string &cuboidXML,
2834 const std::string &cuboidName) {
2835 DOMParser pParser;
2836 Poco::AutoPtr<Document> pDoc;
2837 try {
2838 pDoc = pParser.parseString(cuboidXML);
2839 } catch (...) {
2840 throw Exception::InstrumentDefinitionError(std::string("Unable to parse XML string ") + cuboidXML);
2841 }
2842
2843 Element *pCuboid = pDoc->documentElement();
2844
2845 std::string retVal = translateRotateXMLcuboid(comp, pCuboid, cuboidName);
2846
2847 return retVal;
2848}
2849
2857Poco::AutoPtr<Poco::XML::Document>
2859 // Number of <location> this <locations> element is shorthand for
2860 size_t nElements(0);
2861 if (pElem->hasAttribute("n-elements")) {
2862 auto n = boost::lexical_cast<int>(Strings::strip(pElem->getAttribute("n-elements")));
2863
2864 if (n <= 0) {
2865 throw Exception::InstrumentDefinitionError("n-elements must be positive");
2866 } else {
2867 nElements = static_cast<size_t>(n);
2868 }
2869 } else {
2870 throw Exception::InstrumentDefinitionError("When using <locations> n-elements attribute is required. See "
2871 "docs.mantidproject.org/concepts/InstrumentDefinitionFile.");
2872 }
2873
2874 std::string name;
2875 if (pElem->hasAttribute("name")) {
2876 name = pElem->getAttribute("name");
2877 }
2878
2879 int nameCountStart(0);
2880 if (pElem->hasAttribute("name-count-start")) {
2881 nameCountStart = boost::lexical_cast<int>(Strings::strip(pElem->getAttribute("name-count-start")));
2882 }
2883
2884 int nameCountIncrement(1);
2885 if (pElem->hasAttribute("name-count-increment")) {
2886 nameCountIncrement = boost::lexical_cast<int>(Strings::strip(pElem->getAttribute("name-count-increment")));
2887
2888 if (nameCountIncrement <= 0)
2889 throw Exception::InstrumentDefinitionError("name-count-increment must be greater than zero.");
2890 }
2891
2892 // A list of numeric attributes which are allowed to have corresponding -end
2893 std::set<std::string> rangeAttrs = {"x", "y", "z", "r", "t", "p", "rot"};
2894
2895 // Numeric attributes related to rotation. Doesn't make sense to have -end
2896 // for
2897 // those
2898 std::set<std::string> rotAttrs = {"axis-x", "axis-y", "axis-z"};
2899
2900 // A set of all numeric attributes for convenience
2901 std::set<std::string> allAttrs;
2902 allAttrs.insert(rangeAttrs.begin(), rangeAttrs.end());
2903 allAttrs.insert(rotAttrs.begin(), rotAttrs.end());
2904
2905 // Attribute values as read from <locations>. If the attribute doesn't have
2906 // a
2907 // value here, it
2908 // means that it wasn't set
2909 std::map<std::string, double> attrValues;
2910
2911 // Read all the set attribute values
2912 for (const auto &attr : allAttrs) {
2913 if (pElem->hasAttribute(attr)) {
2914 attrValues[attr] = boost::lexical_cast<double>(Strings::strip(pElem->getAttribute(attr)));
2915 }
2916 }
2917
2918 // Range attribute steps
2919 std::map<std::string, double> rangeAttrSteps;
2920
2921 // Find *-end for range attributes and calculate steps
2922 for (const auto &rangeAttr : rangeAttrs) {
2923 std::string endAttr = rangeAttr + "-end";
2924 if (pElem->hasAttribute(endAttr)) {
2925 if (attrValues.find(rangeAttr) == attrValues.end()) {
2926 throw Exception::InstrumentDefinitionError("*-end attribute without corresponding * attribute.");
2927 }
2928
2929 double from = attrValues[rangeAttr];
2930 auto to = boost::lexical_cast<double>(Strings::strip(pElem->getAttribute(endAttr)));
2931
2932 rangeAttrSteps[rangeAttr] = (to - from) / (static_cast<double>(nElements) - 1);
2933 }
2934 }
2935
2936 Poco::AutoPtr<Document> pDoc = new Document;
2937 Poco::AutoPtr<Element> pRoot = pDoc->createElement("expansion-of-locations-element");
2938 pDoc->appendChild(pRoot);
2939
2940 for (size_t i = 0; i < nElements; ++i) {
2941 Poco::AutoPtr<Element> pLoc = pDoc->createElement("location");
2942
2943 if (!name.empty()) {
2944 // Add name with appropriate numeric postfix
2945 pLoc->setAttribute("name", name + std::to_string(nameCountStart + (i * nameCountIncrement)));
2946 }
2947
2948 // Copy values of all the attributes set
2949 for (auto &attrValue : attrValues) {
2950 pLoc->setAttribute(attrValue.first, boost::lexical_cast<std::string>(attrValue.second));
2951
2952 // If attribute has a step, increase the value by the step
2953 if (rangeAttrSteps.find(attrValue.first) != rangeAttrSteps.end()) {
2954 attrValue.second += rangeAttrSteps[attrValue.first];
2955 }
2956 }
2957
2958 pRoot->appendChild(pLoc);
2959 }
2960
2961 return pDoc;
2962}
2963
2970 std::string retVal;
2971 std::string filename = getMangledName();
2972 if (!filename.empty()) {
2973 std::filesystem::path path =
2974 std::filesystem::path(ConfigService::Instance().getVTPFileDirectory()) / (filename + ".vtp");
2975 retVal = path.string();
2976 }
2977 return retVal;
2978}
2979
2990Poco::XML::Element *InstrumentDefinitionParser::getShapeElement(const Poco::XML::Element *pElem,
2991 const std::string &name) {
2992 // check if this shape element contain an element with name specified by the
2993 // 2nd function argument
2994 Poco::AutoPtr<NodeList> pNL = pElem->getElementsByTagName(name);
2995 if (pNL->length() != 1) {
2996 throw std::invalid_argument("XML element: <" + pElem->tagName() +
2997 "> must contain exactly one sub-element with name: <" + name + ">.");
2998 }
2999 auto *retVal = static_cast<Element *>(pNL->item(0));
3000 return retVal;
3001}
3002
3009 V3D retVal;
3010
3011 if (pElem->hasAttribute("R") || pElem->hasAttribute("theta") || pElem->hasAttribute("phi")) {
3012 double R = attrToDouble(pElem, "R");
3013 double theta = attrToDouble(pElem, "theta");
3014 double phi = attrToDouble(pElem, "phi");
3015
3016 retVal.spherical(R, theta, phi);
3017 } else if (pElem->hasAttribute("r") || pElem->hasAttribute("t") || pElem->hasAttribute("p"))
3018 // This is alternative way a user may specify spherical coordinates
3019 // which may be preferred in the long run to the more verbose of
3020 // using R, theta and phi.
3021 {
3022 double R = attrToDouble(pElem, "r");
3023 double theta = attrToDouble(pElem, "t");
3024 double phi = attrToDouble(pElem, "p");
3025
3026 retVal.spherical(R, theta, phi);
3027 } else {
3028
3029 double x = attrToDouble(pElem, "x");
3030 double y = attrToDouble(pElem, "y");
3031 double z = attrToDouble(pElem, "z");
3032
3033 retVal(x, y, z);
3034 }
3035
3036 return retVal;
3037}
3038
3039//-----------------------------------------------------------------------------------------------------------------------
3058 const Poco::XML::Element *pLocElem,
3059 std::map<std::string, Poco::XML::Element *> &getTypeElement,
3060 Geometry::ICompAssembly *&endAssembly) {
3061 // The location element is required to be a child of a component element.
3062 // Get
3063 // this component element
3064 Element *pCompElem = InstrumentDefinitionParser::getParentComponent(pLocElem);
3065
3066 // Create the assembly that will be appended into the parent.
3068
3069 // The newly added component is required to have a type. Find out what this
3070 // type is and find all the location elements of this type. Finally loop
3071 // over
3072 // these
3073 // location elements
3074
3075 Element *pType = getTypeElement[pCompElem->getAttribute("type")];
3076
3078 endAssembly = ass;
3079
3080 // set location for this newly added comp
3081 setLocation(ass, pLocElem, m_angleConvertConst);
3082
3083 Poco::AutoPtr<NodeList> pNL = pType->getElementsByTagName("location");
3084 if (pNL->length() == 0) {
3085 return pType->getAttribute("name");
3086 } else if (pNL->length() == 1) {
3087 const auto *pElem = static_cast<Element *>(pNL->item(0));
3088 return getShapeCoorSysComp(ass, pElem, getTypeElement, endAssembly);
3089 } else {
3091 std::string("When using <combine-components-into-one-shape> ") +
3092 " the containing component elements are not allowed to contain "
3093 "multiple nested components. See docs.mantidproject.org/concepts/InstrumentDefinitionFile.");
3094 }
3095}
3096
3097} // namespace Mantid::Geometry
std::string name
Definition Run.cpp:60
gsl_vector * tmp
double value
The value of the point.
Definition FitMW.cpp:51
std::map< DeltaEMode::Type, std::string > index
#define UNUSED_ARG(x)
Function arguments are sometimes unused in certain implmentations but are required for documentation ...
Definition System.h:44
double obj
the value of the quadratic function
Class for Assembly of geometric components.
Kernel::V3D getPos() const override
Gets the absolute position of the Parametrized CompAssembly This attempts to read the cached position...
Component is a wrapper for a Component which can modify some of its parameters, e....
Definition Component.h:42
void setRot(const Kernel::Quat &) override
Set the orientation Kernel::Quaternion relative to parent (if present)
void setParent(IComponent *) override
Assign a parent IComponent. Previous parent link is lost.
Kernel::V3D getPos() const override
Get the position of the IComponent. Tree structure is traverse through the.
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
detid_t getID() const override
Gets the detector id.
Definition Detector.cpp:51
GridDetector is a type of CompAssembly, an assembly of components.
static bool compareName(const std::string &proposedMatch)
Matches name to Structured Detector.
Class for Assembly of geometric components.
virtual int add(IComponent *)=0
Add a component to the assembly.
base class for Geometric IComponent
Definition IComponent.h:53
virtual Kernel::V3D getPos() const =0
Get the position of the IComponent. Tree structure is traverse through the.
virtual void setSideBySideViewPos(const Kernel::V2D &)=0
virtual void setPos(double, double, double)=0
Set the IComponent position, x, y, z respective to parent (if present)
virtual std::shared_ptr< const IComponent > getParent() const =0
Return a pointer to the current parent.
virtual Kernel::Quat getRotation() const =0
Get the absolute orientation of the IComponent.
virtual void translate(const Kernel::V3D &)=0
Copy the Rotation from another IComponent.
virtual void rotate(const Kernel::Quat &)=0
Rotate the IComponent. This is relative to parent.
virtual std::string getName() const =0
Get the IComponent name.
Creates an instrument data from a XML instrument description file.
bool m_deltaOffsets
Flag to indicate whether offsets given in spherical coordinates are to be added to the current positi...
void appendLocations(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElems, const Poco::XML::Element *pCompElem, IdList &idList)
Append <locations> in a locations element.
void createNeutronicInstrument()
If appropriate, creates a second instrument containing neutronic detector positions.
std::shared_ptr< Instrument > parseXML(Kernel::ProgressBase *progressReporter)
Parse XML contents.
void createStructuredDetector(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem, const Poco::XML::Element *pCompElem, const std::string &filename, const Poco::XML::Element *pType)
void adjustTypesContainingCombineComponentsElement(ShapeFactory &shapeCreator, const std::string &filename, const std::vector< Poco::XML::Element * > &typeElems, size_t numberOfTypes)
Adjust each type which contains a <combine-components-into-one-shape> element.
void getTypeAndComponentPointers(const Poco::XML::Element *pRootElem, std::vector< Poco::XML::Element * > &typeElems, std::vector< Poco::XML::Element * > &compElems) const
Populate vectors of pointers to type and component xml elements.
void throwIfTypeNameNotUnique(const std::string &filename, const std::string &typeName) const
Throw exception if type name is not unique in the IDF.
std::map< const Geometry::IComponent *, SphVec > m_tempPosHolder
Map to store positions of parent components in spherical coordinates.
void createDetectorOrMonitor(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem, const Poco::XML::Element *pCompElem, const std::string &filename, IdList &idList, const std::string &category)
std::string translateRotateXMLcuboid(Geometry::ICompAssembly *comp, const Poco::XML::Element *cuboidEle, const std::string &cuboidName)
Returns a translated and rotated <cuboid> element.
void createRectangularDetector(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem, const Poco::XML::Element *pCompElem, const std::string &filename, const Poco::XML::Element *pType)
double m_angleConvertConst
when this const equals 1 it means that angle=degree (default) is set in IDF otherwise if this const e...
void appendLeaf(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem, const Poco::XML::Element *pCompElem, IdList &idList)
Add XML element to parent assuming the element contains no other component elements.
std::string getMangledName()
Handle used in the singleton constructor for instrument file should append the value file sha-1 check...
void setComponentLinks(std::shared_ptr< Geometry::Instrument > &instrument, Poco::XML::Element *pRootElem, Kernel::ProgressBase *progress=nullptr, const std::string &requestedDate=std::string())
Add/overwrite any parameters specified in instrument with param values specified in <component-link> ...
void adjust(Poco::XML::Element *pElem, const std::map< std::string, bool > &isTypeAssembly, std::map< std::string, Poco::XML::Element * > &getTypeElement)
Takes as input a <type> element containing a <combine-components-into-one-shape>, and adjust the <typ...
bool m_haveDefaultFacing
True if defaults->components-are-facing is set in instrument def. file.
void setLocation(Geometry::IComponent *comp, const Poco::XML::Element *pElem, const double angleConvertConst, const bool deltaOffsets=false)
Set location (position) of comp as specified in XML location element.
Kernel::V3D m_defaultFacing
Hold default facing position.
CachingOption getAppliedCachingOption() const
Get the applied caching option.
std::map< Geometry::IComponent *, Poco::XML::Element * > m_neutronicPos
A map containing the neutronic position for each detector.
void checkComponentContainsLocationElement(Poco::XML::Element *pElem, const std::string &filename) const
Check component has a <location> or <locations> element.
Kernel::V3D parseFacingElementToV3D(Poco::XML::Element *pElem)
Parse position of facing element to V3D.
void collateTypeInformation(const std::string &filename, const std::vector< Poco::XML::Element * > &typeElems, ShapeFactory &shapeCreator)
Collect some information about types for later use.
void setSideBySideViewLocation(Geometry::IComponent *comp, const Poco::XML::Element *pCompElem)
Set location (position) of comp as specified in XML side-by-side-view-location element.
std::shared_ptr< Geometry::Instrument > m_instrument
For convenience added pointer to instrument here.
InstrumentDefinitionParser()
Default Constructor - not very functional in this state.
CachingOption writeAndApplyCache(IDFObject_const_sptr firstChoiceCache, IDFObject_const_sptr fallBackCache)
Write out a cache file.
Kernel::V3D parsePosition(Poco::XML::Element *pElem)
Get position coordinates from XML element.
void makeXYplaneFaceComponent(Geometry::IComponent *&in, const Geometry::ObjComponent *facing)
Make the shape defined in 1st argument face the component in the second argument.
Kernel::V3D getRelativeTranslation(const Geometry::IComponent *comp, const Poco::XML::Element *pElem, const double angleConvertConst, const bool deltaOffsets=false)
Calculate the position of comp relative to its parent from info provided by <location> element.
std::vector< Poco::XML::Element * > m_hasParameterElement
Holds all the xml elements that have a <parameter> child element.
void setFacing(Geometry::IComponent *comp, const Poco::XML::Element *pElem)
Set facing of comp as specified in XML facing element.
void readDefaults(Poco::XML::Element *defaults)
Reads the contents of the <defaults> element to set member variables,.
Poco::AutoPtr< Poco::XML::Document > m_pDoc
XML document is lazy loaded.
void populateIdList(Poco::XML::Element *pE, IdList &idList)
Method for populating IdList.
std::map< std::string, std::shared_ptr< Geometry::IObject > > mapTypeNameToShape
map which maps the type name to a shared pointer to a geometric shape
void createShapeIfTypeIsNotAnAssembly(Mantid::Geometry::ShapeFactory &shapeCreator, size_t iType, Poco::XML::Element *pTypeElem, const std::string &typeName)
Record type as an assembly if it contains a component, otherwise create a shape for it.
std::map< std::string, bool > isTypeAssembly
map which holds names of types and whether or not they are categorized as being assemblies,...
void setLogfile(const Geometry::IComponent *comp, const Poco::XML::Element *pElem, InstrumentParameterCache &logfileCache, const std::string &requestedDate=std::string())
Set parameter/logfile info (if any) associated with component.
void createGridDetector(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem, const Poco::XML::Element *pCompElem, const std::string &filename, const Poco::XML::Element *pType)
const std::string createVTPFileName()
creates a vtp filename from a given xml filename
void parseLocationsForEachTopLevelComponent(Kernel::ProgressBase *progressReporter, const std::string &filename, const std::vector< Poco::XML::Element * > &compElems)
Aggregate locations and IDs for components.
static Poco::XML::Element * getParentComponent(const Poco::XML::Element *pLocElem)
Get parent component element of location element.
std::map< std::string, Poco::XML::Element * > getTypeElement
map which holds names of types and pointers to these type for fast retrieval in code
Poco::XML::Element * getShapeElement(const Poco::XML::Element *pElem, const std::string &name)
Return a subelement of an XML element.
void appendAssembly(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem, const Poco::XML::Element *pCompElem, IdList &idList)
Add XML element to parent assuming the element contains other component elements.
std::vector< std::string > buildExcludeList(const Poco::XML::Element *const location)
void createVectorOfElementsContainingAParameterElement(Poco::XML::Element *pRootElem)
Create a vector of elements which contain a <parameter>
std::string getShapeCoorSysComp(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem, std::map< std::string, Poco::XML::Element * > &getTypeElement, Geometry::ICompAssembly *&endAssembly)
This method returns the parent appended which its child components and also name of type of the last ...
CachingOption setupGeometryCache()
Reads in or creates the geometry cache ('vtp') file.
void setValidityRange(const Poco::XML::Element *pRootElem)
Check the validity range and add it to the instrument object.
bool m_hasParameterElement_beenSet
has m_hasParameterElement been set - used when public method setComponentLinks is used
double attrToDouble(const Poco::XML::Element *pElem, const std::string &name)
return 0 if the attribute doesn't exist.
void initialise(const std::string &filename, const std::string &instName, const std::string &xmlText, const std::string &vtpFilename)
shared Constructor logic
Poco::AutoPtr< Poco::XML::Document > convertLocationsElement(const Poco::XML::Element *pElem)
Take as input a <locations> element.
std::vector< Geometry::ObjComponent * > m_facingComponent
Container to hold all detectors and monitors added to the instrument.
Kernel::V3D getAbsolutPositionInCompCoorSys(Geometry::ICompAssembly *comp, Kernel::V3D)
return absolute position of point which is set relative to the coordinate system of the input compone...
static std::string getNameOfLocationElement(const Poco::XML::Element *pElem, const Poco::XML::Element *pCompElem)
get name of location element
Poco::AutoPtr< Poco::XML::Document > getDocument()
lazy loads the document and returns a pointer
bool isAssembly(const std::string &) const
Return true if assembly, false if not assembly and throws exception if string not in assembly.
bool m_sideBySideViewLocation_exists
Store if xml text contains side-by-side-view-location string.
bool m_indirectPositions
Flag to indicate whether IDF contains physical & neutronic positions.
void checkIdListExistsAndDefinesEnoughIDs(const IdList &idList, Poco::XML::Element *pElem, const std::string &filename) const
Check IdList.
void saveDOM_Tree(const std::string &outFilename)
Save DOM tree to xml file.
void applyCache(const IDFObject_const_sptr &cacheToApply)
Reads from a cache file.
Class for Assembly of geometric components.
Object Component class, this class brings together the physical attributes of the component to the po...
void setShape(std::shared_ptr< const IObject > newShape)
Set a new shape on the component void setShape(std::shared_ptr<const IObject> newShape);.
RectangularDetector is a type of CompAssembly, an assembly of components.
static bool compareName(const std::string &proposedMatch)
Matches name to Structured Detector.
Class originally intended to be used with the DataHandling 'LoadInstrument' algorithm.
std::shared_ptr< CSGObject > createShape(Poco::XML::Element *pElem)
Creates a geometric object from a DOM-element-node pointing to an element whose child nodes contain t...
StructuredDetector is a type of CompAssembly, an assembly of components.
static bool compareName(const std::string &proposedMatch)
Matches name to Structured Detector.
Reads the Geometry Cache from the file to the Object.
Writes the Geometry from Object to Cache.
Exception for when an item is already in a collection.
Definition Exception.h:164
Exception for errors associated with the instrument definition.
Definition Exception.h:220
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 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
void information(const std::string &msg)
Logs at information level.
Definition Logger.cpp:136
void resetNumSteps(int64_t nsteps, double start, double end)
Change the number of steps between start/end.
virtual bool hasCancellationBeenRequested() const
Override so that the reporter can inform whether a cancellation request has been used.
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Class for quaternions.
Definition Quat.h:39
void inverse()
Inverse a quaternion (in the sense of rotation inversion)
Definition Quat.cpp:376
void rotate(V3D &) const
Rotate a vector.
Definition Quat.cpp:397
Implements a 2-dimensional vector embedded in a 3D space, i.e.
Definition V2D.h:29
Class for 3D vectors.
Definition V3D.h:34
constexpr double X() const noexcept
Get x.
Definition V3D.h:238
constexpr V3D cross_prod(const V3D &v) const noexcept
Cross product (this * argument)
Definition V3D.h:284
constexpr double Y() const noexcept
Get y.
Definition V3D.h:239
void spherical(const double R, const double theta, const double phi) noexcept
Sets the vector position based on spherical coordinates.
Definition V3D.cpp:56
double angle(const V3D &) const
Angle between this and another vector.
Definition V3D.cpp:162
double norm() const noexcept
Definition V3D.h:269
constexpr double Z() const noexcept
Get z.
Definition V3D.h:240
Handedness
Type to distingusih between l and r handedness.
std::map< std::pair< std::string, const IComponent * >, std::shared_ptr< XMLInstrumentParameter > > InstrumentParameterCache
Convenience typedef.
Definition Instrument.h:39
Mantid::Kernel::Logger g_log("Goniometer")
std::shared_ptr< const AbstractIDFObject > IDFObject_const_sptr
Definition IDFObject.h:94
PointingAlong axisNameToAxisType(const std::string &label, const std::string &input)
std::shared_ptr< Instrument > Instrument_sptr
Shared pointer to an instrument object.
PointingAlong
Type to describe pointing along options.
MANTID_KERNEL_DLL std::string sha1FromString(const std::string &input)
create a SHA-1 checksum from a string
MANTID_KERNEL_DLL std::string strip(const std::string &A)
strip pre/post spaces
Definition Strings.cpp:419
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
Definition V3D.h:352
Helper class which provides the Collimation Length for SANS instruments.
int32_t detid_t
Typedef for a detector ID.
STL namespace.
std::string to_string(const wide_integer< Bits, Signed > &n)
int counted
Used to count the number of detector encounted so far.
Stripped down vector that holds position in terms of spherical coordinates, Needed when processing in...