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")) {
99 "File path of the data file to load");
101 "The output workspace.");
102 declareProperty(
"TwoThetaOffset", 0.0,
"2 theta offset for D1B data, in degrees.");
104 "Apply vertical and horizontal alignment of tubes as defined in IPF");
106 "Whether to convert the spectrum axis to 2theta and "
107 "transpose (for 1D detector and no-scan configuration)");
111 std::map<std::string, std::string> issues;
125 progress.report(
"Loading the scanned variables");
128 progress.report(
"Loading the detector scan data");
131 progress.report(
"Loading the metadata");
134 progress.report(
"Setting additional sample logs");
154 std::string dataName =
"data_scan/detector_data/data";
164 NXInt scanned = firstEntry.
openNXInt(
"data_scan/scanned_variables/variables_names/scanned");
168 NXInt axis = firstEntry.
openNXInt(
"data_scan/scanned_variables/variables_names/axis");
172 double twoThetaValue = 0;
175 g_log.
notice(
"A 2theta offset angle is necessary for D1B data.");
181 std::string twoThetaPath =
"instrument/2theta/value";
184 twoThetaValue = double(twoTheta0[0]);
188 m_sizeDim1 =
static_cast<size_t>(data.dim1());
189 m_sizeDim2 =
static_cast<size_t>(data.dim2());
195 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
196 m_scanVar[i].setAxis(axis[
static_cast<int>(i)]);
197 m_scanVar[i].setScanned(scanned[
static_cast<int>(i)]);
224 mutableRun.addProperty(
"Facility", std::string(
"ILL"));
231 g_log.
debug() <<
"Failed to open nexus file \"" <<
m_filename <<
"\" in read mode: " << e.what() <<
"\n";
234 if (mutableRun.hasProperty(
"run_number"))
235 mutableRun.addProperty(
"run_list", mutableRun.getPropertyValueAsType<
int>(
"run_number"));
237 throw std::runtime_error(
"Failed to find run_number in Run object");
239 if (!mutableRun.hasProperty(
"Detector.calibration_file"))
240 mutableRun.addProperty(
"Detector.calibration_file", std::string(
"none"));
270 const size_t nBins = 1;
271 const bool isPointData =
true;
274 auto &run = instrumentWorkspace->mutableRun();
276 run.addProperty(
"start_time",
m_startTime.toISO8601String());
280 const auto &instrument = instrumentWorkspace->getInstrument();
281 auto ¶ms = instrumentWorkspace->instrumentParameters();
285 double refR, refTheta, refPhi;
286 referenceComponentPosition.getSpherical(refR, refTheta, refPhi);
290 auto &compInfo = instrumentWorkspace->mutableComponentInfo();
293 const auto detCompIndex = compInfo.indexOf(detectors->getComponentID());
294 const auto tubes = compInfo.children(detCompIndex);
295 const size_t nTubes = tubes.size();
297 const auto tube1CompIndex = compInfo.indexOf(tube1->getComponentID());
298 const auto pixels = compInfo.children(tube1CompIndex);
299 const size_t nPixels = pixels.size();
303 pixel->getBoundingBox(bb);
306 const auto tubeAnglesStr = params.getString(
"D2B",
"tube_angles");
307 if (!tubeAnglesStr.empty() && doAlign) {
308 std::vector<std::string> tubeAngles;
309 boost::split(tubeAngles, tubeAnglesStr[0], boost::is_any_of(
","));
310 const double ref = -refTheta;
311 for (
size_t i = 1; i <= nTubes; ++i) {
314 double r, theta, phi;
315 V3D oldPos = component->getPos();
318 const double angle = std::stod(tubeAngles[i - 1]);
319 const double finalAngle =
fabs(ref - angle);
320 g_log.
debug() <<
"Rotating " << compName <<
"to " << finalAngle <<
"rad\n";
322 const auto componentIndex = compInfo.indexOf(component->getComponentID());
323 compInfo.setPosition(componentIndex, newPos);
327 const auto tubeCentersStr = params.getString(
"D2B",
"tube_centers");
328 if (!tubeCentersStr.empty() && doAlign) {
329 std::vector<std::string> tubeCenters;
330 double maxYOffset = 0.;
331 boost::split(tubeCenters, tubeCentersStr[0], boost::is_any_of(
","));
332 for (
size_t i = 1; i <= nTubes; ++i) {
335 const double offset = std::stod(tubeCenters[i - 1]) - (double(nPixels) / 2 - 0.5);
337 V3D translation(0,
y, 0);
338 if (std::fabs(
y) > maxYOffset) {
339 maxYOffset = std::fabs(
y);
341 g_log.
debug() <<
"Moving " << compName <<
" to " <<
y <<
"\n";
342 V3D pos = component->getPos() + translation;
343 const auto componentIndex = compInfo.indexOf(component->getComponentID());
344 compInfo.setPosition(componentIndex, pos);
353 scanningWorkspaceBuilder.setTimeRanges(
m_startTime, timeDurations);
358 << (
m_startTime + std::accumulate(timeDurations.begin(), timeDurations.end(), 0.0)).toISO8601String()
367 auto rotationCentre =
V3D(0, 0, 0);
368 auto rotationAxis =
V3D(0, 1, 0);
369 scanningWorkspaceBuilder.setRelativeRotationsForScans(std::move(tubeAngles), rotationCentre, rotationAxis);
385 return instrumentWorkspace->getInstrument()->getComponentByName(
"tube_128")->getPos();
388 const auto &detInfo = instrumentWorkspace->detectorInfo();
389 const auto &indexOfFirstDet = detInfo.indexOf(1);
390 return detInfo.position(indexOfFirstDet);
407 double firstTubeRotationAngle = firstTubePosition.
angle(
V3D(0, 0, 1)) * RAD_TO_DEG;
415 firstTubeRotationAngle = -firstTubeRotationAngle;
416 std::transform(tubeRotations.begin(), tubeRotations.end(), tubeRotations.begin(),
417 [&](
double angle) { return (-angle); });
420 g_log.
debug() <<
"First tube rotation:" << firstTubeRotationAngle <<
"\n";
423 std::transform(tubeRotations.begin(), tubeRotations.end(), tubeRotations.begin(),
424 [&](
double angle) { return (angle - firstTubeRotationAngle); });
426 g_log.
debug() <<
"Instrument rotations to be applied : " << tubeRotations.front() <<
" to " << tubeRotations.back()
438 std::vector<double> axis = {0.};
439 std::vector<double> monitor =
getMonitor(scan);
442 for (
size_t i = 0; i < NUMBER_MONITORS; ++i) {
452 std::tuple<int, int, int> dimOrder{2, 1, 0};
453 std::set<int> acceptedIDs;
458 std::vector<int> customDetectorIDs;
474 const std::vector<double> axis =
getAxis(scan);
475 const std::vector<double> monitor =
getMonitor(scan);
477 std::size_t startIndex = NUMBER_MONITORS;
482 std::transform(monitor.begin(), monitor.end(),
m_outWorkspace->mutableE(0).begin(), [](
double e) { return sqrt(e); });
485 std::tuple<int, int, int> dimOrder{2, 1, 0};
486 std::set<detid_t> acceptedIDs;
489 acceptedIDs.insert(
static_cast<detid_t>(i));
507 Group entry0 = h5file.openGroup(
"entry0");
508 Group dataScan = entry0.openGroup(
"data_scan");
509 Group scanVar = dataScan.openGroup(
"scanned_variables");
510 Group varNames = scanVar.openGroup(
"variables_names");
516 for (
size_t i = 0; i < names.size(); ++i) {
534 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
535 if (!
m_scanVar[i].property.starts_with(
"Monitor")) {
536 const std::string scanVarName = boost::algorithm::to_lower_copy(
m_scanVar[i].
name);
537 const std::string scanVarProp = boost::algorithm::to_lower_copy(
m_scanVar[i].property);
538 const std::string propName = scanVarName +
"." + scanVarProp;
540 mutableRun.addProperty(
"ScanVar", propName,
true);
542 auto property = std::make_unique<TimeSeriesProperty<double>>(propName);
544 property->addValue(absoluteTimes[j], scan(
static_cast<int>(i),
static_cast<int>(j)));
546 mutableRun.addLogData(std::move(property),
true);
562 const std::string &propertyName)
const {
563 std::vector<double> scannedVariable;
565 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
566 if (
m_scanVar[i].property == propertyName) {
568 scannedVariable.emplace_back(scan(
static_cast<int>(i),
static_cast<int>(j)));
574 if (scannedVariable.empty())
575 throw std::runtime_error(
"Can not load file because scanned variable with property name " + propertyName +
578 return scannedVariable;
590 std::vector<double> monitor = {0.};
591 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
597 throw std::runtime_error(
"Monitors not found in scanned variables");
607 std::vector<double> axis = {0.};
609 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
625 std::vector<double> timeDurations;
626 for (
size_t i = 0; i <
m_scanVar.size(); ++i) {
627 if (
m_scanVar[i].property.starts_with(
"Time")) {
632 return timeDurations;
641 std::vector<DateAndTime> times;
644 times.emplace_back(time);
645 size_t timeIndex = 1;
647 time += durations[timeIndex - 1];
648 times.emplace_back(time);
664 if (scanVar.scanned == 1) {
666 if (scanVar.name ==
"2theta") {
683 throw std::runtime_error(
"Instrument " +
m_instName +
" not supported.");
694 size_t activePixels = D20_NUMBER_PIXELS - 2 * D20_NUMBER_DEAD_PIXELS;
698 throw std::runtime_error(
"Unknown resolution mode for instrument " +
m_instName);
707 <<
" actual detectors.\n";
718 double twoTheta0Actual = twoTheta0Read;
723 g_log.
debug() <<
"Setting 2theta0 to " << twoTheta0Actual;
725 const auto componentIndex = componentInfo.indexOf(component->getComponentID());
726 componentInfo.setRotation(componentIndex,
rotation);
737 std::filesystem::path directory(ConfigService::Instance().getInstrumentDirectory());
738 std::filesystem::path file(instName +
"_Definition.xml");
739 std::filesystem::path fullPath = std::filesystem::path(directory) / file;
740 return fullPath.string();
748 std::string scanTypeStr =
"NoScan";
750 scanTypeStr =
"DetectorScan";
752 scanTypeStr =
"OtherScan";
756 std::string resModeStr =
"Nominal";
773 throw std::runtime_error(
"Neither wavelength nor Monochromator.ei are not specified in the loaded file.");
789 m_offsetTheta =
static_cast<double>(D20_NUMBER_DEAD_PIXELS) * D20_PIXEL_SIZE -
799 extractor->setProperty(
"StartWorkspaceIndex", 1);
800 extractor->setProperty(
"OutputWorkspace",
"__unused");
801 extractor->execute();
804 converter->setProperty(
"InputWorkspace", det);
805 converter->setProperty(
"OutputWorkspace",
"__unused");
806 converter->setProperty(
"Target",
"SignedTheta");
807 converter->execute();
810 transposer->setProperty(
"InputWorkspace", converted);
811 transposer->setProperty(
"OutputWorkspace",
"__unused");
812 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_LAZY_FILELOADER_ALGORITHM(classname)
DECLARE_NEXUS_LAZY_FILELOADER_ALGORITHM should be used in place of the standard DECLARE_ALGORITHM mac...
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
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.