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_mixedNeutronicPositions(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_mixedNeutronicPositions(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_mixedNeutronicPositions(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 // Check if the IDF requests mixed-mode neutronic positions.
1014 // When set, detectors without <neutronic> tags retain their physical
1015 // positions instead of being removed from the instrument. This also
1016 // implies indirect neutronic positions are enabled.
1017 if (defaults->getChildElement("indirect-neutronic-positions-mixed")) {
1018 m_indirectPositions = true;
1020 }
1021
1022 /*
1023 Try to extract the reference frame information.
1024 */
1025 // Get the target xml element.
1026 Element *referenceFrameElement = defaults->getChildElement("reference-frame");
1027 // Extract if available
1028 if (referenceFrameElement) {
1029 using Poco::XML::XMLString;
1030 // Get raw xml values
1031 Element *upElement = referenceFrameElement->getChildElement("pointing-up");
1032 Element *alongElement = referenceFrameElement->getChildElement("along-beam");
1033 Element *handednessElement = referenceFrameElement->getChildElement("handedness");
1034 Element *originElement = referenceFrameElement->getChildElement("origin");
1035 Element *thetaSignElement = referenceFrameElement->getChildElement("theta-sign");
1036
1037 // Defaults
1038 XMLString s_alongBeam("z");
1039 XMLString s_pointingUp("y");
1040 XMLString s_handedness("right");
1041 XMLString s_origin;
1042
1043 // Make extractions from sub elements where possible.
1044 if (alongElement) {
1045 s_alongBeam = alongElement->getAttribute("axis");
1046 }
1047 if (upElement) {
1048 s_pointingUp = upElement->getAttribute("axis");
1049 }
1050 if (handednessElement) {
1051 s_handedness = handednessElement->getAttribute("val");
1052 }
1053 if (originElement) {
1054 s_origin = originElement->getAttribute("val");
1055 }
1056
1057 // Extract theta sign axis if specified.
1058 XMLString s_thetaSign(s_pointingUp);
1059 if (thetaSignElement) {
1060 s_thetaSign = thetaSignElement->getAttribute("axis");
1061 }
1062
1063 // Convert to input types
1064 PointingAlong alongBeam = axisNameToAxisType("along-beam", s_alongBeam);
1065 PointingAlong pointingUp = axisNameToAxisType("pointing-up", s_pointingUp);
1066 PointingAlong thetaSign = axisNameToAxisType("theta-sign", s_thetaSign);
1067 Handedness handedness = s_handedness == "right" ? Right : Left;
1068
1069 // Overwrite the default reference frame.
1070 m_instrument->setReferenceFrame(
1071 std::make_shared<ReferenceFrame>(pointingUp, alongBeam, thetaSign, handedness, s_origin));
1072 }
1073}
1074
1075std::vector<std::string> InstrumentDefinitionParser::buildExcludeList(const Poco::XML::Element *const location) {
1076 // check if <exclude> sub-elements for this location and create new exclude
1077 // list to pass on
1078 Poco::AutoPtr<NodeList> pNLexclude = location->getElementsByTagName("exclude");
1079 unsigned long numberExcludeEle = pNLexclude->length();
1080 std::vector<std::string> newExcludeList;
1081 for (unsigned long i = 0; i < numberExcludeEle; i++) {
1082 auto *pExElem = static_cast<Element *>(pNLexclude->item(i));
1083 if (pExElem->hasAttribute("sub-part"))
1084 newExcludeList.emplace_back(pExElem->getAttribute("sub-part"));
1085 }
1086
1087 return newExcludeList;
1088}
1089
1090//-----------------------------------------------------------------------------------------------------------------------
1105void InstrumentDefinitionParser::appendAssembly(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem,
1106 const Poco::XML::Element *pCompElem, IdList &idList) {
1107 const std::string filename = m_xmlFile->getFileFullPathStr();
1108 // The location element is required to be a child of a component element. Get
1109 // this component element
1110 // Element* pCompElem =
1111 // InstrumentDefinitionParser::getParentComponent(pLocElem);
1112
1113 // Read detector IDs into idlist if required
1114 // Note idlist may be defined for any component
1115 // Note any new idlist found will take precedence.
1116
1117 if (pCompElem->hasAttribute("idlist")) {
1118 std::string idlist = pCompElem->getAttribute("idlist");
1119
1120 if (idlist != idList.idname) {
1121 Element *pFound = pCompElem->ownerDocument()->getElementById(idlist, "idname");
1122
1123 if (pFound == nullptr) {
1125 "No <idlist> with name idname=\"" + idlist + "\" present in instrument definition file.", filename);
1126 }
1127 idList.reset();
1128 populateIdList(pFound, idList);
1129 }
1130 }
1131
1132 // Create the assembly that will be appended into the parent.
1134 // The newly added component is required to have a type. Find out what this
1135 // type is and find all the location elements of this type. Finally loop over
1136 // these
1137 // location elements
1138
1139 Element *pType = getTypeElement[pCompElem->getAttribute("type")];
1140 std::string category;
1141 if (pType->hasAttribute("is"))
1142 category = pType->getAttribute("is");
1143 if (category == "SamplePos" || category == "samplePos") {
1145 } else if (pType->hasAttribute("outline") && pType->getAttribute("outline") != "no") {
1147 parent);
1148 } else {
1150 }
1151
1152 // set location for this newly added comp and set facing if specified in
1153 // instrument def. file. Also
1154 // check if any logfiles are referred to through the <parameter> element.
1155
1157 setSideBySideViewLocation(ass, pCompElem);
1158 setFacing(ass, pLocElem);
1159 setLogfile(ass, pCompElem,
1160 m_instrument->getLogfileCache()); // params specified within <component>
1161 setLogfile(ass, pLocElem,
1162 m_instrument->getLogfileCache()); // params specified within specific <location>
1163
1164 // check if special Component
1165 if (category == "SamplePos" || category == "samplePos") {
1166 m_instrument->markAsSamplePos(ass);
1167 }
1168 if (category == "Source" || category == "source") {
1169 m_instrument->markAsSource(ass);
1170 }
1171
1172 // If enabled, check for a 'neutronic position' tag and add to cache if found
1173 if (m_indirectPositions) {
1174 Element *neutronic = pLocElem->getChildElement("neutronic");
1175 if (neutronic)
1176 m_neutronicPos[ass] = neutronic;
1177 }
1178
1179 // Check for <exclude> tags for this location
1180 const std::vector<std::string> excludeList = buildExcludeList(pLocElem);
1181
1182 NodeIterator it(pType, NodeFilter::SHOW_ELEMENT);
1183
1184 Node *pNode = it.nextNode();
1185 while (pNode) {
1186 if (pNode->nodeName() == "location") {
1187 // pLocElem is the location of a type. This type is here an assembly and
1188 // pElem below is a <location> within this type
1189 const Element *pElem = static_cast<Element *>(pNode);
1190
1191 // get the parent of pElem, i.e. a pointer to the <component> element that
1192 // contains pElem
1193 const Element *pParentElem = InstrumentDefinitionParser::getParentComponent(pElem);
1194
1195 // check if this location is in the exclude list
1196 auto inExcluded = find(excludeList.cbegin(), excludeList.cend(),
1198 if (inExcluded == excludeList.end()) {
1199
1200 std::string typeName = (InstrumentDefinitionParser::getParentComponent(pElem))->getAttribute("type");
1201
1202 if (isAssembly(typeName)) {
1203 appendAssembly(ass, pElem, pParentElem, idList);
1204 } else {
1205 appendLeaf(ass, pElem, pParentElem, idList);
1206 }
1207 }
1208 }
1209 if (pNode->nodeName() == "locations") {
1210 const Element *pLocationsElems = static_cast<Element *>(pNode);
1211 const Element *pParentLocationsElem = InstrumentDefinitionParser::getParentComponent(pLocationsElems);
1212
1213 // append <locations> elements in <locations>
1214 appendLocations(ass, pLocationsElems, pParentLocationsElem, idList);
1215 }
1216 pNode = it.nextNode();
1217 }
1218
1219 // create outline object for the assembly
1220 if (pType->hasAttribute("outline") && pType->getAttribute("outline") != "no") {
1221 auto *objAss = dynamic_cast<Geometry::ObjCompAssembly *>(ass);
1222 if (!objAss) {
1223 throw std::logic_error("Failed to cast ICompAssembly object to ObjCompAssembly");
1224 }
1225 if (pType->getAttribute("object_created") == "no") {
1226 pType->setAttribute("object_created", "yes");
1227 std::shared_ptr<Geometry::IObject> obj = objAss->createOutline();
1228 if (obj) {
1229 mapTypeNameToShape[pType->getAttribute("name")] = obj;
1230 } else { // object failed to be created
1231 pType->setAttribute("outline", "no");
1232 g_log.warning() << "Failed to create outline object for assembly " << pType->getAttribute("name") << '\n';
1233 }
1234 } else {
1235 objAss->setOutline(mapTypeNameToShape[pType->getAttribute("name")]);
1236 }
1237 }
1238}
1239
1241 const Poco::XML::Element *pLocElem,
1242 const Poco::XML::Element *pCompElem,
1243 const std::string &filename, IdList &idList,
1244 const std::string &category) {
1245
1246 //-------------- Create a Detector
1247 //------------------------------------------------
1248 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1249
1250 // before setting detector ID check that the IDF satisfies the following
1251
1252 if (idList.counted >= static_cast<int>(idList.vec.size())) {
1253 std::stringstream ss1, ss2;
1254 ss1 << idList.vec.size();
1255 ss2 << idList.counted;
1256 if (idList.idname.empty()) {
1257 g_log.error("No list of detector IDs found for location element " + name);
1258 throw Kernel::Exception::InstrumentDefinitionError("Detector location element " + name + " has no idlist.",
1259 filename);
1260 } else if (idList.vec.empty()) {
1261 g_log.error("No detector IDs found for detectors in list " + idList.idname);
1262 } else {
1263 g_log.error("The number of detector IDs listed in idlist named " + idList.idname +
1264 " is less then the number of detectors");
1265 }
1267 "Number of IDs listed in idlist (=" + ss1.str() + ") is less than the number of detectors.", filename);
1268 }
1269
1270 std::string typeName = pCompElem->getAttribute("type");
1271
1272 // Create detector and increment id. Finally add the detector to the parent
1273 Geometry::Detector *detector =
1274 new Geometry::Detector(name, idList.vec[idList.counted], mapTypeNameToShape[typeName], parent);
1275 idList.counted++;
1276 parent->add(detector);
1277
1278 // set location for this newly added comp and set facing if specified in
1279 // instrument def. file. Also
1280 // check if any logfiles are referred to through the <parameter> element.
1281 setLocation(detector, pLocElem, m_angleConvertConst, m_deltaOffsets);
1282 setFacing(detector, pLocElem);
1283 setLogfile(detector, pCompElem,
1284 m_instrument->getLogfileCache()); // params specified within <component>
1285 setLogfile(detector, pLocElem,
1286 m_instrument->getLogfileCache()); // params specified within specific <location>
1287
1288 // If enabled, check for a 'neutronic position' tag and add to cache
1289 // (null pointer added INTENTIONALLY if not found)
1290 if (m_indirectPositions) {
1291 m_neutronicPos[detector] = pLocElem->getChildElement("neutronic");
1292 }
1293
1294 // mark-as is a deprecated attribute used before is="monitor" was introduced
1295 if (pCompElem->hasAttribute("mark-as") || pLocElem->hasAttribute("mark-as")) {
1296 g_log.warning() << "Attribute 'mark-as' is a deprecated attribute in "
1297 "Instrument Definition File."
1298 << " Please see the deprecated section of "
1299 "docs.mantidproject.org/concepts/InstrumentDefinitionFile for how to remove this "
1300 "warning message\n";
1301 }
1302
1303 try {
1304 if (category == "Monitor" || category == "monitor") {
1305 m_instrument->markAsMonitorIncomplete(detector);
1306 } else {
1307 // for backwards compatibility look for mark-as="monitor"
1308 if ((pCompElem->hasAttribute("mark-as") && pCompElem->getAttribute("mark-as") == "monitor") ||
1309 (pLocElem->hasAttribute("mark-as") && pLocElem->getAttribute("mark-as") == "monitor")) {
1310 m_instrument->markAsMonitorIncomplete(detector);
1311 } else {
1312 m_instrument->markAsDetectorIncomplete(detector);
1313 }
1314 }
1315
1316 } catch (Kernel::Exception::ExistsError &) {
1317 std::stringstream convert;
1318 convert << detector->getID();
1320 "Detector with ID = " + convert.str() + " present more then once in XML instrument file", filename);
1321 }
1322
1323 // Add all monitors and detectors to 'facing component' container. This is
1324 // only used if the
1325 // "facing" elements are defined in the instrument definition file
1326 m_facingComponent.emplace_back(detector);
1327
1328 setSideBySideViewLocation(detector, pCompElem);
1329}
1330
1331void InstrumentDefinitionParser::createGridDetector(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem,
1332 const Poco::XML::Element *pCompElem, const std::string &filename,
1333 const Poco::XML::Element *pType) {
1334
1335 //-------------- Create a GridDetector
1336 //------------------------------------------------
1337 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1338
1339 // Create the bank with the given parent.
1340 auto bank = new Geometry::GridDetector(name, parent);
1341
1342 // set location for this newly added comp and set facing if specified in
1343 // instrument def. file. Also
1344 // check if any logfiles are referred to through the <parameter> element.
1346 setFacing(bank, pLocElem);
1347 setLogfile(bank, pCompElem,
1348 m_instrument->getLogfileCache()); // params specified within <component>
1349 setLogfile(bank, pLocElem,
1350 m_instrument->getLogfileCache()); // params specified within specific <location>
1351
1352 // Extract all the parameters from the XML attributes
1353 int xpixels = 0;
1354 int ypixels = 0;
1355 int zpixels = 0;
1356 int idstart = 0;
1357 std::string idfillorder;
1358 int idstepbyrow = 0;
1359 int idstep = 1;
1360
1361 // The shape!
1362 // Given that this leaf component is actually an assembly, its constituent
1363 // component detector shapes comes from its type attribute.
1364 const std::string shapeType = pType->getAttribute("type");
1365 std::shared_ptr<Geometry::IObject> shape = mapTypeNameToShape[shapeType];
1366 // These parameters are in the TYPE defining RectangularDetector
1367 if (pType->hasAttribute("xpixels"))
1368 xpixels = std::stoi(pType->getAttribute("xpixels"));
1369 double xstart = attrToDouble(pType, "xstart");
1370 double xstep = attrToDouble(pType, "xstep");
1371
1372 if (pType->hasAttribute("ypixels"))
1373 ypixels = std::stoi(pType->getAttribute("ypixels"));
1374 double ystart = attrToDouble(pType, "ystart");
1375 double ystep = attrToDouble(pType, "ystep");
1376
1377 if (pType->hasAttribute("zpixels"))
1378 zpixels = std::stoi(pType->getAttribute("zpixels"));
1379 double zstart = attrToDouble(pType, "zstart");
1380 double zstep = attrToDouble(pType, "zstep");
1381
1382 // THESE parameters are in the INSTANCE of this type - since they will
1383 // change.
1384 if (pCompElem->hasAttribute("idstart"))
1385 idstart = std::stoi(pCompElem->getAttribute("idstart"));
1386 if (pCompElem->hasAttribute("idfillorder"))
1387 idfillorder = pCompElem->getAttribute("idfillorder");
1388 // Default ID row step size
1389 if (!idfillorder.empty() && idfillorder[0] == 'x')
1390 idstepbyrow = xpixels;
1391 else if (!idfillorder.empty() && idfillorder[0] == 'y')
1392 idstepbyrow = ypixels;
1393 else
1394 idstepbyrow = zpixels;
1395
1396 if (pCompElem->hasAttribute("idstepbyrow")) {
1397 idstepbyrow = std::stoi(pCompElem->getAttribute("idstepbyrow"));
1398 }
1399 // Default ID row step size
1400 if (pCompElem->hasAttribute("idstep"))
1401 idstep = std::stoi(pCompElem->getAttribute("idstep"));
1402
1403 setSideBySideViewLocation(bank, pCompElem);
1404
1405 // Now, initialize all the pixels in the bank
1406 bank->initialize(shape, xpixels, xstart, xstep, ypixels, ystart, ystep, zpixels, zstart, zstep, idstart, idfillorder,
1407 idstepbyrow, idstep);
1408
1409 // Loop through all detectors in the newly created bank and mark those in
1410 // the instrument.
1411 try {
1412 for (int z = 0; z < bank->nelements(); ++z) {
1413 auto zLayer = std::dynamic_pointer_cast<Geometry::ICompAssembly>((*bank)[z]);
1414 for (int x = 0; x < zLayer->nelements(); ++x) {
1415 auto xColumn = std::dynamic_pointer_cast<Geometry::ICompAssembly>((*zLayer)[x]);
1416 for (int y = 0; y < xColumn->nelements(); ++y) {
1417 std::shared_ptr<Geometry::Detector> detector = std::dynamic_pointer_cast<Geometry::Detector>((*xColumn)[y]);
1418 if (detector) {
1419 // Make default facing for the pixel
1420 auto *comp = static_cast<IComponent *>(detector.get());
1423 // Mark it as a detector (add to the instrument cache)
1424 m_instrument->markAsDetectorIncomplete(detector.get());
1425 }
1426 }
1427 }
1428 }
1429 } catch (Kernel::Exception::ExistsError &) {
1430 throw Kernel::Exception::InstrumentDefinitionError("Duplicate detector ID found when adding GridDetector " + name +
1431 " in XML instrument file" + filename);
1432 }
1433}
1434
1436 const Poco::XML::Element *pLocElem,
1437 const Poco::XML::Element *pCompElem,
1438 const std::string &filename,
1439 const Poco::XML::Element *pType) {
1440 //-------------- Create a RectangularDetector
1441 //------------------------------------------------
1442 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1443
1444 // Create the bank with the given parent.
1445 auto bank = new Geometry::RectangularDetector(name, parent);
1446
1447 // set location for this newly added comp and set facing if specified in
1448 // instrument def. file. Also
1449 // check if any logfiles are referred to through the <parameter> element.
1451 setFacing(bank, pLocElem);
1452 setLogfile(bank, pCompElem,
1453 m_instrument->getLogfileCache()); // params specified within <component>
1454 setLogfile(bank, pLocElem,
1455 m_instrument->getLogfileCache()); // params specified within specific <location>
1456
1457 // Extract all the parameters from the XML attributes
1458 int xpixels = 0;
1459 int ypixels = 0;
1460 int idstart = 0;
1461 bool idfillbyfirst_y = true;
1462 int idstepbyrow = 0;
1463 int idstep = 1;
1464
1465 // The shape!
1466 // Given that this leaf component is actually an assembly, its constituent
1467 // component detector shapes comes from its type attribute.
1468 const std::string shapeType = pType->getAttribute("type");
1469 std::shared_ptr<Geometry::IObject> shape = mapTypeNameToShape[shapeType];
1470
1471 // These parameters are in the TYPE defining RectangularDetector
1472 if (pType->hasAttribute("xpixels"))
1473 xpixels = std::stoi(pType->getAttribute("xpixels"));
1474 double xstart = attrToDouble(pType, "xstart");
1475 double xstep = attrToDouble(pType, "xstep");
1476
1477 if (pType->hasAttribute("ypixels"))
1478 ypixels = std::stoi(pType->getAttribute("ypixels"));
1479 double ystart = attrToDouble(pType, "ystart");
1480 double ystep = attrToDouble(pType, "ystep");
1481
1482 // THESE parameters are in the INSTANCE of this type - since they will
1483 // change.
1484 if (pCompElem->hasAttribute("idstart"))
1485 idstart = std::stoi(pCompElem->getAttribute("idstart"));
1486 if (pCompElem->hasAttribute("idfillbyfirst"))
1487 idfillbyfirst_y = (pCompElem->getAttribute("idfillbyfirst") == "y");
1488 // Default ID row step size
1489 if (idfillbyfirst_y)
1490 idstepbyrow = ypixels;
1491 else
1492 idstepbyrow = xpixels;
1493 if (pCompElem->hasAttribute("idstepbyrow")) {
1494 idstepbyrow = std::stoi(pCompElem->getAttribute("idstepbyrow"));
1495 }
1496 // Default ID row step size
1497 if (pCompElem->hasAttribute("idstep"))
1498 idstep = std::stoi(pCompElem->getAttribute("idstep"));
1499
1500 setSideBySideViewLocation(bank, pCompElem);
1501
1502 // Now, initialize all the pixels in the bank
1503 bank->initialize(shape, xpixels, xstart, xstep, ypixels, ystart, ystep, idstart, idfillbyfirst_y, idstepbyrow,
1504 idstep);
1505
1506 // Loop through all detectors in the newly created bank and mark those in
1507 // the instrument.
1508 try {
1509 for (int x = 0; x < bank->nelements(); x++) {
1510 std::shared_ptr<Geometry::ICompAssembly> xColumn = std::dynamic_pointer_cast<Geometry::ICompAssembly>((*bank)[x]);
1511 for (int y = 0; y < xColumn->nelements(); y++) {
1512 std::shared_ptr<Geometry::Detector> detector = std::dynamic_pointer_cast<Geometry::Detector>((*xColumn)[y]);
1513 if (detector) {
1514 // Make default facing for the pixel
1515 auto *comp = static_cast<IComponent *>(detector.get());
1518 // Mark it as a detector (add to the instrument cache)
1519 m_instrument->markAsDetectorIncomplete(detector.get());
1520 }
1521 }
1522 }
1523 } catch (Kernel::Exception::ExistsError &) {
1524 throw Kernel::Exception::InstrumentDefinitionError("Duplicate detector ID found when adding RectangularDetector " +
1525 name + " in XML instrument file" + filename);
1526 }
1527}
1528
1530 const Poco::XML::Element *pLocElem,
1531 const Poco::XML::Element *pCompElem,
1532 const std::string &filename,
1533 const Poco::XML::Element *pType) {
1534 //-------------- Create a StructuredDetector
1535 //------------------------------------------------
1536 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1537
1538 // Create the bank with the given parent.
1539 auto bank = new Geometry::StructuredDetector(name, parent);
1540
1541 // set location for this newly added comp and set facing if specified in
1542 // instrument def. file. Also
1543 // check if any logfiles are referred to through the <parameter> element.
1545 setLogfile(bank, pCompElem,
1546 m_instrument->getLogfileCache()); // params specified within <component>
1547 setLogfile(bank, pLocElem,
1548 m_instrument->getLogfileCache()); // params specified within specific <location>
1549
1550 // Extract all the parameters from the XML attributes
1551 int xpixels = 0;
1552 int ypixels = 0;
1553 int idstart = 0;
1554 bool idfillbyfirst_y = true;
1555 int idstepbyrow = 0;
1556 int idstep = 1;
1557 std::vector<double> xValues;
1558 std::vector<double> yValues;
1559
1560 std::string typeName = pType->getAttribute("name");
1561 // These parameters are in the TYPE defining StructuredDetector
1562 if (pType->hasAttribute("xpixels"))
1563 xpixels = std::stoi(pType->getAttribute("xpixels"));
1564 if (pType->hasAttribute("ypixels"))
1565 ypixels = std::stoi(pType->getAttribute("ypixels"));
1566
1567 // THESE parameters are in the INSTANCE of this type - since they will
1568 // change.
1569 if (pCompElem->hasAttribute("idstart"))
1570 idstart = std::stoi(pCompElem->getAttribute("idstart"));
1571 if (pCompElem->hasAttribute("idfillbyfirst"))
1572 idfillbyfirst_y = (pCompElem->getAttribute("idfillbyfirst") == "y");
1573 // Default ID row step size
1574 if (idfillbyfirst_y)
1575 idstepbyrow = ypixels;
1576 else
1577 idstepbyrow = xpixels;
1578 if (pCompElem->hasAttribute("idstepbyrow")) {
1579 idstepbyrow = std::stoi(pCompElem->getAttribute("idstepbyrow"));
1580 }
1581 // Default ID row step size
1582 if (pCompElem->hasAttribute("idstep"))
1583 idstep = std::stoi(pCompElem->getAttribute("idstep"));
1584
1585 // Access type element which defines structured detecor vertices
1586 Element *pElem = nullptr;
1587 NodeIterator tags(pCompElem->ownerDocument(), NodeFilter::SHOW_ELEMENT);
1588 Node *pNode = tags.nextNode();
1589
1590 while (pNode) {
1591 auto *check = static_cast<Element *>(pNode);
1592 if (pNode->nodeName() == "type" && check->hasAttribute("is")) {
1593 std::string is = check->getAttribute("is");
1594 if (StructuredDetector::compareName(is) && typeName == check->getAttribute("name")) {
1595 pElem = check;
1596 break;
1597 }
1598 }
1599
1600 pNode = tags.nextNode();
1601 }
1602
1603 if (pElem == nullptr)
1604 throw Kernel::Exception::InstrumentDefinitionError("No <type> with attribute is=\"StructuredDetector\"", filename);
1605
1606 // Ensure vertices are present within the IDF
1607 Poco::AutoPtr<NodeList> pNL = pElem->getElementsByTagName("vertex");
1608 if (pNL->length() == 0)
1609 throw Kernel::Exception::InstrumentDefinitionError("StructuredDetector must contain vertices.", filename);
1610
1611 NodeIterator it(pElem, NodeFilter::SHOW_ELEMENT);
1612
1613 pNode = it.nextNode();
1614
1615 while (pNode) {
1616 if (pNode->nodeName() == "vertex") {
1617 auto *pVertElem = static_cast<Element *>(pNode);
1618
1619 if (pVertElem->hasAttribute("x"))
1620 xValues.emplace_back(attrToDouble(pVertElem, "x"));
1621 if (pVertElem->hasAttribute("y"))
1622 yValues.emplace_back(attrToDouble(pVertElem, "y"));
1623 }
1624
1625 pNode = it.nextNode();
1626 }
1627
1628 V3D zVector(0, 0, 1); // Z aligned beam
1629 bool isZBeam = m_instrument->getReferenceFrame()->isVectorPointingAlongBeam(zVector);
1630 // Now, initialize all the pixels in the bank
1631 bank->initialize(xpixels, ypixels, std::move(xValues), std::move(yValues), isZBeam, idstart, idfillbyfirst_y,
1632 idstepbyrow, idstep);
1633
1634 // Loop through all detectors in the newly created bank and mark those in
1635 // the instrument.
1636 try {
1637 for (int x = 0; x < bank->nelements(); x++) {
1638 std::shared_ptr<Geometry::ICompAssembly> xColumn = std::dynamic_pointer_cast<Geometry::ICompAssembly>((*bank)[x]);
1639 for (int y = 0; y < xColumn->nelements(); y++) {
1640 std::shared_ptr<Geometry::Detector> detector = std::dynamic_pointer_cast<Geometry::Detector>((*xColumn)[y]);
1641 if (detector) {
1642 // Make default facing for the pixel
1643 auto *comp = static_cast<IComponent *>(detector.get());
1646 // Mark it as a detector (add to the instrument cache)
1647 m_instrument->markAsDetectorIncomplete(detector.get());
1648 }
1649 }
1650 }
1651 } catch (Kernel::Exception::ExistsError &) {
1652 throw Kernel::Exception::InstrumentDefinitionError("Duplicate detector ID found when adding StructuredDetector " +
1653 name + " in XML instrument file" + filename);
1654 }
1655}
1656
1657//-----------------------------------------------------------------------------------------------------------------------
1676void InstrumentDefinitionParser::appendLeaf(Geometry::ICompAssembly *parent, const Poco::XML::Element *pLocElem,
1677 const Poco::XML::Element *pCompElem, IdList &idList) {
1678 const std::string filename = m_xmlFile->getFileFullPathStr();
1679
1680 //--- Get the detector's X/Y pixel sizes (optional) ---
1681 // Read detector IDs into idlist if required
1682 // Note idlist may be defined for any component
1683 // Note any new idlist found will take precedence.
1684
1685 if (pCompElem->hasAttribute("idlist")) {
1686 std::string idlist = pCompElem->getAttribute("idlist");
1687
1688 if (idlist != idList.idname) {
1689 Element *pFound = pCompElem->ownerDocument()->getElementById(idlist, "idname");
1690
1691 if (pFound == nullptr) {
1693 "No <idlist> with name idname=\"" + idlist + "\" present in instrument definition file.", filename);
1694 }
1695
1696 idList.reset();
1697 populateIdList(pFound, idList);
1698 }
1699 }
1700
1701 // get the type element of the component element in order to determine if
1702 // the
1703 // type
1704 // belong to the category: "detector", "SamplePos or "Source".
1705
1706 std::string typeName = pCompElem->getAttribute("type");
1707 Element *pType = getTypeElement[typeName];
1708
1709 std::string category;
1710 if (pType->hasAttribute("is"))
1711 category = pType->getAttribute("is");
1712
1713 static const boost::regex exp("Detector|detector|Monitor|monitor");
1714
1715 // do stuff a bit differently depending on which category the type belong to
1716 if (GridDetector::compareName(category)) {
1717 createGridDetector(parent, pLocElem, pCompElem, filename, pType);
1718 } else if (RectangularDetector::compareName(category)) {
1719 createRectangularDetector(parent, pLocElem, pCompElem, filename, pType);
1720 } else if (StructuredDetector::compareName(category)) {
1721 createStructuredDetector(parent, pLocElem, pCompElem, filename, pType);
1722 } else if (boost::regex_match(category, exp)) {
1723 createDetectorOrMonitor(parent, pLocElem, pCompElem, filename, idList, category);
1724 } else {
1725 //-------------- Not a Detector, RectangularDetector or Structured Detector
1726 //------------------------------
1727 IComponent *comp;
1728 if (category == "SamplePos" || category == "samplePos") {
1729 // check if special SamplePos Component
1730 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1731 comp = new Geometry::Component(name, parent);
1732 m_instrument->markAsSamplePos(comp);
1733 } else {
1734 std::string name = InstrumentDefinitionParser::getNameOfLocationElement(pLocElem, pCompElem);
1735
1736 comp = new Geometry::ObjComponent(name, mapTypeNameToShape[typeName], parent);
1737 }
1738 parent->add(comp);
1739
1740 // check if special Source Component
1741 if (category == "Source" || category == "source") {
1742 m_instrument->markAsSource(comp);
1743 }
1744
1745 // set location for this newly added comp and set facing if specified in
1746 // instrument def. file. Also
1747 // check if any logfiles are referred to through the <parameter> element.
1748
1750 setFacing(comp, pLocElem);
1751 setLogfile(comp, pCompElem,
1752 m_instrument->getLogfileCache()); // params specified within <component>
1753 setLogfile(comp, pLocElem,
1754 m_instrument->getLogfileCache()); // params specified within
1755 // specific <location>
1756 }
1757}
1758
1759//-----------------------------------------------------------------------------------------------------------------------
1769void InstrumentDefinitionParser::populateIdList(Poco::XML::Element *pE, IdList &idList) {
1770 const std::string filename = m_xmlFile->getFileFullPathStr();
1771
1772 if ((pE->tagName()) != "idlist") {
1773 g_log.error("Argument to function createIdList must be a pointer to an XML "
1774 "element with tag name idlist.");
1775 throw std::logic_error("Argument to function createIdList must be a "
1776 "pointer to an XML element with tag name idlist.");
1777 }
1778
1779 // set name of idlist
1780
1781 idList.idname = pE->getAttribute("idname");
1782
1783 // If idname element has start and end attributes then just use those to
1784 // populate idlist.
1785 // Otherwise id sub-elements
1786
1787 if (pE->hasAttribute("start")) {
1788 int startID = std::stoi(pE->getAttribute("start"));
1789
1790 int endID;
1791 if (pE->hasAttribute("end"))
1792 endID = std::stoi(pE->getAttribute("end"));
1793 else
1794 endID = startID;
1795
1796 int increment = 1;
1797 if (pE->hasAttribute("step"))
1798 increment = std::stoi(pE->getAttribute("step"));
1799
1800 if (0 == increment) {
1801 std::stringstream ss;
1802 ss << "The step element cannot be zero, got start: " << startID << ", end: " << endID << ", step: " << increment;
1803 throw Kernel::Exception::InstrumentDefinitionError(ss.str(), filename);
1804 }
1805
1806 // check the start end and increment values are sensible
1807 int steps = (endID - startID) / increment;
1808 if (steps < 0) {
1809 std::stringstream ss;
1810 ss << "The start, end, and step elements do not allow a single id in "
1811 "the "
1812 "idlist entry - ";
1813 ss << "start: " << startID << ", end: " << endID << ", step: " << increment;
1814
1815 throw Kernel::Exception::InstrumentDefinitionError(ss.str(), filename);
1816 }
1817
1818 idList.vec.reserve(steps);
1819 for (int i = startID; i != endID + increment; i += increment) {
1820 idList.vec.emplace_back(i);
1821 }
1822 } else {
1823 // test first if any <id> elements
1824
1825 Poco::AutoPtr<NodeList> pNL = pE->getElementsByTagName("id");
1826
1827 if (pNL->length() == 0) {
1828 throw Kernel::Exception::InstrumentDefinitionError("No id subelement of idlist element in XML instrument file",
1829 filename);
1830 }
1831
1832 // get id numbers
1833
1834 NodeIterator it(pE, NodeFilter::SHOW_ELEMENT);
1835
1836 Node *pNode = it.nextNode();
1837 while (pNode) {
1838 if (pNode->nodeName() == "id") {
1839 auto *pIDElem = static_cast<Element *>(pNode);
1840
1841 if (pIDElem->hasAttribute("val")) {
1842 int valID = std::stoi(pIDElem->getAttribute("val"));
1843 idList.vec.emplace_back(valID);
1844 } else if (pIDElem->hasAttribute("start")) {
1845 int startID = std::stoi(pIDElem->getAttribute("start"));
1846
1847 int endID;
1848 if (pIDElem->hasAttribute("end"))
1849 endID = std::stoi(pIDElem->getAttribute("end"));
1850 else
1851 endID = startID;
1852
1853 int increment = 1;
1854 if (pIDElem->hasAttribute("step"))
1855 increment = std::stoi(pIDElem->getAttribute("step"));
1856
1857 // check the start end and increment values are sensible
1858 if (0 == increment) {
1859 std::stringstream ss;
1860 ss << "The step element cannot be zero, found step: " << increment;
1861
1862 throw Kernel::Exception::InstrumentDefinitionError(ss.str(), filename);
1863 }
1864 int numSteps = (endID - startID) / increment;
1865 if (numSteps < 0) {
1866 std::stringstream ss;
1867 ss << "The start, end, and step elements do not allow a single "
1868 "id "
1869 "in the idlist entry - ";
1870 ss << "start: " << startID << ", end: " << endID << ", step: " << increment;
1871
1872 throw Kernel::Exception::InstrumentDefinitionError(ss.str(), filename);
1873 }
1874
1875 idList.vec.reserve(numSteps);
1876 for (int i = startID; i != endID + increment; i += increment) {
1877 idList.vec.emplace_back(i);
1878 }
1879 } else {
1881 "id subelement of idlist " + std::string("element wrongly specified in XML instrument file"), filename);
1882 }
1883 }
1884
1885 pNode = it.nextNode();
1886 } // end while loop
1887 }
1888}
1889
1890//-----------------------------------------------------------------------------------------------------------------------
1899bool InstrumentDefinitionParser::isAssembly(const std::string &type) const {
1900 const std::string filename = m_xmlFile->getFileFullPathStr();
1901 auto it = isTypeAssembly.find(type);
1902
1903 if (it == isTypeAssembly.end()) {
1904 throw Kernel::Exception::InstrumentDefinitionError("type with name = " + type + " not defined.", filename);
1905 }
1906
1907 return it->second;
1908}
1909
1910//-----------------------------------------------------------------------------------------------------------------------
1925
1926//-----------------------------------------------------------------------------------------------------------------------
1936 Kernel::V3D pos = in->getPos();
1937
1938 // vector from facing object to component we want to rotate
1939 Kernel::V3D facingDirection = pos - facingPoint;
1940 const auto facingDirLength = facingDirection.norm();
1941 if (facingDirLength == 0.0)
1942 return;
1943 facingDirection /= facingDirLength;
1944
1945 // now aim to rotate shape such that the z-axis of of the object we want to
1946 // rotate points in the direction of facingDirection. That way the XY plane
1947 // faces the 'facing object'.
1948 constexpr Kernel::V3D z(0, 0, 1);
1949 Kernel::Quat R = in->getRotation();
1950 R.inverse();
1951 R.rotate(facingDirection);
1952
1953 Kernel::V3D normal = facingDirection.cross_prod(z);
1954 const auto normalLength = normal.norm();
1955 if (normalLength == 0.) {
1956 normal = normalize(-facingDirection);
1957 } else {
1958 normal /= normalLength;
1959 }
1960 double theta = (180.0 / M_PI) * facingDirection.angle(z);
1961
1962 if (normal.norm() > 0.0)
1963 in->rotate(Kernel::Quat(-theta, normal));
1964 else {
1965 // To take into account the case where the facing direction is in the
1966 // (0,0,1) or (0,0,-1) direction.
1967 in->rotate(Kernel::Quat(-theta, Kernel::V3D(0, 1, 0)));
1968 }
1969}
1970
1971//-----------------------------------------------------------------------------------------------------------------------
1978 Kernel::V3D retV3D;
1979
1980 // Polar coordinates can be labelled as (r,t,p) or (R,theta,phi)
1981 if (pElem->hasAttribute("r") || pElem->hasAttribute("t") || pElem->hasAttribute("p") || pElem->hasAttribute("R") ||
1982 pElem->hasAttribute("theta") || pElem->hasAttribute("phi")) {
1983 double R = attrToDouble(pElem, "r");
1984 double theta = m_angleConvertConst * attrToDouble(pElem, "t");
1985 double phi = m_angleConvertConst * attrToDouble(pElem, "p");
1986
1987 if (pElem->hasAttribute("R"))
1988 R = attrToDouble(pElem, "R");
1989 if (pElem->hasAttribute("theta"))
1990 theta = m_angleConvertConst * attrToDouble(pElem, "theta");
1991 if (pElem->hasAttribute("phi"))
1992 phi = m_angleConvertConst * attrToDouble(pElem, "phi");
1993
1994 retV3D.spherical(R, theta, phi);
1995 } else {
1996 double x = attrToDouble(pElem, "x");
1997 double y = attrToDouble(pElem, "y");
1998 double z = attrToDouble(pElem, "z");
1999
2000 retV3D(x, y, z);
2001 }
2002
2003 return retV3D;
2004}
2005
2006//-----------------------------------------------------------------------------------------------------------------------
2020void InstrumentDefinitionParser::setFacing(Geometry::IComponent *comp, const Poco::XML::Element *pElem) {
2021 // Require that pElem points to an element with tag name 'location'
2022
2023 if ((pElem->tagName()) != "location") {
2024 g_log.error("Second argument to function setLocation must be a pointer to "
2025 "an XML element with tag name location.");
2026 throw std::logic_error("Second argument to function setLocation must be a "
2027 "pointer to an XML element with tag name location.");
2028 }
2029
2030 Element *facingElem = pElem->getChildElement("facing");
2031 if (facingElem) {
2032 // check if user want to rotate about z-axis before potentially applying
2033 // facing
2034
2035 if (facingElem->hasAttribute("rot")) {
2036 double rotAngle = m_angleConvertConst * attrToDouble(facingElem, "rot"); // assumed to be in degrees
2037 comp->rotate(Kernel::Quat(rotAngle, Kernel::V3D(0, 0, 1)));
2038 }
2039
2040 // For now assume that if has val attribute it means facing = none. This
2041 // option only has an
2042 // effect when a default facing setting is set. In which case this then
2043 // means "ignore the
2044 // default facing setting" for this component
2045
2046 if (facingElem->hasAttribute("val"))
2047 return;
2048
2049 // Face the component, i.e. rotate the z-axis of the component such that
2050 // it
2051 // points in the direction from
2052 // the point x,y,z (or r,t,p) specified by the <facing> xml element
2053 // towards
2054 // the component
2055
2057
2058 } else // so if no facing element associated with location element apply
2059 // default facing if set
2062}
2063
2064//-----------------------------------------------------------------------------------------------------------------------
2075void InstrumentDefinitionParser::setLogfile(const Geometry::IComponent *comp, const Poco::XML::Element *pElem,
2076 InstrumentParameterCache &logfileCache, const std::string &requestedDate) {
2077 const std::string filename = m_xmlFile->getFileFullPathStr();
2078
2079 // The purpose below is to have a quicker way to judge if pElem contains a
2080 // parameter, see
2081 // defintion of m_hasParameterElement for more info
2083 if (m_hasParameterElement.end() == std::find(m_hasParameterElement.begin(), m_hasParameterElement.end(), pElem))
2084 return;
2085
2086 Poco::AutoPtr<NodeList> pNL_comp = pElem->childNodes(); // here get all child nodes
2087 unsigned long pNL_comp_length = pNL_comp->length();
2088
2089 for (unsigned long i = 0; i < pNL_comp_length; i++) {
2090 // we are only interest in the top level parameter elements hence
2091 // the reason for the if statement below
2092 if (!((pNL_comp->item(i))->nodeType() == Node::ELEMENT_NODE && ((pNL_comp->item(i))->nodeName()) == "parameter"))
2093 continue;
2094
2095 auto *pParamElem = static_cast<Element *>(pNL_comp->item(i));
2096
2097 if (!pParamElem->hasAttribute("name"))
2099 "XML element with name or type = " + comp->getName() +
2100 " contain <parameter> element with no name attribute in XML "
2101 "instrument file",
2102 filename);
2103
2104 std::string paramName = pParamElem->getAttribute("name");
2105
2106 if (paramName == "rot" || paramName == "pos") {
2107 g_log.error() << "XML element with name or type = " << comp->getName()
2108 << " contains <parameter> element with name=\"" << paramName << "\"."
2109 << " This is a reserved Mantid keyword. Please use other name, "
2110 << "and see docs.mantidproject.org/concepts/InstrumentDefinitionFile for list of reserved "
2111 "keywords."
2112 << " This parameter is ignored";
2113 continue;
2114 }
2115
2116 std::string visible = "true";
2117 if (pParamElem->hasAttribute("visible")) {
2118 visible = pParamElem->getAttribute("visible");
2119 }
2120
2121 DateAndTime validityDate;
2122
2123 if (requestedDate.empty()) {
2124 validityDate = DateAndTime::getCurrentTime();
2125 } else {
2126 validityDate.setFromISO8601(requestedDate);
2127 }
2128
2129 std::string logfileID;
2130 std::string value;
2131
2132 DateAndTime validFrom;
2133 DateAndTime validTo;
2134
2135 std::string type = "double"; // default
2136 std::string extractSingleValueAs = "mean"; // default
2137 std::string eq;
2138
2139 Poco::AutoPtr<NodeList> pNLvalue = pParamElem->getElementsByTagName("value");
2140 size_t numberValueEle = pNLvalue->length();
2141 Element *pValueElem;
2142
2143 Poco::AutoPtr<NodeList> pNLlogfile = pParamElem->getElementsByTagName("logfile");
2144 size_t numberLogfileEle = pNLlogfile->length();
2145 Element *pLogfileElem;
2146
2147 Poco::AutoPtr<NodeList> pNLLookUp = pParamElem->getElementsByTagName("lookuptable");
2148 size_t numberLookUp = pNLLookUp->length();
2149
2150 Poco::AutoPtr<NodeList> pNLFormula = pParamElem->getElementsByTagName("formula");
2151 size_t numberFormula = pNLFormula->length();
2152
2153 if ((numberValueEle > 0 && numberLogfileEle + numberLookUp + numberFormula > 0) ||
2154 (numberValueEle == 0 && numberLogfileEle + numberLookUp + numberFormula > 1)) {
2155 g_log.warning() << "XML element with name or type = " << comp->getName()
2156 << " contains <parameter> element where the value of the "
2157 << "parameter has been specified more than once. See "
2158 << "docs.mantidproject.org/concepts/InstrumentDefinitionFile for how the value of the "
2159 << "parameter is set in this case.";
2160 }
2161
2162 if (numberValueEle + numberLogfileEle + numberLookUp + numberFormula == 0) {
2163 g_log.error() << "XML element with name or type = " << comp->getName()
2164 << " contains <parameter> for which no value is specified."
2165 << " See docs.mantidproject.org/concepts/InstrumentDefinitionFile for how to set the value"
2166 << " of a parameter. This parameter is ignored.";
2167 continue;
2168 }
2169
2170 DateAndTime currentValidFrom;
2171 DateAndTime currentValidTo;
2172 currentValidFrom.setToMinimum();
2173 currentValidTo.setToMaximum();
2174
2175 // if more than one <value> specified for a parameter, check the validity
2176 // range
2177 if (numberValueEle >= 1) {
2178 bool hasValue = false;
2179
2180 for (unsigned long j = 0; j < numberValueEle; ++j) {
2181 pValueElem = static_cast<Element *>(pNLvalue->item(j));
2182
2183 if (!pValueElem->hasAttribute(("val")))
2184 continue;
2185
2186 validFrom.setToMinimum();
2187 if (pValueElem->hasAttribute("valid-from"))
2188 validFrom.setFromISO8601(pValueElem->getAttribute("valid-from"));
2189
2190 validTo.setToMaximum();
2191 if (pValueElem->hasAttribute("valid-to"))
2192 validTo.setFromISO8601(pValueElem->getAttribute("valid-to"));
2193
2194 if (validFrom <= validityDate && validityDate <= validTo &&
2195 (validFrom > currentValidFrom || (validFrom == currentValidFrom && validTo <= currentValidTo))) {
2196
2197 currentValidFrom = validFrom;
2198 currentValidTo = validTo;
2199 } else
2200 continue;
2201 hasValue = true;
2202 value = pValueElem->getAttribute("val");
2203 }
2204
2205 if (!hasValue) {
2207 "XML element with name or type = " + comp->getName() +
2208 " contains <parameter> element with invalid syntax for its "
2209 "subelement <value>. Correct syntax is <value val=\"\"/>",
2210 filename);
2211 }
2212
2213 } else if (numberLogfileEle >= 1) {
2214 // <logfile > tag was used at least once.
2215 pLogfileElem = static_cast<Element *>(pNLlogfile->item(0));
2216 if (!pLogfileElem->hasAttribute("id"))
2218 "XML element with name or type = " + comp->getName() +
2219 " contains <parameter> element with invalid syntax for its "
2220 "subelement logfile>." +
2221 " Correct syntax is <logfile id=\"\"/>",
2222 filename);
2223 logfileID = pLogfileElem->getAttribute("id");
2224
2225 if (pLogfileElem->hasAttribute("eq"))
2226 eq = pLogfileElem->getAttribute("eq");
2227 if (pLogfileElem->hasAttribute("extract-single-value-as"))
2228 extractSingleValueAs = pLogfileElem->getAttribute("extract-single-value-as");
2229 }
2230
2231 if (pParamElem->hasAttribute("type"))
2232 type = pParamElem->getAttribute("type");
2233
2234 // check if <fixed /> element present
2235
2236 bool fixed = false;
2237 Poco::AutoPtr<NodeList> pNLFixed = pParamElem->getElementsByTagName("fixed");
2238 size_t numberFixed = pNLFixed->length();
2239 if (numberFixed >= 1) {
2240 fixed = true;
2241 }
2242
2243 // some processing
2244
2245 std::string fittingFunction;
2246 std::string tie;
2247
2248 if (type == "fitting") {
2249 size_t found = paramName.find(':');
2250 if (found != std::string::npos) {
2251 // check that only one : in name
2252 size_t index = paramName.find(':', found + 1);
2253 if (index != std::string::npos) {
2254 g_log.error() << "Fitting <parameter> in instrument definition file defined "
2255 "with"
2256 << " more than one column character :. One must used.\n";
2257 } else {
2258 fittingFunction = paramName.substr(0, found);
2259 paramName = paramName.substr(found + 1, paramName.size());
2260 }
2261 }
2262 }
2263
2264 if (fixed) {
2265 std::ostringstream str;
2266 str << paramName << "=" << value;
2267 tie = str.str();
2268 }
2269
2270 // check if <min> or <max> elements present
2271
2272 std::vector<std::string> constraint(2, "");
2273
2274 Poco::AutoPtr<NodeList> pNLMin = pParamElem->getElementsByTagName("min");
2275 size_t numberMin = pNLMin->length();
2276 Poco::AutoPtr<NodeList> pNLMax = pParamElem->getElementsByTagName("max");
2277 size_t numberMax = pNLMax->length();
2278
2279 if (numberMin >= 1) {
2280 auto *pMin = static_cast<Element *>(pNLMin->item(0));
2281 constraint[0] = pMin->getAttribute("val");
2282 }
2283 if (numberMax >= 1) {
2284 auto *pMax = static_cast<Element *>(pNLMax->item(0));
2285 constraint[1] = pMax->getAttribute("val");
2286 }
2287
2288 // check if penalty-factor> elements present
2289
2290 std::string penaltyFactor;
2291
2292 Poco::AutoPtr<NodeList> pNL_penaltyFactor = pParamElem->getElementsByTagName("penalty-factor");
2293 size_t numberPenaltyFactor = pNL_penaltyFactor->length();
2294
2295 if (numberPenaltyFactor >= 1) {
2296 auto *pPenaltyFactor = static_cast<Element *>(pNL_penaltyFactor->item(0));
2297 penaltyFactor = pPenaltyFactor->getAttribute("val");
2298 }
2299
2300 // Check if look up table is specified
2301
2302 std::vector<std::string> allowedUnits = UnitFactory::Instance().getKeys();
2303
2304 std::shared_ptr<Interpolation> interpolation = std::make_shared<Interpolation>();
2305
2306 if (numberLookUp >= 1) {
2307 auto *pLookUp = static_cast<Element *>(pNLLookUp->item(0));
2308
2309 if (pLookUp->hasAttribute("interpolation"))
2310 interpolation->setMethod(pLookUp->getAttribute("interpolation"));
2311 if (pLookUp->hasAttribute("x-unit")) {
2312 std::vector<std::string>::iterator it;
2313 it = find(allowedUnits.begin(), allowedUnits.end(), pLookUp->getAttribute("x-unit"));
2314 if (it == allowedUnits.end()) {
2315 g_log.warning() << "x-unit used with interpolation table must be "
2316 "one of the recognised units "
2317 << " see http://docs.mantidproject.org/concepts/UnitFactory";
2318 } else
2319 interpolation->setXUnit(pLookUp->getAttribute("x-unit"));
2320 }
2321 if (pLookUp->hasAttribute("y-unit")) {
2322 std::vector<std::string>::iterator it;
2323 it = find(allowedUnits.begin(), allowedUnits.end(), pLookUp->getAttribute("y-unit"));
2324 if (it == allowedUnits.end()) {
2325 g_log.warning() << "y-unit used with interpolation table must be "
2326 "one of the recognised units "
2327 << " see http://docs.mantidproject.org/concepts/UnitFactory";
2328 } else
2329 interpolation->setYUnit(pLookUp->getAttribute("y-unit"));
2330 }
2331
2332 Poco::AutoPtr<NodeList> pNLpoint = pLookUp->getElementsByTagName("point");
2333 unsigned long numberPoint = pNLpoint->length();
2334
2335 for (unsigned long j = 0; j < numberPoint; j++) {
2336 const auto *pPoint = static_cast<Element *>(pNLpoint->item(j));
2337 double x = attrToDouble(pPoint, "x");
2338 double y = attrToDouble(pPoint, "y");
2339 interpolation->addPoint(x, y);
2340 }
2341 }
2342
2343 // Check if formula is specified
2344
2345 std::string formula;
2346 std::string formulaUnit;
2347 std::string resultUnit;
2348
2349 if (numberFormula >= 1) {
2350 auto *pFormula = static_cast<Element *>(pNLFormula->item(0));
2351 formula = pFormula->getAttribute("eq");
2352 if (pFormula->hasAttribute("unit")) {
2353 std::vector<std::string>::iterator it;
2354 it = find(allowedUnits.begin(), allowedUnits.end(), pFormula->getAttribute("unit"));
2355 if (it == allowedUnits.end()) {
2356 g_log.warning() << "unit attribute used with formula must be one "
2357 "of the recognized units "
2358 << " see http://docs.mantidproject.org/concepts/UnitFactory";
2359 } else
2360 formulaUnit = pFormula->getAttribute("unit");
2361 }
2362 if (pFormula->hasAttribute("result-unit"))
2363 resultUnit = pFormula->getAttribute("result-unit");
2364 }
2365 // Check if parameter description is
2366 std::string description;
2367
2368 Poco::AutoPtr<NodeList> pNLDescription = pParamElem->getElementsByTagName("description");
2369 size_t numberDescription = pNLDescription->length();
2370
2371 if (numberDescription >= 1) {
2372 // use only first description from a list
2373 auto *pDescription = static_cast<Element *>(pNLDescription->item(0));
2374 description = pDescription->getAttribute("is");
2375 }
2376
2377 // Qualify the cache key with the fitting-function name (when present) so that two functions
2378 // attached to the same component sharing a parameter name (e.g. IkedaCarpenterPV:Alpha0 and
2379 // IkedaCarpenterMD:Alpha0) do not overwrite each other. The stored XMLInstrumentParameter
2380 // still keeps the unqualified paramName so downstream lookups by short name continue to work.
2381 const std::string cacheParamKey = fittingFunction.empty() ? paramName : fittingFunction + ":" + paramName;
2382 auto cacheKey = std::make_pair(cacheParamKey, comp);
2383 auto cacheValue = std::make_shared<XMLInstrumentParameter>(
2384 logfileID, value, interpolation, formula, formulaUnit, resultUnit, paramName, type, tie, constraint,
2385 penaltyFactor, fittingFunction, extractSingleValueAs, eq, comp, m_angleConvertConst, description, visible);
2386 auto inserted = logfileCache.emplace(cacheKey, cacheValue);
2387 if (!inserted.second) {
2388 logfileCache[cacheKey] = cacheValue;
2389 }
2390 } // end element loop
2391}
2392
2393//-----------------------------------------------------------------------------------------------------------------------
2405void InstrumentDefinitionParser::setComponentLinks(std::shared_ptr<Geometry::Instrument> &instrument,
2406 Poco::XML::Element *pRootElem, Kernel::ProgressBase *progress,
2407 const std::string &requestedDate) {
2408 setComponentLinks(instrument, pRootElem, instrument->getLogfileCache(), progress, requestedDate);
2409}
2410
2420void InstrumentDefinitionParser::setComponentLinks(const std::shared_ptr<const Geometry::Instrument> &instrument,
2421 Poco::XML::Element *pRootElem,
2422 InstrumentParameterCache &logfileCache,
2423 Kernel::ProgressBase *progress, const std::string &requestedDate) {
2424 // check if any logfile cache units set. As of this writing the only unit to
2425 // check is if "angle=radian"
2426 const auto &units = instrument->getLogfileUnit();
2427 const auto unit_it = units.find("angle");
2428 if (unit_it != units.end())
2429 if (unit_it->second == "radian")
2430 m_angleConvertConst = 180.0 / M_PI;
2431
2432 const std::string elemName = "component-link";
2433 Poco::AutoPtr<NodeList> pNL_link = pRootElem->getElementsByTagName(elemName);
2434 unsigned long numberLinks = pNL_link->length();
2435
2436 if (progress)
2437 progress->resetNumSteps(static_cast<int64_t>(numberLinks), 0.0, 0.95);
2438
2439 Node *curNode = pRootElem->firstChild();
2440 while (curNode) {
2441 if (curNode->nodeType() == Node::ELEMENT_NODE && curNode->nodeName() == elemName) {
2442 auto *curElem = static_cast<Element *>(curNode);
2443
2444 if (progress) {
2445 if (progress->hasCancellationBeenRequested())
2446 return;
2447 progress->report("Loading parameters");
2448 }
2449
2450 std::string id = curElem->getAttribute("id");
2451 std::string name = curElem->getAttribute("name");
2452 std::vector<std::shared_ptr<const Geometry::IComponent>> sharedIComp;
2453
2454 // If available, use the detector id as it's the most specific.
2455 if (id.length() > 0) {
2456 int detid;
2457 std::stringstream(id) >> detid;
2458 std::shared_ptr<const Geometry::IComponent> detector = instrument->getDetector(static_cast<detid_t>(detid));
2459
2460 // If we didn't find anything with the detector id, explain why to the
2461 // user, and throw an exception.
2462 if (!detector) {
2463 g_log.error() << "Error whilst loading parameters. No detector "
2464 "found with id '"
2465 << detid << "'\n";
2466 g_log.error() << "Please check that your detectors' ids are correct.\n";
2467 throw Kernel::Exception::InstrumentDefinitionError("Invalid detector id in component-link tag.");
2468 }
2469
2470 sharedIComp.emplace_back(detector);
2471
2472 // If the user also supplied a name, make sure it's consistent with the detector id.
2473 if (name.length() > 0) {
2474 auto comp = std::dynamic_pointer_cast<const IComponent>(detector);
2475 if (comp) {
2476 bool consistent = (comp->getFullName() == name || comp->getName() == name);
2477 if (!consistent) {
2478 g_log.warning() << "Error whilst loading parameters. Name '" << name << "' does not match id '" << detid
2479 << "'.\n";
2480 g_log.warning() << "Parameters have been applied to detector with id '" << detid
2481 << "'. Please check the name is correct.\n";
2482 }
2483 }
2484 }
2485 } else {
2486 // No detector id given, fall back to using the name
2487 if (name.find('/', 0) == std::string::npos) { // Simple name, look for all components of that name.
2488 sharedIComp = instrument->getAllComponentsWithName(name);
2489 } else { // Pathname given. Assume it is unique.
2490 std::shared_ptr<const Geometry::IComponent> shared = instrument->getComponentByName(name);
2491 sharedIComp.emplace_back(shared);
2492 }
2493 }
2494
2495 for (auto &ptr : sharedIComp) {
2496 std::shared_ptr<const Geometry::Component> sharedComp =
2497 std::dynamic_pointer_cast<const Geometry::Component>(ptr);
2498 if (sharedComp) { // Not empty Component
2499 if (sharedComp->isParametrized()) {
2500 setLogfile(sharedComp->base(), curElem, logfileCache, requestedDate);
2501 } else {
2502 setLogfile(ptr.get(), curElem, logfileCache, requestedDate);
2503 }
2504 }
2505 }
2506 }
2507 curNode = curNode->nextSibling();
2508 }
2509}
2510
2519 const std::string cacheFullPath = cacheToApply->getFileFullPathStr();
2520 g_log.information("Loading geometry cache from " + cacheFullPath);
2521 // create a vtk reader
2522 std::shared_ptr<Mantid::Geometry::vtkGeometryCacheReader> reader;
2523 try {
2524 reader = std::make_shared<Mantid::Geometry::vtkGeometryCacheReader>(cacheFullPath);
2525 } catch (const std::exception &e) {
2526 g_log.warning() << "Unable to read geometry cache " << cacheFullPath << ": " << e.what()
2527 << "\nThe cache file will be deleted and recreated.\n";
2528 std::error_code removeError;
2529 std::filesystem::remove(cacheFullPath, removeError);
2530 if (removeError) {
2531 g_log.warning() << "Unable to delete the invalid geometry cache " << cacheFullPath << ": "
2532 << removeError.message() << "\n";
2533 }
2534 return false;
2535 }
2536 std::map<std::string, std::shared_ptr<Geometry::IObject>>::iterator objItr;
2537 for (objItr = mapTypeNameToShape.begin(); objItr != mapTypeNameToShape.end(); ++objItr) {
2538 // caching only applies to CSGObject
2539 if (auto csgObj = std::dynamic_pointer_cast<CSGObject>(((*objItr).second))) {
2540 csgObj->setVtkGeometryCacheReader(reader);
2541 }
2542 }
2543 return true;
2544}
2545
2553 IDFObject_const_sptr fallBackCache) {
2554 IDFObject_const_sptr usedCache = std::move(firstChoiceCache);
2555 auto cachingOption = WroteGeomCache;
2556
2557 g_log.notice("Geometry cache is not available");
2558 try {
2559 std::filesystem::path dir = usedCache->getParentDirectory();
2560 if (!dir.empty() && !std::filesystem::exists(dir)) {
2561 usedCache = std::move(fallBackCache);
2562 cachingOption = WroteCacheTemp;
2563 g_log.information() << "Geometrycache directory does not exist, writing cache "
2564 "to system temp.\n";
2565 } else if (!dir.empty() && (std::filesystem::status(dir).permissions() & std::filesystem::perms::owner_write) ==
2566 std::filesystem::perms::none) {
2567 usedCache = std::move(fallBackCache);
2568 cachingOption = WroteCacheTemp;
2569 g_log.information() << "Geometrycache directory is read only, writing cache "
2570 "to system temp.\n";
2571 }
2572 } catch (std::filesystem::filesystem_error &) {
2573 g_log.error() << "Unable to find instrument definition while attempting to "
2574 "write cache.\n";
2575 throw std::runtime_error("Unable to find instrument definition while "
2576 "attempting to write cache.\n");
2577 }
2578 const std::string cacheFullPath = usedCache->getFileFullPathStr();
2579 g_log.notice() << "Creating cache in " << cacheFullPath << "\n";
2580 // create a vtk writer
2581 std::map<std::string, std::shared_ptr<Geometry::IObject>>::iterator objItr;
2582 std::shared_ptr<Mantid::Geometry::vtkGeometryCacheWriter> writer(
2583 new Mantid::Geometry::vtkGeometryCacheWriter(cacheFullPath));
2584 for (objItr = mapTypeNameToShape.begin(); objItr != mapTypeNameToShape.end(); ++objItr) {
2585 // caching only applies to CSGObject
2586 if (auto csgObj = std::dynamic_pointer_cast<CSGObject>(((*objItr).second))) {
2587 csgObj->setVtkGeometryCacheWriter(writer);
2588 }
2589 }
2590 writer->write();
2591 return cachingOption;
2592}
2593
2598 // Get cached file name
2599 // If the instrument directory is writable, put them there else use
2600 // temporary
2601 // directory.
2602 std::filesystem::path fallBackPath =
2603 std::filesystem::path(ConfigService::Instance().getTempDir()) / (this->getMangledName() + ".vtp");
2604 IDFObject_const_sptr fallBackCache = std::make_shared<const IDFObject>(fallBackPath.string());
2605 CachingOption cachingOption = NoneApplied;
2606 // An existing cache file that cannot be read is deleted by applyCache, so
2607 // that it is regenerated below from the instrument definition.
2608 if (m_cacheFile->exists() && applyCache(m_cacheFile)) {
2609 cachingOption = ReadGeomCache;
2610 } else if (fallBackCache->exists() && applyCache(fallBackCache)) {
2611 cachingOption = ReadFallBack;
2612 } else {
2613 cachingOption = writeAndApplyCache(m_cacheFile, fallBackCache);
2614 }
2615 return cachingOption;
2616}
2617
2625
2627 // Create a copy of the instrument
2628 auto physical = std::make_unique<Instrument>(*m_instrument);
2629 // Store the physical instrument 'inside' the neutronic instrument
2630 m_instrument->setPhysicalInstrument(std::move(physical));
2631
2632 // Now we manipulate the original instrument (m_instrument) to hold
2633 // neutronic positions
2634 for (const auto &component : m_neutronicPos) {
2635 if (component.second) {
2636 setLocation(component.first, component.second, m_angleConvertConst, m_deltaOffsets);
2637 // TODO: Do we need to deal with 'facing'???
2638
2639 // Check for a 'type' attribute, indicating that we want to set the
2640 // neutronic shape
2641 if (component.second->hasAttribute("type") && dynamic_cast<ObjComponent *>(component.first)) {
2642 const Poco::XML::XMLString shapeName = component.second->getAttribute("type");
2643 auto shapeIt = mapTypeNameToShape.find(shapeName);
2644 if (shapeIt != mapTypeNameToShape.end()) {
2645 // Change the shape on the current component to the one requested
2646 auto objCmpt = dynamic_cast<ObjComponent *>(component.first);
2647 if (objCmpt)
2648 objCmpt->setShape(shapeIt->second);
2649 } else {
2650 throw Exception::InstrumentDefinitionError("Requested type " + shapeName + " not defined in IDF");
2651 }
2652 }
2653 } else // We have a null Element*, which signals a detector with no neutronic position
2654 {
2655 // In mixed mode a detector with no neutronic tag keeps its physical position, so an IDF
2656 // can give neutronic positions for the analysed detectors alone while diffraction,
2657 // graphite and monitor detectors stay where they are. Otherwise it is removed.
2659 auto *det = dynamic_cast<Detector *>(component.first);
2660 if (det)
2661 m_instrument->removeDetector(det);
2662 }
2663 }
2664 }
2665}
2666
2684void InstrumentDefinitionParser::adjust(Poco::XML::Element *pElem, const std::map<std::string, bool> &isTypeAssembly,
2685 std::map<std::string, Poco::XML::Element *> &getTypeElement) {
2687 // check if pElem is an element with tag name 'type'
2688 if (pElem->tagName() != "type")
2689 throw Exception::InstrumentDefinitionError("Argument to function adjust() "
2690 "must be a pointer to an XML "
2691 "element with tag name type.");
2692
2693 // check that there is a <combine-components-into-one-shape> element in type
2694 Poco::AutoPtr<NodeList> pNLccioh = pElem->getElementsByTagName("combine-components-into-one-shape");
2695 if (pNLccioh->length() == 0) {
2696 throw Exception::InstrumentDefinitionError(std::string("Argument to function adjust() must be a pointer to an XML "
2697 "element with tag name type,") +
2698 " which contain a <combine-components-into-one-shape> element.");
2699 }
2700
2701 // check that there is a <algebra> element in type
2702 Poco::AutoPtr<NodeList> pNLalg = pElem->getElementsByTagName("algebra");
2703 if (pNLalg->length() == 0) {
2704 throw Exception::InstrumentDefinitionError(std::string("An <algebra> element must be part of a <type>, which") +
2705 " includes a <combine-components-into-one-shape> element. See "
2706 "docs.mantidproject.org/concepts/InstrumentDefinitionFile.");
2707 }
2708
2709 // check that there is a <location> element in type
2710 Poco::AutoPtr<NodeList> pNL = pElem->getElementsByTagName("location");
2711 unsigned long numLocation = pNL->length();
2712 if (numLocation == 0) {
2713 throw Exception::InstrumentDefinitionError(std::string("At least one <location> element must be part of a "
2714 "<type>, which") +
2715 " includes a <combine-components-into-one-shape> element. See "
2716 "docs.mantidproject.org/concepts/InstrumentDefinitionFile.");
2717 }
2718
2719 // check if a <translate-rotate-combined-shape-to> is defined
2720 Poco::AutoPtr<NodeList> pNL_TransRot = pElem->getElementsByTagName("translate-rotate-combined-shape-to");
2721 const Element *pTransRot = nullptr;
2722 if (pNL_TransRot->length() == 1) {
2723 pTransRot = static_cast<Element *>(pNL_TransRot->item(0));
2724 }
2725
2726 // to convert all <component>'s in type into <cuboid> elements, which are
2727 // added
2728 // to pElem, and these <component>'s are deleted after loop
2729
2730 std::unordered_set<Element *> allComponentInType; // used to hold <component>'s found
2731 std::vector<std::string> allLocationName; // used to check if loc names unique
2732 for (unsigned long i = 0; i < numLocation; i++) {
2733 auto *pLoc = static_cast<Element *>(pNL->item(i));
2734
2735 // The location element is required to be a child of a component element.
2736 // Get this component element
2737 Element *pCompElem = InstrumentDefinitionParser::getParentComponent(pLoc);
2738
2739 // get the name given to the <location> element in focus
2740 // note these names are required to be unique for the purpose of
2741 // constructing the <algebra>
2742 std::string locationElementName = pLoc->getAttribute("name");
2743 if (std::find(allLocationName.begin(), allLocationName.end(), locationElementName) == allLocationName.end())
2744 allLocationName.emplace_back(locationElementName);
2745 else
2747 std::string("Names in a <type> element containing ") +
2748 "a <combine-components-into-one-shape> element must be unique. " + "Here error is that " +
2749 locationElementName +
2750 " appears at least twice. See docs.mantidproject.org/concepts/InstrumentDefinitionFile.");
2751
2752 // create dummy component to hold coord. sys. of cuboid
2753 auto baseCoor = std::make_unique<CompAssembly>("base"); // dummy assembly used to get to end assembly if nested
2754 ICompAssembly *endComponent = nullptr; // end assembly, its purpose is to
2755 // hold the shape coordinate system
2756 // get shape coordinate system, returned as endComponent, as defined by
2757 // pLoc
2758 // and nested <location> elements
2759 // of pLoc
2760 std::string shapeTypeName = getShapeCoorSysComp(baseCoor.get(), pLoc, getTypeElement, endComponent);
2761
2762 // translate and rotate cuboid according to shape coordinate system in
2763 // endComponent
2764 std::string cuboidStr = translateRotateXMLcuboid(endComponent, getTypeElement[shapeTypeName], locationElementName);
2765
2766 // if <translate-rotate-combined-shape-to> is specified
2767 if (pTransRot) {
2768 baseCoor = std::make_unique<CompAssembly>("base");
2769
2770 setLocation(baseCoor.get(), pTransRot, m_angleConvertConst);
2771
2772 // Translate and rotate shape xml string according to
2773 // <translate-rotate-combined-shape-to>
2774 cuboidStr = translateRotateXMLcuboid(baseCoor.get(), cuboidStr, locationElementName);
2775 }
2776
2777 DOMParser pParser;
2778 Poco::AutoPtr<Document> pDoc;
2779 try {
2780 pDoc = pParser.parseString(cuboidStr);
2781 } catch (...) {
2782 throw Exception::InstrumentDefinitionError(std::string("Unable to parse XML string ") + cuboidStr);
2783 }
2784 // Get pointer to root element and add this element to pElem
2785 Element *pCuboid = pDoc->documentElement();
2786 Poco::AutoPtr<Node> fisse = (pElem->ownerDocument())->importNode(pCuboid, true);
2787 pElem->appendChild(fisse);
2788
2789 allComponentInType.insert(pCompElem);
2790 }
2791
2792 // delete all <component> found in pElem
2793 for (const auto &component : allComponentInType)
2794 pElem->removeChild(component);
2795}
2796
2806 const Poco::XML::Element *cuboidEle,
2807 const std::string &cuboidName) {
2808 Element *pElem_lfb = getShapeElement(cuboidEle, "left-front-bottom-point");
2809 Element *pElem_lft = getShapeElement(cuboidEle, "left-front-top-point");
2810 Element *pElem_lbb = getShapeElement(cuboidEle, "left-back-bottom-point");
2811 Element *pElem_rfb = getShapeElement(cuboidEle, "right-front-bottom-point");
2812
2813 V3D lfb = parsePosition(pElem_lfb); // left front bottom
2814 V3D lft = parsePosition(pElem_lft); // left front top
2815 V3D lbb = parsePosition(pElem_lbb); // left back bottom
2816 V3D rfb = parsePosition(pElem_rfb); // right front bottom
2817
2818 // translate and rotate cuboid according to coord. sys. of comp
2819 V3D p_lfb = getAbsolutPositionInCompCoorSys(comp, lfb);
2820 V3D p_lft = getAbsolutPositionInCompCoorSys(comp, lft);
2821 V3D p_lbb = getAbsolutPositionInCompCoorSys(comp, lbb);
2822 V3D p_rfb = getAbsolutPositionInCompCoorSys(comp, rfb);
2823
2824 // create output cuboid XML string
2825 std::ostringstream obj_str;
2826
2827 obj_str << "<cuboid id=\"" << cuboidName << "\">";
2828 obj_str << "<left-front-bottom-point ";
2829 obj_str << "x=\"" << p_lfb.X();
2830 obj_str << "\" y=\"" << p_lfb.Y();
2831 obj_str << "\" z=\"" << p_lfb.Z();
2832 obj_str << "\" />";
2833 obj_str << "<left-front-top-point ";
2834 obj_str << "x=\"" << p_lft.X();
2835 obj_str << "\" y=\"" << p_lft.Y();
2836 obj_str << "\" z=\"" << p_lft.Z();
2837 obj_str << "\" />";
2838 obj_str << "<left-back-bottom-point ";
2839 obj_str << "x=\"" << p_lbb.X();
2840 obj_str << "\" y=\"" << p_lbb.Y();
2841 obj_str << "\" z=\"" << p_lbb.Z();
2842 obj_str << "\" />";
2843 obj_str << "<right-front-bottom-point ";
2844 obj_str << "x=\"" << p_rfb.X();
2845 obj_str << "\" y=\"" << p_rfb.Y();
2846 obj_str << "\" z=\"" << p_rfb.Z();
2847 obj_str << "\" />";
2848 obj_str << "</cuboid>";
2849
2850 return obj_str.str();
2851}
2852
2859 Component *dummyComp = new Component("dummy", comp);
2860 comp->add(dummyComp);
2861
2862 dummyComp->setPos(pos); // set pos relative to comp coord. sys.
2863
2864 V3D retVal = dummyComp->getPos(); // get absolute position
2865
2866 return retVal;
2867}
2868
2877std::string InstrumentDefinitionParser::translateRotateXMLcuboid(ICompAssembly *comp, const std::string &cuboidXML,
2878 const std::string &cuboidName) {
2879 DOMParser pParser;
2880 Poco::AutoPtr<Document> pDoc;
2881 try {
2882 pDoc = pParser.parseString(cuboidXML);
2883 } catch (...) {
2884 throw Exception::InstrumentDefinitionError(std::string("Unable to parse XML string ") + cuboidXML);
2885 }
2886
2887 Element *pCuboid = pDoc->documentElement();
2888
2889 std::string retVal = translateRotateXMLcuboid(comp, pCuboid, cuboidName);
2890
2891 return retVal;
2892}
2893
2901Poco::AutoPtr<Poco::XML::Document>
2903 // Number of <location> this <locations> element is shorthand for
2904 size_t nElements(0);
2905 if (pElem->hasAttribute("n-elements")) {
2906 auto n = boost::lexical_cast<int>(Strings::strip(pElem->getAttribute("n-elements")));
2907
2908 if (n <= 0) {
2909 throw Exception::InstrumentDefinitionError("n-elements must be positive");
2910 } else {
2911 nElements = static_cast<size_t>(n);
2912 }
2913 } else {
2914 throw Exception::InstrumentDefinitionError("When using <locations> n-elements attribute is required. See "
2915 "docs.mantidproject.org/concepts/InstrumentDefinitionFile.");
2916 }
2917
2918 std::string name;
2919 if (pElem->hasAttribute("name")) {
2920 name = pElem->getAttribute("name");
2921 }
2922
2923 int nameCountStart(0);
2924 if (pElem->hasAttribute("name-count-start")) {
2925 nameCountStart = boost::lexical_cast<int>(Strings::strip(pElem->getAttribute("name-count-start")));
2926 }
2927
2928 int nameCountIncrement(1);
2929 if (pElem->hasAttribute("name-count-increment")) {
2930 nameCountIncrement = boost::lexical_cast<int>(Strings::strip(pElem->getAttribute("name-count-increment")));
2931
2932 if (nameCountIncrement <= 0)
2933 throw Exception::InstrumentDefinitionError("name-count-increment must be greater than zero.");
2934 }
2935
2936 // A list of numeric attributes which are allowed to have corresponding -end
2937 std::set<std::string> rangeAttrs = {"x", "y", "z", "r", "t", "p", "rot"};
2938
2939 // Numeric attributes related to rotation. Doesn't make sense to have -end
2940 // for
2941 // those
2942 std::set<std::string> rotAttrs = {"axis-x", "axis-y", "axis-z"};
2943
2944 // A set of all numeric attributes for convenience
2945 std::set<std::string> allAttrs;
2946 allAttrs.insert(rangeAttrs.begin(), rangeAttrs.end());
2947 allAttrs.insert(rotAttrs.begin(), rotAttrs.end());
2948
2949 // Attribute values as read from <locations>. If the attribute doesn't have
2950 // a
2951 // value here, it
2952 // means that it wasn't set
2953 std::map<std::string, double> attrValues;
2954
2955 // Read all the set attribute values
2956 for (const auto &attr : allAttrs) {
2957 if (pElem->hasAttribute(attr)) {
2958 attrValues[attr] = boost::lexical_cast<double>(Strings::strip(pElem->getAttribute(attr)));
2959 }
2960 }
2961
2962 // Range attribute steps
2963 std::map<std::string, double> rangeAttrSteps;
2964
2965 // Find *-end for range attributes and calculate steps
2966 for (const auto &rangeAttr : rangeAttrs) {
2967 std::string endAttr = rangeAttr + "-end";
2968 if (pElem->hasAttribute(endAttr)) {
2969 if (attrValues.find(rangeAttr) == attrValues.end()) {
2970 throw Exception::InstrumentDefinitionError("*-end attribute without corresponding * attribute.");
2971 }
2972
2973 double from = attrValues[rangeAttr];
2974 auto to = boost::lexical_cast<double>(Strings::strip(pElem->getAttribute(endAttr)));
2975
2976 rangeAttrSteps[rangeAttr] = (to - from) / (static_cast<double>(nElements) - 1);
2977 }
2978 }
2979
2980 Poco::AutoPtr<Document> pDoc = new Document;
2981 Poco::AutoPtr<Element> pRoot = pDoc->createElement("expansion-of-locations-element");
2982 pDoc->appendChild(pRoot);
2983
2984 for (size_t i = 0; i < nElements; ++i) {
2985 Poco::AutoPtr<Element> pLoc = pDoc->createElement("location");
2986
2987 if (!name.empty()) {
2988 // Add name with appropriate numeric postfix
2989 pLoc->setAttribute("name", name + std::to_string(nameCountStart + (i * nameCountIncrement)));
2990 }
2991
2992 // Copy values of all the attributes set
2993 for (auto &attrValue : attrValues) {
2994 pLoc->setAttribute(attrValue.first, boost::lexical_cast<std::string>(attrValue.second));
2995
2996 // If attribute has a step, increase the value by the step
2997 if (rangeAttrSteps.find(attrValue.first) != rangeAttrSteps.end()) {
2998 attrValue.second += rangeAttrSteps[attrValue.first];
2999 }
3000 }
3001
3002 pRoot->appendChild(pLoc);
3003 }
3004
3005 return pDoc;
3006}
3007
3014 std::string retVal;
3015 std::string filename = getMangledName();
3016 if (!filename.empty()) {
3017 std::filesystem::path path =
3018 std::filesystem::path(ConfigService::Instance().getVTPFileDirectory()) / (filename + ".vtp");
3019 retVal = path.string();
3020 }
3021 return retVal;
3022}
3023
3034Poco::XML::Element *InstrumentDefinitionParser::getShapeElement(const Poco::XML::Element *pElem,
3035 const std::string &name) {
3036 // check if this shape element contain an element with name specified by the
3037 // 2nd function argument
3038 Poco::AutoPtr<NodeList> pNL = pElem->getElementsByTagName(name);
3039 if (pNL->length() != 1) {
3040 throw std::invalid_argument("XML element: <" + pElem->tagName() +
3041 "> must contain exactly one sub-element with name: <" + name + ">.");
3042 }
3043 auto *retVal = static_cast<Element *>(pNL->item(0));
3044 return retVal;
3045}
3046
3053 V3D retVal;
3054
3055 if (pElem->hasAttribute("R") || pElem->hasAttribute("theta") || pElem->hasAttribute("phi")) {
3056 double R = attrToDouble(pElem, "R");
3057 double theta = attrToDouble(pElem, "theta");
3058 double phi = attrToDouble(pElem, "phi");
3059
3060 retVal.spherical(R, theta, phi);
3061 } else if (pElem->hasAttribute("r") || pElem->hasAttribute("t") || pElem->hasAttribute("p"))
3062 // This is alternative way a user may specify spherical coordinates
3063 // which may be preferred in the long run to the more verbose of
3064 // using R, theta and phi.
3065 {
3066 double R = attrToDouble(pElem, "r");
3067 double theta = attrToDouble(pElem, "t");
3068 double phi = attrToDouble(pElem, "p");
3069
3070 retVal.spherical(R, theta, phi);
3071 } else {
3072
3073 double x = attrToDouble(pElem, "x");
3074 double y = attrToDouble(pElem, "y");
3075 double z = attrToDouble(pElem, "z");
3076
3077 retVal(x, y, z);
3078 }
3079
3080 return retVal;
3081}
3082
3083//-----------------------------------------------------------------------------------------------------------------------
3102 const Poco::XML::Element *pLocElem,
3103 std::map<std::string, Poco::XML::Element *> &getTypeElement,
3104 Geometry::ICompAssembly *&endAssembly) {
3105 // The location element is required to be a child of a component element.
3106 // Get
3107 // this component element
3108 Element *pCompElem = InstrumentDefinitionParser::getParentComponent(pLocElem);
3109
3110 // Create the assembly that will be appended into the parent.
3112
3113 // The newly added component is required to have a type. Find out what this
3114 // type is and find all the location elements of this type. Finally loop
3115 // over
3116 // these
3117 // location elements
3118
3119 Element *pType = getTypeElement[pCompElem->getAttribute("type")];
3120
3122 endAssembly = ass;
3123
3124 // set location for this newly added comp
3125 setLocation(ass, pLocElem, m_angleConvertConst);
3126
3127 Poco::AutoPtr<NodeList> pNL = pType->getElementsByTagName("location");
3128 if (pNL->length() == 0) {
3129 return pType->getAttribute("name");
3130 } else if (pNL->length() == 1) {
3131 const auto *pElem = static_cast<Element *>(pNL->item(0));
3132 return getShapeCoorSysComp(ass, pElem, getTypeElement, endAssembly);
3133 } else {
3135 std::string("When using <combine-components-into-one-shape> ") +
3136 " the containing component elements are not allowed to contain "
3137 "multiple nested components. See docs.mantidproject.org/concepts/InstrumentDefinitionFile.");
3138 }
3139}
3140
3141} // 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.
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.
bool m_mixedNeutronicPositions
True if the IDF has <indirect-neutronic-positions-mixed/> tag set, allowing detectors without <neutro...
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.
bool applyCache(const IDFObject_const_sptr &cacheToApply)
Reads from a cache 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.
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.
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:38
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:35
constexpr double X() const noexcept
Get x.
Definition V3D.h:239
constexpr V3D cross_prod(const V3D &v) const noexcept
Cross product (this * argument)
Definition V3D.h:285
constexpr double Y() const noexcept
Get y.
Definition V3D.h:240
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:270
constexpr double Z() const noexcept
Get z.
Definition V3D.h:241
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:40
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:420
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
Definition V3D.h:353
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...