Mantid
Loading...
Searching...
No Matches
CorelliCrossCorrelate.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 +
9#include "MantidAPI/Run.h"
20
21#include <boost/algorithm/string/classification.hpp>
22#include <boost/algorithm/string/split.hpp>
23
24namespace Mantid::Algorithms {
25
26using namespace Kernel;
27using namespace API;
28using namespace Geometry;
29using namespace DataObjects;
30using Types::Core::DateAndTime;
31
32// Register the algorithm into the AlgorithmFactory
33DECLARE_ALGORITHM(CorelliCrossCorrelate)
34
35
38 auto wsValidator = std::make_shared<CompositeValidator>();
39 wsValidator->add<WorkspaceUnitValidator>("TOF");
40 wsValidator->add<InstrumentValidator>();
41
42 declareProperty(
43 std::make_unique<WorkspaceProperty<EventWorkspace>>("InputWorkspace", "", Direction::Input, wsValidator),
44 "An input workspace.");
45 declareProperty(std::make_unique<WorkspaceProperty<EventWorkspace>>("OutputWorkspace", "", Direction::Output),
46 "An output workspace.");
47
48 declareProperty("TimingOffset", EMPTY_INT(), std::make_shared<MandatoryValidator<int>>(),
49 "Correlation chopper TDC timing offset in nanoseconds.");
50}
51
52// Validate inputs workspace first.
53std::map<std::string, std::string> CorelliCrossCorrelate::validateInputs() {
54 std::map<std::string, std::string> errors;
55
56 inputWS = getProperty("InputWorkspace");
57
58 // check for null pointers - this is to protect against workspace groups
59 if (!inputWS) {
60 return errors;
61 }
62
63 // This algorithm will only work for CORELLI, check for CORELLI.
64 if (inputWS->getInstrument()->getName() != "CORELLI")
65 errors["InputWorkspace"] = "This Algorithm will only work for Corelli.";
66
67 // Must include the correlation-chopper in the IDF.
68 else if (!inputWS->getInstrument()->getComponentByName("correlation-chopper"))
69 errors["InputWorkspace"] = "Correlation chopper not found.";
70
71 // The chopper must have a sequence parameter
72 else if (inputWS->getInstrument()->getComponentByName("correlation-chopper")->getStringParameter("sequence").empty())
73 errors["InputWorkspace"] = "Found the correlation chopper but no chopper sequence?";
74
75 // Check for the sample and source.
76 else if (!inputWS->getInstrument()->getSource() || !inputWS->getInstrument()->getSample())
77 errors["InputWorkspace"] = "Instrument not sufficiently defined: failed to "
78 "get source and/or sample";
79
80 // Must include the chopper4 TDCs.
81 else if (!inputWS->run().hasProperty("chopper4_TDC"))
82 errors["InputWorkspace"] = "Workspace is missing chopper4 TDCs.";
83
84 // Must include the chopper4 MotorSpeed.
85 else if (!inputWS->run().hasProperty("BL9:Chop:Skf4:MotorSpeed"))
86 errors["InputWorkspace"] = "Workspace is missing chopper4 Motor Speed.";
87
88 // Check if input workspace is sorted.
89 else if (inputWS->getSortType() == UNSORTED)
90 errors["InputWorkspace"] = "The workspace needs to be a sorted.";
91
92 // Check event type for pulse times
93 else if (inputWS->getEventType() == WEIGHTED_NOTIME)
94 errors["InputWorkspace"] = "This workspace has no pulse time information.";
95
96 return errors;
97}
98
102 inputWS = getProperty("InputWorkspace");
103 outputWS = getProperty("OutputWorkspace");
104
105 if (outputWS != inputWS) {
106 outputWS = inputWS->clone();
107 }
108
109 // Read in chopper sequence from IDF.
110 // Chopper sequence, alternating between open and closed. If index%2==0 than
111 // absorbing else transparent.
112 IComponent_const_sptr chopper = inputWS->getInstrument()->getComponentByName("correlation-chopper");
113 std::vector<std::string> chopperSequence = chopper->getStringParameter("sequence");
114 g_log.information("Found chopper sequence: " + chopperSequence[0]);
115
116 std::vector<std::string> chopperSequenceSplit;
117 boost::split(chopperSequenceSplit, chopperSequence[0], boost::is_space());
118
119 std::vector<double> sequence;
120 sequence.resize(chopperSequenceSplit.size());
121 sequence[0] = boost::lexical_cast<double>(chopperSequenceSplit[0]);
122
123 // Need the cumulative sum of the chopper sequence and total transparent
124 double totalOpen = 0;
125 for (unsigned int i = 1; i < chopperSequenceSplit.size(); i++) {
126 auto seqAngle = boost::lexical_cast<double>(chopperSequenceSplit[i]);
127 sequence[i] = sequence[i - 1] + seqAngle;
128 if (i % 2 == 1)
129 totalOpen += seqAngle;
130 }
131
132 // Calculate the duty cycle and the event weights from the duty cycle.
133 double dutyCycle = totalOpen / sequence.back();
134 auto weightAbsorbing = static_cast<float>(-dutyCycle / (1.0 - dutyCycle));
135 g_log.information() << "dutyCycle = " << dutyCycle << " weightTransparent = 1.0"
136 << " weightAbsorbing = " << weightAbsorbing << "\n";
137
138 // Read in the TDC timings for the correlation chopper and apply the timing
139 // offset.
140 auto chopperTdc = dynamic_cast<ITimeSeriesProperty *>(inputWS->run().getLogData("chopper4_TDC"));
141 if (!chopperTdc) {
142 throw std::runtime_error("chopper4_TDC not found");
143 }
144 std::vector<DateAndTime> tdc = chopperTdc->timesAsVector();
145
146 int offset_int = getProperty("TimingOffset");
147 const auto offset = static_cast<int64_t>(offset_int);
148
149 std::transform(tdc.begin(), tdc.end(), tdc.begin(), [offset](auto timing) { return timing + offset; });
150
151 // Determine period from chopper frequency.
152 auto motorSpeed = dynamic_cast<TimeSeriesProperty<double> *>(inputWS->run().getProperty("BL9:Chop:Skf4:MotorSpeed"));
153 if (!motorSpeed) {
154 throw Exception::NotFoundError("Could not find a log value for the motor speed", "BL9:Chop:Skf4:MotorSpeed");
155 }
156 double period = 1e9 / static_cast<double>(motorSpeed->timeAverageValue());
157 g_log.information() << "Frequency = " << 1e9 / period << "Hz Period = " << period << "ns\n";
158
159 // Get the sample and source, calculate distances.
160 IComponent_const_sptr sample = inputWS->getInstrument()->getSample();
161 const double distanceChopperToSource = inputWS->getInstrument()->getSource()->getDistance(*chopper);
162 const double distanceSourceToSample = inputWS->getInstrument()->getSource()->getDistance(*sample);
163
164 // extract formula from instrument parameters
165 std::vector<std::string> t0_formula = inputWS->getInstrument()->getStringParameter("t0_formula");
166 if (t0_formula.empty())
167 throw Exception::InstrumentDefinitionError("Unable to retrieve t0_formula among instrument parameters");
168 std::string formula = t0_formula[0];
169 g_log.debug() << formula << "\n";
170
171 const double m_convfactor = 0.5e+12 * Mantid::PhysicalConstants::NeutronMass / Mantid::PhysicalConstants::meV;
172
173 // Do the cross correlation.
174 auto numHistograms = static_cast<int64_t>(inputWS->getNumberHistograms());
175 API::Progress prog = API::Progress(this, 0.0, 1.0, numHistograms);
176 const auto &spectrumInfo = inputWS->spectrumInfo();
178 for (int64_t i = 0; i < numHistograms; ++i) {
180
181 auto &evlist = outputWS->getSpectrum(i);
182
183 // Switch to weighted if needed.
184 if (evlist.getEventType() == TOF)
185 evlist.switchTo(WEIGHTED);
186
187 std::vector<WeightedEvent> &events = evlist.getWeightedEvents();
188
189 // Skip if empty.
190 if (events.empty())
191 continue;
192
193 // Check for duplicate pulse problem in Corelli.
194 DateAndTime emptyTime;
195 if (events.back().pulseTime() == emptyTime)
196 throw std::runtime_error("Missing pulse times on events. This will not work.");
197
198 // Scale for elastic scattering.
199 double distanceSourceToDetector = distanceSourceToSample + spectrumInfo.l2(i);
200 double tofScale = distanceChopperToSource / distanceSourceToDetector;
201
202 double E1;
203 mu::Parser parser;
204 parser.DefineVar("incidentEnergy",
205 &E1); // associate variable E1 to this parser
206 parser.SetExpr(formula);
207
208 uint64_t tdc_i = 0;
209 std::vector<WeightedEvent>::iterator it;
210 for (it = events.begin(); it != events.end(); ++it) {
211 double tof = it->tof();
212 E1 = m_convfactor * (distanceSourceToDetector / tof) * (distanceSourceToDetector / tof);
213 double t0 = parser.Eval();
214
215 DateAndTime tofTime = it->pulseTime() + static_cast<int64_t>(((tof - t0) * tofScale + t0) * 1000.);
216 while (tdc_i != tdc.size() && tofTime > tdc[tdc_i])
217 tdc_i += 1;
218
219 double angle =
220 360. * static_cast<double>(tofTime.totalNanoseconds() - tdc[tdc_i - 1].totalNanoseconds()) / period;
221
222 std::vector<double>::iterator location;
223 location = std::lower_bound(sequence.begin(), sequence.end(), angle);
224
225 if ((location - sequence.begin()) % 2 == 0) {
226 it->m_weight *= weightAbsorbing;
227 it->m_errorSquared *= weightAbsorbing * weightAbsorbing;
228 }
229 }
230
231 // Warn if the tdc signal has stopped during the run
232 if ((events.back().pulseTime() + static_cast<int64_t>(events.back().tof() * 1000.)) >
233 (tdc.back() + static_cast<int64_t>(period * 2)))
234 g_log.warning("Events occurred long after last TDC.");
235
236 prog.report();
238 }
240 setProperty("OutputWorkspace", outputWS);
241}
242
243} // namespace Mantid::Algorithms
#define DECLARE_ALGORITHM(classname)
Definition: Algorithm.h:576
#define PARALLEL_START_INTERRUPT_REGION
Begins a block to skip processing is the algorithm has been interupted Note the end of the block if n...
Definition: MultiThreaded.h:94
#define PARALLEL_END_INTERRUPT_REGION
Ends a block to skip processing is the algorithm has been interupted Note the start of the block if n...
#define PARALLEL_FOR_IF(condition)
Empty definitions - to enable set your complier to enable openMP.
#define PARALLEL_CHECK_INTERRUPT_REGION
Adds a check after a Parallel region to see if it was interupted.
TypedValue getProperty(const std::string &name) const override
Get the value of a property.
Definition: Algorithm.cpp:2076
Kernel::Logger & g_log
Definition: Algorithm.h:451
A validator which checks that a workspace has a valid instrument.
Helper class for reporting progress from algorithms.
Definition: Progress.h:25
A property class for workspaces.
A validator which checks that the unit of the workspace referred to by a WorkspaceProperty is the exp...
CorelliCrossCorrelate : TODO: DESCRIPTION.
void exec() override
Execute the algorithm.
DataObjects::EventWorkspace_sptr outputWS
std::map< std::string, std::string > validateInputs() override
Method checking errors on ALL the inputs, before execution.
DataObjects::EventWorkspace_const_sptr inputWS
Input workspace.
Exception for errors associated with the instrument definition.
Definition: Exception.h:220
Exception for when an item is not found in a collection.
Definition: Exception.h:145
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
A non-templated interface to a TimeSeriesProperty.
void debug(const std::string &msg)
Logs at debug level.
Definition: Logger.cpp:114
void warning(const std::string &msg)
Logs at warning level.
Definition: Logger.cpp:86
void information(const std::string &msg)
Logs at information level.
Definition: Logger.cpp:105
Validator to check that a property is not left empty.
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
Definition: ProgressBase.h:51
A specialised Property class for holding a series of time-value pairs.
std::complex< double > MANTID_API_DLL E1(std::complex< double > z)
Integral for Gamma.
@ WEIGHTED_NOTIME
Definition: IEventList.h:18
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
Definition: IComponent.h:161
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.
Definition: MultiThreaded.h:22
static constexpr double NeutronMass
Mass of the neutron in kg.
static constexpr double meV
1 meV in Joules.
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
Definition: EmptyValues.h:25
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54