27#include "MantidNexus/NexusFile.h"
30#include <boost/algorithm/string.hpp>
31#include <boost/math/special_functions/round.hpp>
38using namespace Geometry;
40using namespace Kernel;
42using Types::Core::DateAndTime;
47constexpr size_t D20_NUMBER_PIXELS = 1600;
49constexpr size_t D20_NUMBER_DEAD_PIXELS = 32;
52constexpr size_t NUMBER_MONITORS = 1;
54constexpr double D20_PIXEL_SIZE = 0.1;
56constexpr double RAD_TO_DEG = 180. / M_PI;
58constexpr double WAVE_TO_E = 81.8;
70 if ((descriptor.isEntry(
"/entry0/instrument/2theta") && !descriptor.isEntry(
"/entry0/instrument/Tx")) ||
71 descriptor.isEntry(
"/entry0/instrument/Canne")) {
100 "File path of the data file to load");
102 "The output workspace.");
103 declareProperty(
"TwoThetaOffset", 0.0,
"2 theta offset for D1B data, in degrees.");
105 "Apply vertical and horizontal alignment of tubes as defined in IPF");
107 "Whether to convert the spectrum axis to 2theta and "
108 "transpose (for 1D detector and no-scan configuration)");
112 std::map<std::string, std::string> issues;
126 progress.report(
"Loading the scanned variables");
129 progress.report(
"Loading the detector scan data");
132 progress.report(
"Loading the metadata");
135 progress.report(
"Setting additional sample logs");
155 std::string dataName =
"data_scan/detector_data/data";
165 NXInt scanned = firstEntry.
openNXInt(
"data_scan/scanned_variables/variables_names/scanned");
169 NXInt axis = firstEntry.
openNXInt(
"data_scan/scanned_variables/variables_names/axis");
173 double twoThetaValue = 0;
176 g_log.
notice(
"A 2theta offset angle is necessary for D1B data.");
182 std::string twoThetaPath =
"instrument/2theta/value";
185 twoThetaValue = double(twoTheta0[0]);
189 m_sizeDim1 =
static_cast<size_t>(data.dim1());
190 m_sizeDim2 =
static_cast<size_t>(data.dim2());
196 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
197 m_scanVar[i].setAxis(axis[
static_cast<int>(i)]);
198 m_scanVar[i].setScanned(scanned[
static_cast<int>(i)]);
225 mutableRun.addProperty(
"Facility", std::string(
"ILL"));
232 g_log.
debug() <<
"Failed to open nexus file \"" <<
m_filename <<
"\" in read mode: " << e.what() <<
"\n";
235 if (mutableRun.hasProperty(
"run_number"))
236 mutableRun.addProperty(
"run_list", mutableRun.getPropertyValueAsType<
int>(
"run_number"));
238 throw std::runtime_error(
"Failed to find run_number in Run object");
240 if (!mutableRun.hasProperty(
"Detector.calibration_file"))
241 mutableRun.addProperty(
"Detector.calibration_file", std::string(
"none"));
271 const size_t nBins = 1;
272 const bool isPointData =
true;
275 auto &run = instrumentWorkspace->mutableRun();
277 run.addProperty(
"start_time",
m_startTime.toISO8601String());
281 const auto &instrument = instrumentWorkspace->getInstrument();
282 auto ¶ms = instrumentWorkspace->instrumentParameters();
286 double refR, refTheta, refPhi;
287 referenceComponentPosition.getSpherical(refR, refTheta, refPhi);
291 auto &compInfo = instrumentWorkspace->mutableComponentInfo();
294 const auto detCompIndex = compInfo.indexOf(detectors->getComponentID());
295 const auto tubes = compInfo.children(detCompIndex);
296 const size_t nTubes = tubes.size();
298 const auto tube1CompIndex = compInfo.indexOf(tube1->getComponentID());
299 const auto pixels = compInfo.children(tube1CompIndex);
300 const size_t nPixels = pixels.size();
304 pixel->getBoundingBox(bb);
307 const auto tubeAnglesStr = params.getString(
"D2B",
"tube_angles");
308 if (!tubeAnglesStr.empty() && doAlign) {
309 std::vector<std::string> tubeAngles;
310 boost::split(tubeAngles, tubeAnglesStr[0], boost::is_any_of(
","));
311 const double ref = -refTheta;
312 for (
size_t i = 1; i <= nTubes; ++i) {
315 double r, theta, phi;
316 V3D oldPos = component->getPos();
319 const double angle = std::stod(tubeAngles[i - 1]);
320 const double finalAngle =
fabs(ref - angle);
321 g_log.
debug() <<
"Rotating " << compName <<
"to " << finalAngle <<
"rad\n";
323 const auto componentIndex = compInfo.indexOf(component->getComponentID());
324 compInfo.setPosition(componentIndex, newPos);
328 const auto tubeCentersStr = params.getString(
"D2B",
"tube_centers");
329 if (!tubeCentersStr.empty() && doAlign) {
330 std::vector<std::string> tubeCenters;
331 double maxYOffset = 0.;
332 boost::split(tubeCenters, tubeCentersStr[0], boost::is_any_of(
","));
333 for (
size_t i = 1; i <= nTubes; ++i) {
336 const double offset = std::stod(tubeCenters[i - 1]) - (double(nPixels) / 2 - 0.5);
338 V3D translation(0,
y, 0);
339 if (std::fabs(
y) > maxYOffset) {
340 maxYOffset = std::fabs(
y);
342 g_log.
debug() <<
"Moving " << compName <<
" to " <<
y <<
"\n";
343 V3D pos = component->getPos() + translation;
344 const auto componentIndex = compInfo.indexOf(component->getComponentID());
345 compInfo.setPosition(componentIndex, pos);
354 scanningWorkspaceBuilder.setTimeRanges(
m_startTime, timeDurations);
359 << (
m_startTime + std::accumulate(timeDurations.begin(), timeDurations.end(), 0.0)).toISO8601String()
368 auto rotationCentre =
V3D(0, 0, 0);
369 auto rotationAxis =
V3D(0, 1, 0);
370 scanningWorkspaceBuilder.setRelativeRotationsForScans(std::move(tubeAngles), rotationCentre, rotationAxis);
386 return instrumentWorkspace->getInstrument()->getComponentByName(
"tube_128")->getPos();
389 const auto &detInfo = instrumentWorkspace->detectorInfo();
390 const auto &indexOfFirstDet = detInfo.indexOf(1);
391 return detInfo.position(indexOfFirstDet);
408 double firstTubeRotationAngle = firstTubePosition.
angle(
V3D(0, 0, 1)) * RAD_TO_DEG;
416 firstTubeRotationAngle = -firstTubeRotationAngle;
417 std::transform(tubeRotations.begin(), tubeRotations.end(), tubeRotations.begin(),
418 [&](
double angle) { return (-angle); });
421 g_log.
debug() <<
"First tube rotation:" << firstTubeRotationAngle <<
"\n";
424 std::transform(tubeRotations.begin(), tubeRotations.end(), tubeRotations.begin(),
425 [&](
double angle) { return (angle - firstTubeRotationAngle); });
427 g_log.
debug() <<
"Instrument rotations to be applied : " << tubeRotations.front() <<
" to " << tubeRotations.back()
439 std::vector<double> axis = {0.};
440 std::vector<double> monitor =
getMonitor(scan);
443 for (
size_t i = 0; i < NUMBER_MONITORS; ++i) {
453 std::tuple<int, int, int> dimOrder{2, 1, 0};
454 std::set<int> acceptedIDs;
459 std::vector<int> customDetectorIDs;
475 const std::vector<double> axis =
getAxis(scan);
476 const std::vector<double> monitor =
getMonitor(scan);
478 std::size_t startIndex = NUMBER_MONITORS;
483 std::transform(monitor.begin(), monitor.end(),
m_outWorkspace->mutableE(0).begin(), [](
double e) { return sqrt(e); });
486 std::tuple<int, int, int> dimOrder{2, 1, 0};
487 std::set<detid_t> acceptedIDs;
490 acceptedIDs.insert(
static_cast<detid_t>(i));
508 Group entry0 = h5file.openGroup(
"entry0");
509 Group dataScan = entry0.openGroup(
"data_scan");
510 Group scanVar = dataScan.openGroup(
"scanned_variables");
511 Group varNames = scanVar.openGroup(
"variables_names");
517 for (
size_t i = 0; i < names.size(); ++i) {
535 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
536 if (!
m_scanVar[i].property.starts_with(
"Monitor")) {
537 const std::string scanVarName = boost::algorithm::to_lower_copy(
m_scanVar[i].
name);
538 const std::string scanVarProp = boost::algorithm::to_lower_copy(
m_scanVar[i].property);
539 const std::string propName = scanVarName +
"." + scanVarProp;
541 mutableRun.addProperty(
"ScanVar", propName,
true);
543 auto property = std::make_unique<TimeSeriesProperty<double>>(propName);
545 property->addValue(absoluteTimes[j], scan(
static_cast<int>(i),
static_cast<int>(j)));
547 mutableRun.addLogData(std::move(property),
true);
563 const std::string &propertyName)
const {
564 std::vector<double> scannedVariable;
566 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
567 if (
m_scanVar[i].property == propertyName) {
569 scannedVariable.emplace_back(scan(
static_cast<int>(i),
static_cast<int>(j)));
575 if (scannedVariable.empty())
576 throw std::runtime_error(
"Can not load file because scanned variable with property name " + propertyName +
579 return scannedVariable;
591 std::vector<double> monitor = {0.};
592 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
598 throw std::runtime_error(
"Monitors not found in scanned variables");
608 std::vector<double> axis = {0.};
610 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
626 std::vector<double> timeDurations;
627 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
628 if (
m_scanVar[i].property.starts_with(
"Time")) {
633 return timeDurations;
642 std::vector<DateAndTime> times;
645 times.emplace_back(time);
646 size_t timeIndex = 1;
648 time += durations[timeIndex - 1];
649 times.emplace_back(time);
665 if (scanVar.scanned == 1) {
667 if (scanVar.name ==
"2theta") {
684 throw std::runtime_error(
"Instrument " +
m_instName +
" not supported.");
695 size_t activePixels = D20_NUMBER_PIXELS - 2 * D20_NUMBER_DEAD_PIXELS;
699 throw std::runtime_error(
"Unknown resolution mode for instrument " +
m_instName);
708 <<
" actual detectors.\n";
719 double twoTheta0Actual = twoTheta0Read;
724 g_log.
debug() <<
"Setting 2theta0 to " << twoTheta0Actual;
726 const auto componentIndex = componentInfo.indexOf(component->getComponentID());
727 componentInfo.setRotation(componentIndex,
rotation);
738 std::filesystem::path directory(ConfigService::Instance().getInstrumentDirectory());
739 std::filesystem::path file(instName +
"_Definition.xml");
740 std::filesystem::path fullPath = std::filesystem::path(directory) / file;
741 return fullPath.string();
749 std::string scanTypeStr =
"NoScan";
751 scanTypeStr =
"DetectorScan";
753 scanTypeStr =
"OtherScan";
757 std::string resModeStr =
"Nominal";
774 throw std::runtime_error(
"Neither wavelength nor Monochromator.ei are not specified in the loaded file.");
790 m_offsetTheta =
static_cast<double>(D20_NUMBER_DEAD_PIXELS) * D20_PIXEL_SIZE -
800 extractor->setProperty(
"StartWorkspaceIndex", 1);
801 extractor->setProperty(
"OutputWorkspace",
"__unused");
802 extractor->execute();
805 converter->setProperty(
"InputWorkspace", det);
806 converter->setProperty(
"OutputWorkspace",
"__unused");
807 converter->setProperty(
"Target",
"SignedTheta");
808 converter->execute();
811 transposer->setProperty(
"InputWorkspace", converted);
812 transposer->setProperty(
"OutputWorkspace",
"__unused");
813 transposer->execute();
const std::vector< double > * lambda
std::map< DeltaEMode::Type, std::string > index
Mantid::Kernel::Quat(ComponentInfo::* rotation)(const size_t) const
#define DECLARE_NEXUS_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM macro wh...
void declareProperty(std::unique_ptr< Kernel::Property > p, const std::string &doc="") override
Add a property to the list of managed properties.
Kernel::Property * getPointerToProperty(const std::string &name) const override
Get a property by name.
std::string getPropertyValue(const std::string &name) const override
Get the value of a property as a string.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
virtual std::shared_ptr< Algorithm > createChildAlgorithm(const std::string &name, const double startProgress=-1., const double endProgress=-1., const bool enableLogging=true, const int &version=-1)
Create a Child Algorithm.
void progress(double p, const std::string &msg="", double estimatedTime=0.0, int progressPrecision=0)
Sends ProgressNotification.
bool isDefault(const std::string &name) const
@ Load
allowed here which will be passed to the algorithm
Defines an interface to an algorithm that loads a file so that it can take part in the automatic sele...
void addLogData(Kernel::Property *p)
Add a log entry.
bool hasProperty(const std::string &name) const
Does the property exist on the object.
HeldType getPropertyValueAsType(const std::string &name) const
Get the value of a property as the given TYPE.
double getLogAsSingleValue(const std::string &name, Kernel::Math::StatisticType statistic=Kernel::Math::Mean) const
Helper class for reporting progress from algorithms.
This class stores information regarding an experimental run as a series of log entries.
A property class for workspaces.
LoadILLDiffraction : Loads ILL diffraction nexus files.
std::vector< ScannedVariables > m_scanVar
holds the scan info
int version() const override
Algorithm's version for identification.
size_t m_sizeDim1
size of dim1, number of tubes (D2B) or the whole detector (D20)
size_t m_sizeDim2
size of dim2, number of pixels (1 for D20!)
size_t m_resolutionMode
resolution mode; 1:low, 2:nominal, 3:high
void moveTwoThetaZero(double)
Rotates the detector to the 2theta0 read from the file.
API::MatrixWorkspace_sptr m_outWorkspace
output workspace
void calculateRelativeRotations(std::vector< double > &instrumentAngles, const Kernel::V3D &firstTubePosition)
Convert from absolute rotation angle, around the sample, of tube 1, to a relative rotation angle arou...
std::map< std::string, std::string > validateInputs() override
Perform validation of ALL the input properties of the algorithm.
std::string m_instName
instrument name to load the IDF
size_t m_numberDetectorsRead
number of cells read from file
Kernel::V3D getReferenceComponentPosition(const API::MatrixWorkspace_sptr &instrumentWorkspace)
Get the position of the component in the workspace which corresponds to the angle stored in the scann...
void fillStaticInstrumentScan(const Nexus::NXInt &, const Nexus::NXDouble &, const double &)
Fills the loaded data to the workspace when the detector is not moving during the run,...
const std::string summary() const override
Algorithm's summary for use in the GUI and help.
std::vector< Types::Core::DateAndTime > getAbsoluteTimes(const Nexus::NXDouble &) const
Returns the vector of absolute times for each scan point.
std::string getInstrumentFilePath(const std::string &) const
Makes up the full path of the relevant IDF dependent on resolution mode.
const std::string category() const override
Algorithm's category for identification.
double m_pixelHeight
height of the pixel in D2B
void fillMovingInstrumentScan(const Nexus::NXInt &, const Nexus::NXDouble &)
Fills the counts for the instrument with moving detectors.
size_t m_numberDetectorsActual
number of cells actually active
ScanType m_scanType
NoScan, DetectorScan or OtherScan.
std::set< std::string > m_instNames
supported instruments
const std::string name() const override
Algorithms name for identification.
std::vector< double > getAxis(const Nexus::NXDouble &) const
Returns the x-axis.
std::vector< double > getScannedVaribleByPropertyName(const Nexus::NXDouble &scan, const std::string &propertyName) const
Gets a scanned variable based on its property type in the scanned_variables block.
void initStaticWorkspace()
Initializes the output workspace based on the resolved instrument, scan points, and scan type.
void initMovingWorkspace(const Nexus::NXDouble &scan)
Use the ScanningWorkspaceBuilder to create a time indexed workspace.
void computeThetaOffset()
Computes the 2theta offset of the decoder for D20.
void loadDataScan()
Loads the scanned detector data.
std::string m_filename
file name to load
void resolveInstrument()
Resolves the instrument based on instrument name and resolution mode.
LoadILLDiffraction()
Constructor.
void init() override
Initialize the algorithm's properties.
size_t m_numberScanPoints
number of scan points
double m_maxHeight
maximum absolute height of the D2B tubes
void setSampleLogs()
Adds some sample logs needed later by reduction.
void fillDataScanMetaData(const Nexus::NXDouble &)
Creates time series sample logs for the scanned variables.
std::vector< double > getMonitor(const Nexus::NXDouble &) const
Returns the monitor spectrum.
std::vector< double > getDurations(const Nexus::NXDouble &) const
Returns the durations in seconds for each scan point.
void exec() override
Executes the algorithm.
Types::Core::DateAndTime m_startTime
start time of acquisition
void convertAxisAndTranspose()
the 2theta offset for D20 to account for dead pixels
void loadScanVars()
Loads the scanned_variables/variables_names block.
void loadMetaData()
Dumps the metadata from the whole file to SampleLogs.
void resolveScanType()
Resolves the scan type.
ScanningWorkspaceBuilder : This is a helper class to make it easy to build a scanning workspace (a wo...
A simple structure that defines an axis-aligned cuboid shaped bounding box for a geometrical object.
double yMax() const
Return the maximum value of Y.
double yMin() const
Return the minimum value of Y.
The class Group represents a set of symmetry operations (or symmetry group).
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void debug(const std::string &msg)
Logs at debug level.
void notice(const std::string &msg)
Logs at notice level.
The concrete, templated class for properties.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
void spherical(const double R, const double theta, const double phi) noexcept
Sets the vector position based on spherical coordinates.
double angle(const V3D &) const
Angle between this and another vector.
void getSpherical(double &R, double &theta, double &phi) const noexcept
Return the vector's position in spherical coordinates.
Class that provides for a standard Nexus exception.
std::string getString(const std::string &name) const
Returns a string.
NXFloat openNXFloat(const std::string &name) const
Creates and opens a float dataset.
void close()
Close this class.
NXInt openNXInt(const std::string &name) const
Creates and opens an integer dataset.
NXDataSetTyped< T > openNXDataSet(const std::string &name) const
Templated method for creating datasets.
Templated class implementation of NXDataSet.
void load()
Read all of the datablock in.
dimsize_t dim2() const
Returns the number of elements along the third dimension.
dimsize_t dim1() const
Returns the number of elements along the second dimension.
Implements NXentry Nexus class.
Implements NXroot Nexus class.
NXEntry openFirstEntry()
Open the first NXentry in the file.
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
void fillMovingWorkspace(const API::MatrixWorkspace_sptr &, const Mantid::Nexus::NXInt &, const std::vector< double > &xAxis, int64_t initialSpectrum=0, const std::set< detid_t > &acceptedID=std::set< int >(), const std::vector< detid_t > &customID=std::vector< int >(), const std::tuple< short, short, short > &axisOrder=std::tuple< short, short, short >(0, 1, 2))
Fills workspace with histogram data from provided data structure.
void addNexusFieldsToWsRun(Nexus::File &filehandle, API::Run &runDetails, const std::string &entryName="", bool useFullAddress=false)
Add properties from a nexus file to the workspace run.
Nexus::NXDouble getDoubleDataset(const Nexus::NXEntry &, const std::string &)
Fetches NXDouble data from the requested group name in the entry provided.
void fillStaticWorkspace(const API::MatrixWorkspace_sptr &, const Mantid::Nexus::NXInt &, const std::vector< double > &xAxis, int64_t initialSpectrum=0, bool pointData=false, const std::vector< detid_t > &detectorIDs=std::vector< int >(), const std::set< detid_t > &acceptedID=std::set< int >(), const std::tuple< short, short, short > &axisOrder=std::tuple< short, short, short >(0, 1, 2))
Fills workspace with histogram data from provided data structure.
std::string dateTimeInIsoFormat(const std::string &)
Parses the date as formatted at the ILL: 29-Jun-12 11:27:26 and converts it to the ISO format used in...
void loadEmptyInstrument(const API::MatrixWorkspace_sptr &ws, const std::string &instrumentName, const std::string &instrumentAddress="")
Loads empty instrument of chosen name into a provided workspace.
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
std::shared_ptr< const Instrument > Instrument_const_sptr
Shared pointer to an const instrument object.
MANTID_NEXUS_DLL H5::FileAccPropList defaultFileAcc()
Default file access is H5F_CLOSE_STRONG.
MANTID_NEXUS_DLL std::vector< std::string > readStringVector(H5::Group &, const std::string &)
int32_t detid_t
Typedef for a detector ID.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
@ Output
An output workspace.