29#include <boost/algorithm/string.hpp>
30#include <boost/math/special_functions/round.hpp>
31#include <nexus/napi.h>
37using namespace Geometry;
39using namespace Kernel;
41using Types::Core::DateAndTime;
46constexpr size_t D20_NUMBER_PIXELS = 1600;
48constexpr size_t D20_NUMBER_DEAD_PIXELS = 32;
51constexpr size_t NUMBER_MONITORS = 1;
53constexpr double D20_PIXEL_SIZE = 0.1;
55constexpr double RAD_TO_DEG = 180. / M_PI;
57constexpr double WAVE_TO_E = 81.8;
59constexpr size_t D2B_NUMBER_PIXELS_IN_TUBES = 128;
71 if ((descriptor.pathExists(
"/entry0/instrument/2theta") && !descriptor.pathExists(
"/entry0/instrument/Tx")) ||
72 descriptor.pathExists(
"/entry0/instrument/Canne") ||
73 (descriptor.pathExists(
"/entry0/data_scan") && descriptor.pathExists(
"/entry0/experiment_identifier") &&
74 descriptor.pathExists(
"/entry0/instrument/Detector"))) {
103 "File path of the data file to load");
105 "The output workspace.");
106 std::vector<std::string> calibrationOptions{
"Auto",
"Raw",
"Calibrated"};
107 declareProperty(
"DataType",
"Auto", std::make_shared<StringListValidator>(calibrationOptions),
108 "Select the type of data, with or without calibration "
109 "already applied. If Auto then the calibrated data is "
110 "loaded if available, otherwise the raw data is loaded.");
111 declareProperty(
"TwoThetaOffset", 0.0,
"2 theta offset for D1B data, in degrees.");
113 "Apply vertical and horizontal alignment of tubes as defined in IPF");
115 "Whether to convert the spectrum axis to 2theta and "
116 "transpose (for 1D detector and no-scan configuration)");
120 std::map<std::string, std::string> issues;
122 issues[
"DataType"] =
"Calibrated data requested, but only raw data exists "
123 "in this NeXus file.";
138 progress.report(
"Loading the scanned variables");
141 progress.report(
"Loading the detector scan data");
144 progress.report(
"Loading the metadata");
147 progress.report(
"Setting additional sample logs");
171 if (dataType !=
"Raw" && hasCalibratedData) {
175 std::string dataName;
176 if (dataType ==
"Raw" && hasCalibratedData)
177 dataName =
"data_scan/detector_data/raw_data";
179 dataName =
"data_scan/detector_data/data";
190 NXInt scanned = firstEntry.
openNXInt(
"data_scan/scanned_variables/variables_names/scanned");
194 NXInt axis = firstEntry.
openNXInt(
"data_scan/scanned_variables/variables_names/axis");
198 double twoThetaValue = 0;
201 g_log.
notice(
"A 2theta offset angle is necessary for D1B data.");
207 std::string twoThetaPath =
"instrument/2theta/value";
210 twoThetaValue = double(twoTheta0[0]);
221 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
222 m_scanVar[i].setAxis(axis[
static_cast<int>(i)]);
223 m_scanVar[i].setScanned(scanned[
static_cast<int>(i)]);
230 std::string start_time = firstEntry.
getString(
"start_time");
254 mutableRun.addProperty(
"Facility", std::string(
"ILL"));
258 NXstatus nxStat = NXopen(
m_filename.c_str(), NXACC_READ, &nxHandle);
260 if (nxStat != NX_ERROR) {
264 mutableRun.addProperty(
"run_list", mutableRun.getPropertyValueAsType<
int>(
"run_number"));
266 if (mutableRun.hasProperty(
"Detector.calibration_file")) {
268 mutableRun.getProperty(
"Detector.calibration_file")->setValue(
"none");
270 mutableRun.addProperty(
"Detector.calibration_file", std::string(
"none"));
292 m_outWorkspace->mutableRun().addProperty(
"start_time", start_time);
303 const size_t nBins = 1;
304 const bool isPointData =
true;
307 const auto &instrument = instrumentWorkspace->getInstrument();
308 auto ¶ms = instrumentWorkspace->instrumentParameters();
312 double refR, refTheta, refPhi;
313 referenceComponentPosition.getSpherical(refR, refTheta, refPhi);
317 auto &compInfo = instrumentWorkspace->mutableComponentInfo();
320 const auto detCompIndex = compInfo.indexOf(detectors->getComponentID());
321 const auto tubes = compInfo.children(detCompIndex);
322 const size_t nTubes = tubes.size();
324 const auto tube1CompIndex = compInfo.indexOf(tube1->getComponentID());
325 const auto pixels = compInfo.children(tube1CompIndex);
326 const size_t nPixels = pixels.size();
330 pixel->getBoundingBox(bb);
333 const auto tubeAnglesStr = params.getString(
"D2B",
"tube_angles");
334 if (!tubeAnglesStr.empty() && doAlign) {
335 std::vector<std::string> tubeAngles;
336 boost::split(tubeAngles, tubeAnglesStr[0], boost::is_any_of(
","));
337 const double ref = -refTheta;
338 for (
size_t i = 1; i <= nTubes; ++i) {
341 double r, theta, phi;
342 V3D oldPos = component->getPos();
345 const double angle = std::stod(tubeAngles[i - 1]);
346 const double finalAngle =
fabs(ref - angle);
347 g_log.
debug() <<
"Rotating " << compName <<
"to " << finalAngle <<
"rad\n";
349 const auto componentIndex = compInfo.indexOf(component->getComponentID());
350 compInfo.setPosition(componentIndex, newPos);
354 const auto tubeCentersStr = params.getString(
"D2B",
"tube_centers");
355 if (!tubeCentersStr.empty() && doAlign) {
356 std::vector<std::string> tubeCenters;
357 double maxYOffset = 0.;
358 boost::split(tubeCenters, tubeCentersStr[0], boost::is_any_of(
","));
359 for (
size_t i = 1; i <= nTubes; ++i) {
362 const double offset = std::stod(tubeCenters[i - 1]) - (double(nPixels) / 2 - 0.5);
364 V3D translation(0,
y, 0);
365 if (std::fabs(
y) > maxYOffset) {
366 maxYOffset = std::fabs(
y);
368 g_log.
debug() <<
"Moving " << compName <<
" to " <<
y <<
"\n";
369 V3D pos = component->getPos() + translation;
370 const auto componentIndex = compInfo.indexOf(component->getComponentID());
371 compInfo.setPosition(componentIndex, pos);
380 scanningWorkspaceBuilder.setTimeRanges(
m_startTime, timeDurations);
385 << (
m_startTime + std::accumulate(timeDurations.begin(), timeDurations.end(), 0.0)).toISO8601String()
394 auto rotationCentre =
V3D(0, 0, 0);
395 auto rotationAxis =
V3D(0, 1, 0);
396 scanningWorkspaceBuilder.setRelativeRotationsForScans(std::move(tubeAngles), rotationCentre, rotationAxis);
412 return instrumentWorkspace->getInstrument()->getComponentByName(
"tube_128")->getPos();
415 const auto &detInfo = instrumentWorkspace->detectorInfo();
416 const auto &indexOfFirstDet = detInfo.indexOf(1);
417 return detInfo.position(indexOfFirstDet);
434 double firstTubeRotationAngle = firstTubePosition.
angle(
V3D(0, 0, 1)) * RAD_TO_DEG;
442 firstTubeRotationAngle = -firstTubeRotationAngle;
443 std::transform(tubeRotations.begin(), tubeRotations.end(), tubeRotations.begin(),
444 [&](
double angle) { return (-angle); });
447 g_log.
debug() <<
"First tube rotation:" << firstTubeRotationAngle <<
"\n";
450 std::transform(tubeRotations.begin(), tubeRotations.end(), tubeRotations.begin(),
451 [&](
double angle) { return (angle - firstTubeRotationAngle); });
453 g_log.
debug() <<
"Instrument rotations to be applied : " << tubeRotations.front() <<
" to " << tubeRotations.back()
465 std::vector<double> axis = {0.};
466 std::vector<double> monitor =
getMonitor(scan);
469 for (
size_t i = 0; i < NUMBER_MONITORS; ++i) {
482 const auto tubeNumber = (i - NUMBER_MONITORS) /
m_sizeDim2;
483 auto pixelInTubeNumber = (i - NUMBER_MONITORS) %
m_sizeDim2;
485 pixelInTubeNumber = D2B_NUMBER_PIXELS_IN_TUBES - 1 - pixelInTubeNumber;
487 unsigned int y = data(
static_cast<int>(j),
static_cast<int>(tubeNumber),
static_cast<int>(pixelInTubeNumber));
506 const std::vector<double> axis =
getAxis(scan);
507 const std::vector<double> monitor =
getMonitor(scan);
509 size_t monitorIndex = 0;
510 size_t startIndex = NUMBER_MONITORS;
519 std::transform(monitor.begin(), monitor.end(),
m_outWorkspace->mutableE(monitorIndex).begin(),
520 [](
double e) { return sqrt(e); });
527 const auto tubeNumber = (i - startIndex) /
m_sizeDim2;
528 auto pixelInTubeNumber = (i - startIndex) %
m_sizeDim2;
530 pixelInTubeNumber = D2B_NUMBER_PIXELS_IN_TUBES - 1 - pixelInTubeNumber;
533 unsigned int y = data(
static_cast<int>(j),
static_cast<int>(tubeNumber),
static_cast<int>(pixelInTubeNumber));
555 Group entry0 = h5file.openGroup(
"entry0");
556 Group dataScan = entry0.openGroup(
"data_scan");
557 Group scanVar = dataScan.openGroup(
"scanned_variables");
558 Group varNames = scanVar.openGroup(
"variables_names");
564 for (
size_t i = 0; i < names.size(); ++i) {
582 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
583 if (!boost::starts_with(
m_scanVar[i].property,
"Monitor")) {
584 const std::string scanVarName = boost::algorithm::to_lower_copy(
m_scanVar[i].
name);
585 const std::string scanVarProp = boost::algorithm::to_lower_copy(
m_scanVar[i].property);
586 const std::string propName = scanVarName +
"." + scanVarProp;
588 mutableRun.addProperty(
"ScanVar", propName,
true);
590 auto property = std::make_unique<TimeSeriesProperty<double>>(propName);
592 property->addValue(absoluteTimes[j], scan(
static_cast<int>(i),
static_cast<int>(j)));
594 mutableRun.addLogData(std::move(property),
true);
610 const std::string &propertyName)
const {
611 std::vector<double> scannedVariable;
613 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
614 if (
m_scanVar[i].property == propertyName) {
616 scannedVariable.emplace_back(scan(
static_cast<int>(i),
static_cast<int>(j)));
622 if (scannedVariable.empty())
623 throw std::runtime_error(
"Can not load file because scanned variable with property name " + propertyName +
626 return scannedVariable;
638 std::vector<double> monitor = {0.};
639 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
645 throw std::runtime_error(
"Monitors not found in scanned variables");
655 std::vector<double> axis = {0.};
657 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
673 std::vector<double> timeDurations;
674 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
675 if (boost::starts_with(
m_scanVar[i].property,
"Time")) {
680 return timeDurations;
689 std::vector<DateAndTime> times;
692 times.emplace_back(time);
693 size_t timeIndex = 1;
695 time += durations[timeIndex - 1];
696 times.emplace_back(time);
712 if (scanVar.scanned == 1) {
714 if (scanVar.name ==
"2theta") {
731 throw std::runtime_error(
"Instrument " +
m_instName +
" not supported.");
742 size_t activePixels = D20_NUMBER_PIXELS - 2 * D20_NUMBER_DEAD_PIXELS;
746 throw std::runtime_error(
"Unknown resolution mode for instrument " +
m_instName);
755 <<
" actual detectors.\n";
766 loadInst->setProperty(
"RewriteSpectraMap",
OptionalBool(
true));
778 loadInst->setPropertyValue(
"InstrumentName",
m_instName);
780 auto &run = ws->mutableRun();
783 run.addProperty(
"start_time", start_time);
786 loadInst->setProperty(
"RewriteSpectraMap",
OptionalBool(
true));
788 return loadInst->getProperty(
"Workspace");
798 double twoTheta0Actual = twoTheta0Read;
803 g_log.
debug() <<
"Setting 2theta0 to " << twoTheta0Actual;
805 const auto componentIndex = componentInfo.indexOf(component->getComponentID());
806 componentInfo.setRotation(componentIndex,
rotation);
818 Poco::Path file(instName +
"_Definition.xml");
819 Poco::Path fullPath(directory, file);
820 return fullPath.toString();
828 std::string scanTypeStr =
"NoScan";
830 scanTypeStr =
"DetectorScan";
832 scanTypeStr =
"OtherScan";
836 std::string resModeStr =
"Nominal";
853 throw std::runtime_error(
"Neither wavelength nor Monochromator.ei are not specified in the loaded file.");
876 return descriptor.
pathExists(
"/entry0/data_scan/detector_data/raw_data");
883 m_offsetTheta =
static_cast<double>(D20_NUMBER_DEAD_PIXELS) * D20_PIXEL_SIZE -
893 extractor->setProperty(
"StartWorkspaceIndex", 1);
894 extractor->setProperty(
"OutputWorkspace",
"__unused");
895 extractor->execute();
898 converter->setProperty(
"InputWorkspace", det);
899 converter->setProperty(
"OutputWorkspace",
"__unused");
900 converter->setProperty(
"Target",
"SignedTheta");
901 converter->execute();
904 transposer->setProperty(
"InputWorkspace", converted);
905 transposer->setProperty(
"OutputWorkspace",
"__unused");
906 transposer->execute();
const std::vector< double > * lambda
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
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.
@ 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 fillMovingInstrumentScan(const NeXus::NXUInt &, const NeXus::NXDouble &)
Fills the counts for the instrument with moving detectors.
bool containsCalibratedData(const std::string &filename) const
Returns true if the file contains calibrated data.
void moveTwoThetaZero(double)
Rotates the detector to the 2theta0 read from the file.
API::MatrixWorkspace_sptr m_outWorkspace
output workspace
void fillStaticInstrumentScan(const NeXus::NXUInt &, const NeXus::NXDouble &, const double &)
Fills the loaded data to the workspace when the detector is not moving during the run,...
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...
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.
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.
API::MatrixWorkspace_sptr loadEmptyInstrument(const std::string &start_time)
Runs LoadInstrument and returns a workspace with the instrument, to be used in the ScanningWorkspaceB...
std::string getInstrumentFilePath(const std::string &) const
Makes up the full path of the relevant IDF dependent on resolution mode.
bool m_useCalibratedData
whether to use the calibrated data in the nexus (D2B only)
const std::string category() const override
Algorithm's category for identification.
double m_pixelHeight
height of the pixel in D2B
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.
void fillDataScanMetaData(const NeXus::NXDouble &)
Creates time series sample logs for the scanned variables.
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.
std::vector< double > getAxis(const NeXus::NXDouble &) const
Returns the x-axis.
void initMovingWorkspace(const NeXus::NXDouble &scan, const std::string &start_time)
Use the ScanningWorkspaceBuilder to create a time indexed workspace.
void exec() override
Executes the algorithm.
Types::Core::DateAndTime m_startTime
start time of acquisition
std::vector< double > getMonitor(const NeXus::NXDouble &) const
Returns the monitor spectrum.
void convertAxisAndTranspose()
the 2theta offset for D20 to account for dead pixels
void loadStaticInstrument()
Runs LoadInstrument as child to link the non-moving instrument to workspace.
void loadScanVars()
Loads the scanned_variables/variables_names block.
void loadMetaData()
Dumps the metadata from the whole file to SampleLogs.
std::vector< double > getDurations(const NeXus::NXDouble &) const
Returns the durations in seconds for each scan point.
void initStaticWorkspace(const std::string &start_time)
Initializes the output workspace based on the resolved instrument, scan points, and scan type.
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.
Defines a wrapper around a file whose internal structure can be accessed using the NeXus API.
bool pathExists(const std::string &path) const
Query if a path exists.
OptionalBool : Tri-state bool.
The concrete, templated class for properties.
virtual bool isDefault() const =0
Overriden function that returns if property has the same value that it was initialised with,...
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.
NXInt openNXInt(const std::string &name) const
Creates and opens an integer dataset.
void close()
Close this class.
NXDataSetTyped< T > openNXDataSet(const std::string &name) const
Templated method for creating datasets.
NXFloat openNXFloat(const std::string &name) const
Creates and opens a float dataset.
std::string getString(const std::string &name) const
Returns a string.
Templated class implementation of NXDataSet.
void load(const int blocksize=1, int i=-1, int j=-1, int k=-1, int l=-1) override
Implementation of the virtual NXDataSet::load(...) method.
int dim2() const
Returns the number of elements along the third dimension.
int dim0() const
Returns the number of elements along the first dimension.
int dim1() const
Returns the number of elements along the second dimension.
Implements NXdata Nexus class.
NXDouble openDoubleData()
Opens data of double type.
Implements NXentry Nexus class.
NXData openNXData(const std::string &name) const
Opens a NXData.
Implements NXroot Nexus class.
NXEntry openFirstEntry()
Open the first NXentry in the file.
Kernel::Logger g_log("ExperimentInfo")
static logger object
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
MANTID_DATAHANDLING_DLL std::vector< std::string > readStringVector(H5::Group &, const std::string &)
void addNexusFieldsToWsRun(NXhandle nxfileID, API::Run &runDetails, const std::string &entryName="", bool useFullPath=false)
Add properties from a nexus file to the workspace run.
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...
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.
std::enable_if< std::is_pointer< Arg >::value, bool >::type threadSafe(Arg workspace)
Thread-safety check Checks the workspace to ensure it is suitable for multithreaded access.
std::string to_string(const wide_integer< Bits, Signed > &n)
@ Input
An input workspace.
@ Output
An output workspace.