37#include <boost/algorithm/string.hpp>
45constexpr int DEFAULT_NPATHS = 1000;
46constexpr int DEFAULT_SEED = 123456789;
47constexpr int DEFAULT_NSCATTERINGS = 2;
48constexpr int DEFAULT_LATITUDINAL_DETS = 5;
49constexpr int DEFAULT_LONGITUDINAL_DETS = 10;
57inline double fromWaveVector(
double wavevector) {
61struct EFixedProvider {
62 explicit EFixedProvider(
const ExperimentInfo &expt) : m_expt(expt), m_emode(expt.
getEMode()), m_EFixed(0.0) {
64 m_EFixed = m_expt.getEFixed();
72 return m_expt.getEFixed(detID);
85 auto data2DNew = std::make_unique<DiscusData2D>();
86 data2DNew->m_data.resize(
m_data.size());
87 for (
size_t i = 0; i <
m_data.size(); i++) {
88 data2DNew->m_data[i].X =
m_data[i].X;
89 data2DNew->m_data[i].Y = clearY ? std::vector<double>(
m_data[i].
Y.size(), 0.) :
m_data[i].Y;
97 throw std::runtime_error(
"DiscusData2D::getSpecAxisValues - No spec axis has been defined.");
109 auto wsValidator = std::make_shared<InstrumentValidator>();
113 "The name of the input workspace. The input workspace must have X units of Momentum (k) for elastic "
114 "calculations and units of energy transfer (DeltaE) for inelastic calculations. This is used to "
115 "supply the sample details, the detector positions and the x axis range to calculate corrections for");
118 "The name of the workspace containing S'(q) or S'(q, w). For elastic calculations, the input "
119 "workspace must contain a single spectrum and have X units of momentum transfer. A workspace group "
120 "containing one workspace per component can also be supplied if a calculation is being run on a "
121 "workspace with a sample environment specified");
123 "Name for the WorkspaceGroup that will be created. Each workspace in the "
124 "group contains a calculated weight for a particular number of "
125 "scattering events. The number of scattering events varies from 1 up to "
126 "the number supplied in the NumberOfScatterings parameter. The group "
127 "will also include an additional workspace for a calculation with a "
128 "single scattering event where the absorption post scattering has been "
130 auto wsKValidator = std::make_shared<WorkspaceUnitValidator>(
"Momentum");
133 "A workspace containing the scattering cross section as a function of k, :math:`\\sigma_s(k)`. Note "
134 "- this parameter would normally be left empty which results in the tabulated cross section data "
135 "being used instead which implies no wavelength dependence");
137 auto positiveInt = std::make_shared<Kernel::BoundedValidator<int>>();
138 positiveInt->setLower(1);
139 declareProperty(
"NumberOfSimulationPoints",
EMPTY_INT(), positiveInt,
140 "The number of points on the input workspace x axis for which a simulation is attempted");
142 declareProperty(
"NeutronPathsSingle", DEFAULT_NPATHS, positiveInt,
143 "The number of \"neutron\" paths to generate for single scattering");
144 declareProperty(
"NeutronPathsMultiple", DEFAULT_NPATHS, positiveInt,
145 "The number of \"neutron\" paths to generate for multiple scattering");
146 declareProperty(
"SeedValue", DEFAULT_SEED, positiveInt,
"Seed the random number generator with this value");
147 auto nScatteringsValidator = std::make_shared<Kernel::BoundedValidator<int>>();
148 nScatteringsValidator->setLower(1);
149 nScatteringsValidator->setUpper(5);
150 declareProperty(
"NumberScatterings", DEFAULT_NSCATTERINGS, nScatteringsValidator,
"Number of scatterings");
152 auto interpolateOpt = createInterpolateOption();
153 declareProperty(interpolateOpt->property(), interpolateOpt->propertyDoc());
154 declareProperty(
"SparseInstrument",
false,
155 "Enable simulation on special "
156 "instrument with a sparse grid of "
157 "detectors interpolating the "
158 "results to the real instrument.");
159 auto threeOrMore = std::make_shared<Kernel::BoundedValidator<int>>();
160 threeOrMore->setLower(3);
161 declareProperty(
"NumberOfDetectorRows", DEFAULT_LATITUDINAL_DETS, threeOrMore,
162 "Number of detector rows in the detector grid of the sparse instrument.");
163 setPropertySettings(
"NumberOfDetectorRows",
164 std::make_unique<EnabledWhenProperty>(
"SparseInstrument", ePropertyCriterion::IS_NOT_DEFAULT));
165 auto twoOrMore = std::make_shared<Kernel::BoundedValidator<int>>();
166 twoOrMore->setLower(2);
167 declareProperty(
"NumberOfDetectorColumns", DEFAULT_LONGITUDINAL_DETS, twoOrMore,
168 "Number of detector columns in the detector grid "
169 "of the sparse instrument.");
170 setPropertySettings(
"NumberOfDetectorColumns",
171 std::make_unique<EnabledWhenProperty>(
"SparseInstrument", ePropertyCriterion::IS_NOT_DEFAULT));
172 declareProperty(
"ImportanceSampling",
false,
173 "Enable importance sampling on the Q value chosen on multiple scatters based on Q.S(Q)");
175 declareProperty(
"MaxScatterPtAttempts", 5000, positiveInt,
176 "Maximum number of tries made to generate a scattering point "
177 "within the sample. Objects with holes in them, e.g. a thin "
178 "annulus can cause problems if this number is too low.\n"
179 "If a scattering point cannot be generated by increasing "
180 "this value then there is most likely a problem with "
181 "the sample geometry.");
182 declareProperty(
"SimulateEnergiesIndependently",
false,
183 "For inelastic calculation, whether the results for adjacent energy transfer bins are simulated "
184 "separately. Currently applies to Direct geometry only");
185 declareProperty(
"NormalizeStructureFactors",
false,
186 "Enable normalization of supplied structure factor(s). May be required when running a calculation "
187 "involving more than one material where the normalization of the default S(Q)=1 structure factor "
188 "doesn't match the normalization of a supplied non-isotropic structure factor");
189 declareProperty(
"RadialCollimator",
false,
190 "Enable use of a radial collimator that assign zero weights to tracks where the final scatter "
191 "is not in a position that allows the final track segment to pass through the collimator corridor "
192 "which spans from the guage volume toward the each detector");
200 std::map<std::string, std::string> issues;
202 if (inputWS ==
nullptr) {
205 issues[
"InputWorkspace"] =
"Input workspace must be a matrix workspace";
210 issues[
"InputWorkspace"] =
"Input workspace does not have a Sample";
212 bool atLeastOneValidShape = inputWS->sample().getShape().hasValidShape();
213 if (!atLeastOneValidShape) {
214 if (inputWS->sample().hasEnvironment()) {
215 auto env = &inputWS->sample().getEnvironment();
216 for (
size_t i = 0; i < env->nelements(); i++) {
217 if (env->getComponent(i).hasValidShape()) {
218 atLeastOneValidShape =
true;
224 if (!atLeastOneValidShape) {
225 issues[
"InputWorkspace"] =
"Either the Sample or one of the environment parts must have a valid shape.";
228 if (inputWS->sample().getShape().hasValidShape())
229 if (inputWS->sample().getMaterial().numberDensity() == 0)
230 issues[
"InputWorkspace"] =
"Sample must have a material set up with a non-zero number density\n";
231 if (inputWS->sample().hasEnvironment()) {
232 auto env = &inputWS->sample().getEnvironment();
233 for (
size_t i = 0; i < env->nelements(); i++)
234 if (env->getComponent(i).hasValidShape())
235 if (env->getComponent(i).material().numberDensity() == 0)
236 issues[
"InputWorkspace"] =
"Sample environment component " +
std::to_string(i) +
237 " must have a material set up with a non-zero number density\n";
240 std::vector<MatrixWorkspace_sptr> SQWSs;
242 auto SQWSGroup = std::dynamic_pointer_cast<WorkspaceGroup>(SQWSBase);
244 auto groupMembers = SQWSGroup->getAllItems();
245 std::set<std::string> materialNames;
246 materialNames.insert(inputWS->sample().getMaterial().name());
247 if (inputWS->sample().hasEnvironment()) {
248 auto nEnvComponents = inputWS->sample().getEnvironment().nelements();
249 for (
size_t i = 0; i < nEnvComponents; i++)
250 materialNames.insert(inputWS->sample().getEnvironment().getComponent(i).material().name());
253 for (
auto &materialName : materialNames) {
254 auto wsIt = std::find_if(groupMembers.begin(), groupMembers.end(),
255 [materialName](
Workspace_sptr &ws) { return ws->getName() == materialName; });
256 if (wsIt == groupMembers.end()) {
257 issues[
"StructureFactorWorkspace"] =
258 "No workspace for material " + materialName +
" found in S(Q,w) workspace group";
260 SQWSs.push_back(std::dynamic_pointer_cast<MatrixWorkspace>(*wsIt));
263 SQWSs.push_back(std::dynamic_pointer_cast<MatrixWorkspace>(SQWSBase));
266 if (inputWS->getAxis(0)->unit()->unitID() !=
"Momentum")
267 issues[
"InputWorkspace"] +=
"Input workspace must have units of Momentum (k) for elastic instrument\n";
268 for (
auto &SQWS : SQWSs) {
269 if (SQWS->getNumberHistograms() != 1)
270 issues[
"StructureFactorWorkspace"] +=
"S(Q) workspace must contain a single spectrum for elastic mode\n";
272 if (SQWS->getAxis(0)->unit()->unitID() !=
"MomentumTransfer")
273 issues[
"StructureFactorWorkspace"] +=
"S(Q) workspace must have units of MomentumTransfer\n";
276 for (
auto &SQWS : SQWSs) {
277 if (inputWS->getAxis(0)->unit()->unitID() !=
"DeltaE")
278 issues[
"InputWorkspace"] =
"Input workspace must have units of DeltaE for inelastic instrument\n";
279 std::set<std::string> axisUnits;
280 axisUnits.insert(SQWS->getAxis(0)->unit()->unitID());
281 axisUnits.insert(SQWS->getAxis(1)->unit()->unitID());
282 if (axisUnits != std::set<std::string>{
"DeltaE",
"MomentumTransfer"})
283 issues[
"StructureFactorWorkspace"] +=
284 "S(Q, w) workspace must have units of Energy Transfer and MomentumTransfer\n";
286 if (SQWS->getAxis(1)->isSpectra())
287 issues[
"StructureFactorWorkspace"] +=
"S(Q, w) must have a numeric spectrum axis\n";
288 std::vector<double> wValues;
289 if (SQWS->getAxis(0)->unit()->unitID() ==
"DeltaE") {
290 if (!SQWS->isCommonBins())
291 issues[
"StructureFactorWorkspace"] +=
"S(Q,w) must have common w values at all Q";
294 auto checkEqualQBins = [&issues](
const MantidVec &qValues) {
297 issues[
"StructureFactorWorkspace"] +=
298 "S(Q,w) must have equal size bins in Q in order to support gaussian interpolation";
302 if (SQWS->getAxis(0)->unit()->unitID() ==
"MomentumTransfer") {
303 for (
size_t iHist = 0; iHist < SQWS->getNumberHistograms(); iHist++) {
304 auto qValues = SQWS->dataX(iHist);
305 checkEqualQBins(qValues);
307 }
else if (SQWS->getAxis(1)->unit()->unitID() ==
"MomentumTransfer") {
308 auto qAxis =
dynamic_cast<NumericAxis *
>(SQWS->getAxis(1));
311 checkEqualQBins(qValues);
317 for (
auto &SQWS : SQWSs) {
318 for (
size_t i = 0; i < SQWS->getNumberHistograms(); i++) {
319 auto &
y = SQWS->y(i);
320 if (std::any_of(
y.cbegin(),
y.cend(), [](
const auto yval) { return yval < 0 || std::isnan(yval); }))
321 issues[
"StructureFactorWorkspace"] +=
"S(Q) workspace must have all y >= 0";
325 const int nSimulationPoints =
getProperty(
"NumberOfSimulationPoints");
326 if (!
isEmpty(nSimulationPoints)) {
329 interpOpt.
set(interpValue,
false,
false);
331 if (!nSimPointsIssue.empty())
332 issues[
"NumberOfSimulationPoints"] = nSimPointsIssue;
335 const bool simulateEnergiesIndependently =
getProperty(
"SimulateEnergiesIndependently");
336 if (simulateEnergiesIndependently) {
338 issues[
"SimulateEnergiesIndependently"] =
339 "SimulateEnergiesIndependently is only applicable to inelastic direct geometry calculations";
341 issues[
"SimulateEnergiesIndependently"] =
342 "SimulateEnergiesIndependently is only applicable to inelastic direct geometry calculations. Different "
343 "energy transfer bins are always simulated separately for indirect geometry";
356 double &xmax)
const {
358 xmin = std::numeric_limits<double>::max();
364 for (
size_t wsIndex = 0; wsIndex < numberOfSpectra; wsIndex++) {
365 if (spectrumInfo.hasDetectors(wsIndex) && !spectrumInfo.isMonitor(wsIndex) && !spectrumInfo.isMasked(wsIndex)) {
366 const auto &dataX = ws.
points(wsIndex);
367 const double xfront = dataX.front();
368 const double xback = dataX.back();
369 if (std::isnormal(xfront) && std::isnormal(xback)) {
378 throw std::runtime_error(
"Unable to determine min and max x values for workspace");
383 auto SQWSGroup = std::dynamic_pointer_cast<WorkspaceGroup>(suppliedSQWS);
384 size_t nEnvComponents = 0;
390 auto SQWSGroupMember = std::static_pointer_cast<MatrixWorkspace>(SQWSGroup->getItem(matName));
392 if (nEnvComponents > 0) {
394 SQWSGroupMember = std::static_pointer_cast<MatrixWorkspace>(SQWSGroup->getItem(matName));
397 for (
size_t i = 1; i < nEnvComponents; i++) {
399 SQWSGroupMember = std::static_pointer_cast<MatrixWorkspace>(SQWSGroup->getItem(matName));
404 std::dynamic_pointer_cast<MatrixWorkspace>(suppliedSQWS));
406 *std::dynamic_pointer_cast<MatrixWorkspace>(suppliedSQWS),
static_cast<size_t>(1),
407 HistogramData::Histogram(HistogramData::Points{0.}, HistogramData::Frequencies{1.}));
408 if (nEnvComponents > 0) {
410 g_log.
information() <<
"Creating isotropic structure factor for " << matName << std::endl;
413 for (
size_t i = 1; i < nEnvComponents; i++) {
415 g_log.
information() <<
"Creating isotropic structure factor for " << matName << std::endl;
426 const std::string_view &matName,
431 if (SQWS->getAxis(1)->unit()->unitID() ==
"MomentumTransfer") {
433 transposeAlgorithm->initialize();
434 transposeAlgorithm->setProperty(
"InputWorkspace", SQWS);
435 transposeAlgorithm->setProperty(
"OutputWorkspace",
"_");
436 transposeAlgorithm->execute();
437 SQWS = transposeAlgorithm->getProperty(
"OutputWorkspace");
438 }
else if (SQWS->getAxis(1)->isSpectra()) {
440 auto newAxis = std::make_unique<NumericAxis>(std::vector<double>{0.});
441 newAxis->setUnit(
"DeltaE");
442 SQWS->replaceAxis(1, std::move(newAxis));
444 auto specAxis =
dynamic_cast<NumericAxis *
>(SQWS->getAxis(1));
445 std::vector<DiscusData1D> data;
446 for (
size_t i = 0; i < SQWS->getNumberHistograms(); i++) {
447 data.emplace_back(SQWS->x(i).rawData(), SQWS->y(i).rawData());
451 std::make_shared<DiscusData2D>(data, std::make_shared<std::vector<double>>(specAxis->getValues()))};
452 SQWSMapping.
logSQ = SQWSMapping.SQ->createCopy();
454 m_SQWSs.push_back(SQWSMapping);
463 if (ws->isHistogramData()) {
466 pointDataAlgorithm->initialize();
467 pointDataAlgorithm->setProperty(
"InputWorkspace", ws);
468 pointDataAlgorithm->setProperty(
"OutputWorkspace",
"_");
469 pointDataAlgorithm->execute();
470 ws = pointDataAlgorithm->getProperty(
"OutputWorkspace");
475 SQWSPoints->setSharedY(0, ws->sharedY(0));
476 SQWSPoints->setSharedE(0, ws->sharedE(0));
477 std::vector<double> newX = ws->histogram(0).dataX();
479 SQWSPoints->setSharedX(0, HistogramData::Points(newX).cowData());
483 auto binAxis =
dynamic_cast<BinEdgeAxis *
>(ws->getAxis(1));
486 std::vector<double> centres;
488 auto newAxis = std::make_unique<NumericAxis>(centres);
489 newAxis->setUnit(ws->getAxis(1)->unit()->unitID());
490 ws->replaceAxis(1, std::move(newAxis));
499 throw std::runtime_error(
"This algorithm explicitly stores named output workspaces in the ADS so must be run with "
500 "AlwaysStoreInADS set to true");
509 m_sigmaSS = std::make_shared<DiscusData1D>(sigmaSSWS->x(0).rawData(), sigmaSSWS->y(0).rawData());
513 double qmax = std::numeric_limits<float>::max();
514 EFixedProvider
efixed(*inputWS);
528 int nSimulationPointsInt =
getProperty(
"NumberOfSimulationPoints");
529 size_t nSimulationPoints =
static_cast<size_t>(nSimulationPointsInt);
531 if (
isEmpty(nSimulationPoints)) {
532 nSimulationPoints = inputNbins;
533 }
else if (nSimulationPoints > inputNbins) {
534 g_log.
warning() <<
"The requested number of simulation points is larger "
535 "than the maximum number of simulations per spectra. "
537 << inputNbins <<
".\n ";
538 nSimulationPoints = inputNbins;
543 const bool useSparseInstrument =
getProperty(
"SparseInstrument");
545 if (useSparseInstrument) {
546 const int latitudinalDets =
getProperty(
"NumberOfDetectorRows");
547 const int longitudinalDets =
getProperty(
"NumberOfDetectorColumns");
550 const int nScatters =
getProperty(
"NumberScatterings");
552 std::vector<MatrixWorkspace_sptr> simulationWSs;
553 std::vector<MatrixWorkspace_sptr> outputWSs;
556 auto noAbsSimulationWS = useSparseInstrument ? sparseWS->clone() : noAbsOutputWS;
557 for (
int i = 0; i < nScatters; i++) {
560 simulationWSs.emplace_back(simulationWS);
561 outputWSs.emplace_back(outputWS);
563 const MatrixWorkspace &instrumentWS = useSparseInstrument ? *sparseWS : *inputWS;
564 const auto nhists = useSparseInstrument ? sparseWS->
getNumberHistograms() : inputWS->getNumberHistograms();
566 const int nSingleScatterEvents =
getProperty(
"NeutronPathsSingle");
567 const int nMultiScatterEvents =
getProperty(
"NeutronPathsMultiple");
578 Progress prog(
this, 0.0, 1.0, nhists * (nSimulationPoints + 1));
580 const std::string reportMsg =
"Computing corrections";
582 bool enableParallelFor =
true;
583 enableParallelFor = std::all_of(simulationWSs.cbegin(), simulationWSs.cend(),
592 for (int64_t i = 0; i < static_cast<int64_t>(nhists); ++i) {
600 if (spectrumInfo.hasDetectors(i) && !spectrumInfo.isMonitor(i) && !spectrumInfo.isMasked(i)) {
602 const double eFixedValue =
efixed.value(spectrumInfo.detector(i).getID());
603 auto xPoints = instrumentWS.
points(i).rawData();
607 const auto nbins = kInW.size();
609 const size_t nsteps = std::max(
static_cast<size_t>(1), nSimulationPoints - 1);
610 const size_t xStepSize = nbins == 1 ? 1 : (nbins - 1) / nsteps;
613 auto componentWorkspaces =
m_SQWSs;
619 std::vector<double> kValues;
620 std::transform(kInW.begin(), kInW.end(), std::back_inserter(kValues),
621 [](std::tuple<double, int, double> t) { return std::get<0>(t); });
624 for (
size_t bin = 0; bin < nbins; bin += xStepSize) {
625 const double kinc = std::get<0>(kInW[bin]);
626 if ((kinc <= 0) || std::isnan(kinc)) {
631 std::vector<double> wValues = std::get<1>(kInW[bin]) == -1 ? xPoints : std::vector{std::get<2>(kInW[bin])};
636 auto [weights, weightsErrors] =
637 simulatePaths(nSingleScatterEvents, 1, rng, componentWorkspaces, kinc, wValues,
true, detectorInfo, i);
638 if (std::get<1>(kInW[bin]) == -1) {
639 noAbsSimulationWS->getSpectrum(i).mutableY() += weights;
640 noAbsSimulationWS->getSpectrum(i).mutableE() += weightsErrors;
642 noAbsSimulationWS->getSpectrum(i).mutableY()[std::get<1>(kInW[bin])] = weights[0];
643 noAbsSimulationWS->getSpectrum(i).mutableE()[std::get<1>(kInW[bin])] = weightsErrors[0];
646 for (
int ne = 0; ne < nScatters; ne++) {
647 int nEvents = ne == 0 ? nSingleScatterEvents : nMultiScatterEvents;
649 std::tie(weights, weightsErrors) =
650 simulatePaths(nEvents, ne + 1, rng, componentWorkspaces, kinc, wValues,
false, detectorInfo, i);
651 if (std::get<1>(kInW[bin]) == -1.0) {
652 simulationWSs[ne]->getSpectrum(i).mutableY() += weights;
653 simulationWSs[ne]->getSpectrum(i).mutableE() += weightsErrors;
655 simulationWSs[ne]->getSpectrum(i).mutableY()[std::get<1>(kInW[bin])] = weights[0];
656 simulationWSs[ne]->getSpectrum(i).mutableE()[std::get<1>(kInW[bin])] = weightsErrors[0];
663 if (xStepSize > 1 && bin + xStepSize >= nbins && bin + 1 != nbins) {
664 bin = nbins - xStepSize - 1;
671 if (!useSparseInstrument && xStepSize > 1) {
672 auto histNoAbs = noAbsSimulationWS->histogram(i);
673 if (xStepSize < nbins) {
676 std::fill(histNoAbs.mutableY().begin() + 1, histNoAbs.mutableY().end(), histNoAbs.y()[0]);
678 noAbsOutputWS->setHistogram(i, histNoAbs);
680 for (
size_t ne = 0; ne < static_cast<size_t>(nScatters); ne++) {
681 auto histnew = simulationWSs[ne]->histogram(i);
682 if (xStepSize < nbins) {
685 std::fill(histnew.mutableY().begin() + 1, histnew.mutableY().end(), histnew.y()[0]);
687 outputWSs[ne]->setHistogram(i, histnew);
697 if (useSparseInstrument) {
698 Poco::Thread::sleep(200);
699 const std::string reportMsgSpatialInterpolation =
"Spatial Interpolation";
700 prog.
report(reportMsgSpatialInterpolation);
701 interpolateFromSparse(*noAbsOutputWS, *std::dynamic_pointer_cast<SparseWorkspace>(noAbsSimulationWS),
703 for (
size_t ne = 0; ne < static_cast<size_t>(nScatters); ne++) {
704 interpolateFromSparse(*outputWSs[ne], *std::dynamic_pointer_cast<SparseWorkspace>(simulationWSs[ne]),
710 auto wsgroup = std::make_shared<WorkspaceGroup>();
712 if (AnalysisDataService::Instance().doesExist(outputGroupWSName))
713 API::AnalysisDataService::Instance().deepRemoveGroup(outputGroupWSName);
715 const std::string wsNamePrefix = outputGroupWSName +
"_Scatter_";
716 std::string wsName = wsNamePrefix +
"1_NoAbs";
718 wsgroup->addWorkspace(noAbsOutputWS);
720 for (
size_t i = 0; i < outputWSs.size(); i++) {
723 wsgroup->addWorkspace(outputWSs[i]);
725 auto integratedWorkspace =
integrateWS(outputWSs[i]);
727 wsgroup->addWorkspace(integratedWorkspace);
730 if (outputWSs.size() > 1) {
733 summedMScatOutput = std::accumulate(outputWSs.cbegin() + 1, outputWSs.cend(), summedMScatOutput);
734 wsName = wsNamePrefix +
"2_" +
std::to_string(outputWSs.size()) +
"_Summed";
736 wsgroup->addWorkspace(summedMScatOutput);
739 summedAllScatOutput = summedMScatOutput + outputWSs[0];
740 wsName = wsNamePrefix +
"1_" +
std::to_string(outputWSs.size()) +
"_Summed";
742 wsgroup->addWorkspace(summedAllScatOutput);
745 ratioOutput = outputWSs[0] / summedAllScatOutput;
746 wsName = outputGroupWSName +
"_Ratio_Single_To_All";
748 wsgroup->addWorkspace(ratioOutput);
752 auto invRatioOutput = 1 / ratioOutput;
754 replaceNans->setChild(
true);
755 replaceNans->initialize();
756 replaceNans->setProperty(
"InputWorkspace", invRatioOutput);
757 replaceNans->setProperty(
"OutputWorkspace", invRatioOutput);
758 replaceNans->setProperty(
"NaNValue", 0.0);
759 replaceNans->setProperty(
"InfinityValue", 0.0);
760 replaceNans->execute();
761 wsName = outputGroupWSName +
"_Ratio_All_To_Single";
763 wsgroup->addWorkspace(invRatioOutput);
770 if (
g_log.
is(Kernel::Logger::Priority::PRIO_INFORMATION)) {
771 g_log.
information() <<
"Total simulation points=" << nhists * nSimulationPoints <<
"\n";
773 g_log.
information() <<
"Generating initial track required " << kv.first <<
" attempts on " << kv.second
777 <<
static_cast<double>(
m_IkCalculations) /
static_cast<double>(nhists * nSimulationPoints)
779 if (
g_log.
is(Kernel::Logger::Priority::PRIO_DEBUG))
780 for (
size_t i = 0; i <
m_SQWSs.size(); i++)
793std::vector<std::tuple<double, int, double>>
795 std::vector<std::tuple<double, int, double>> kInW;
796 const double kFixed = toWaveVector(
efixed);
799 std::transform(xPoints.begin(), xPoints.end(), std::back_inserter(kInW), [&
index](
double d) {
800 auto t = std::make_tuple(d, index, 0.);
806 kInW.emplace_back(std::make_tuple(kFixed, -1, 0.));
808 for (
int i = 0; i < static_cast<int>(xPoints.size()); i++) {
810 kInW.emplace_back(std::make_tuple(kFixed, i, xPoints[i]));
812 const double initialE =
efixed + xPoints[i];
814 const double kin = toWaveVector(initialE);
815 kInW.emplace_back(std::make_tuple(kin, i, xPoints[i]));
818 kInW.emplace_back(std::make_tuple(-1.0, i, xPoints[i]));
832 for (
auto &SQWSMapping :
m_SQWSs) {
833 auto &SQWS = SQWSMapping.SQ;
834 std::shared_ptr<DiscusData2D> outputWS = SQWS->createCopy(
true);
835 std::vector<double> IOfQYFull;
837 for (
size_t iW = 0; iW < SQWS->getNumberHistograms(); iW++) {
838 std::vector<double> qValues = SQWS->histogram(iW).X;
839 std::vector<double> SQValues = SQWS->histogram(iW).Y;
841 if (qValues.front() > 0.) {
842 qValues.insert(qValues.begin(), 0.);
843 SQValues.insert(SQValues.begin(), SQValues.front());
845 if (qValues.back() < qmax) {
846 qValues.push_back(qmax);
847 SQValues.push_back(SQValues.back());
850 for (
size_t i = 1; i < qValues.size(); i++) {
851 if (std::abs(SQValues[i] - SQValues[i - 1]) >
852 std::numeric_limits<double>::epsilon() * std::min(SQValues[i - 1], SQValues[i])) {
853 qValues.insert(qValues.begin() + i, std::nextafter(qValues[i], -DBL_MAX));
854 SQValues.insert(SQValues.begin() + i, SQValues[i - 1]);
859 std::vector<double> QSQValues;
860 std::transform(SQValues.begin(), SQValues.end(), qValues.begin(), std::back_inserter(QSQValues),
861 std::multiplies<double>());
863 outputWS->histogram(iW).X.resize(qValues.size());
864 outputWS->histogram(iW).X = qValues;
865 outputWS->histogram(iW).Y.resize(QSQValues.size());
866 outputWS->histogram(iW).Y = QSQValues;
868 SQWSMapping.QSQ = outputWS;
882std::tuple<std::vector<double>, std::vector<double>, std::vector<double>>
884 const bool returnCumulative) {
885 std::vector<double> IOfQYFull, qValuesFull, wIndices;
886 double IOfQMaxPreviousRow = 0.;
888 auto &wValues = QSQ->getSpecAxisValues();
889 std::vector<double> wWidths;
890 if (wValues.size() == 1) {
893 wWidths.push_back(1.);
895 std::vector<double> wBinEdges;
896 wBinEdges.reserve(wValues.size() + 1);
898 std::adjacent_difference(wBinEdges.begin(), wBinEdges.end(), std::back_inserter(wWidths));
899 wWidths.erase(wWidths.begin());
902 double wMax = fromWaveVector(kinc);
903 auto it = std::lower_bound(wValues.begin(), wValues.end(), wMax);
904 size_t iFirstInaccessibleW = std::distance(wValues.begin(), it);
905 auto nAccessibleWPoints = iFirstInaccessibleW;
908 std::vector<double> IOfQX, IOfQY;
910 IOfQYFull.reserve(nAccessibleWPoints);
911 qValuesFull.reserve(nAccessibleWPoints);
912 wIndices.reserve(nAccessibleWPoints);
914 for (
size_t iW = 0; iW < nAccessibleWPoints; iW++) {
915 auto kf =
getKf((wValues)[iW], kinc);
919 integrateCumulative(QSQ->histogram(iW), qmin, qmin + qrange, IOfQX, IOfQY, returnCumulative);
921 double wBinWidth = wWidths[iW];
922 std::transform(IOfQY.begin(), IOfQY.end(), IOfQY.begin(),
923 [IOfQMaxPreviousRow, wBinWidth](
double d) ->
double { return d * wBinWidth + IOfQMaxPreviousRow; });
924 IOfQMaxPreviousRow = IOfQY.back();
925 IOfQYFull.insert(IOfQYFull.end(), IOfQY.begin(), IOfQY.end());
926 qValuesFull.insert(qValuesFull.end(), IOfQX.begin(), IOfQX.end());
927 wIndices.insert(wIndices.end(), IOfQX.size(),
static_cast<double>(iW));
930 return {IOfQYFull, qValuesFull, wIndices};
942 for (
size_t iMat = 0; iMat < materialWorkspaces.size(); iMat++) {
943 auto QSQ = materialWorkspaces[iMat].QSQ;
944 auto [IOfQYFull, qValuesFull, wIndices] =
integrateQSQ(QSQ, kinc,
true);
945 auto IOfQYAtQMax = IOfQYFull.empty() ? 0. : IOfQYFull.back();
946 if (IOfQYAtQMax == 0.)
947 throw std::runtime_error(
"Integral of Q * S(Q) is zero so can't generate probability distribution");
949 std::vector<double> IOfQYNorm;
950 std::transform(IOfQYFull.begin(), IOfQYFull.end(), std::back_inserter(IOfQYNorm),
951 [IOfQYAtQMax](
double d) ->
double { return d / IOfQYAtQMax; });
954 auto &InvPOfQ = materialWorkspaces[iMat].InvPOfQ;
955 for (
size_t i = 0; i < InvPOfQ->getNumberHistograms(); i++) {
956 InvPOfQ->histogram(i).X.resize(IOfQYNorm.size());
957 InvPOfQ->histogram(i).X = IOfQYNorm;
959 InvPOfQ->histogram(0).Y.resize(qValuesFull.size());
960 InvPOfQ->histogram(0).Y = qValuesFull;
961 InvPOfQ->histogram(1).Y.resize(wIndices.size());
962 InvPOfQ->histogram(1).Y = wIndices;
969 for (
size_t i = 0; i < SOfQ->getNumberHistograms(); i++) {
970 auto &ySQ = SOfQ->histogram(i).Y;
972 std::transform(ySQ.begin(), ySQ.end(), ySQ.begin(), [](
double d) ->
double {
973 const double exp_that_gives_close_to_zero = -20.0;
975 return exp_that_gives_close_to_zero;
994 const std::vector<double> &specialKs) {
995 for (
auto &SQWSMapping : matWSs) {
996 std::vector<double> finalkValues, QSQIntegrals;
1000 double kMax = specialKs.back();
1001 std::vector<double> IOfQYFull, qValuesFull;
1002 std::tie(IOfQYFull, qValuesFull, std::ignore) =
integrateQSQ(SQWSMapping.QSQ, kMax,
true);
1003 for (
auto k : specialKs) {
1004 auto qUpperLimit = 2 * k;
1005 auto iterPrevIntegral = std::upper_bound(qValuesFull.begin(), qValuesFull.end(), qUpperLimit) - 1;
1006 auto idxPrevIntegral =
static_cast<size_t>(std::distance(qValuesFull.begin(), iterPrevIntegral));
1007 std::vector<double> ignoreVector, topUpIntegral;
1008 integrateCumulative(SQWSMapping.QSQ->histogram(0), *iterPrevIntegral, qUpperLimit, ignoreVector, topUpIntegral,
1010 double IOfQY = IOfQYFull[idxPrevIntegral] + topUpIntegral[0];
1012 double normalisedIntegral = IOfQY / (2 * k * k);
1013 finalkValues.push_back(k);
1014 QSQIntegrals.push_back(normalisedIntegral);
1020 std::set<double> kValues(specialKs.begin(), specialKs.end());
1021 const std::vector<double> &qValues = SQWSMapping.SQ->histogram(0).X;
1022 for (
auto q : qValues) {
1024 kValues.insert(q / 2);
1029 double maxSuppliedQ = qValues.back();
1030 if (maxSuppliedQ > 0.) {
1031 kValues.insert(maxSuppliedQ);
1032 kValues.insert(2 * maxSuppliedQ);
1035 for (
auto k : kValues) {
1036 std::vector<double> IOfQYFull;
1037 std::tie(IOfQYFull, std::ignore, std::ignore) =
integrateQSQ(SQWSMapping.QSQ, k,
false);
1038 auto IOfQYAtQMax = IOfQYFull.empty() ? 0. : IOfQYFull.back();
1041 if (IOfQYAtQMax > 0) {
1042 double normalisedIntegral = IOfQYAtQMax / (2 * k * k);
1043 finalkValues.push_back(k);
1044 QSQIntegrals.push_back(normalisedIntegral);
1048 auto QSQScaleFactor = std::make_shared<DiscusData1D>(finalkValues, QSQIntegrals);
1049 SQWSMapping.QSQScaleFactor = QSQScaleFactor;
1068 const double xmax, std::vector<double> &resultX,
1069 std::vector<double> &resultY,
1070 const bool returnCumulative) {
1071 assert(
h.X.size() ==
h.Y.size());
1072 const std::vector<double> &xValues =
h.X;
1073 const std::vector<double> &yValues =
h.Y;
1076 if (returnCumulative) {
1077 resultX.emplace_back(xmin);
1078 resultY.emplace_back(0.);
1083 if (xValues.front() > xmin)
1084 throw std::runtime_error(
"Distribution doesn't extend as far as lower integration limit, x=" +
1087 if (xValues.back() < xmax)
1088 throw std::runtime_error(
"Distribution doesn't extend as far as upper integration limit, x=" +
1091 auto iter = std::upper_bound(xValues.cbegin(), xValues.cend(), xmin);
1092 auto iRight =
static_cast<size_t>(std::distance(xValues.cbegin(), iter));
1094 auto linearInterp = [&xValues, &yValues](
const double x,
const size_t lIndex,
const size_t rIndex) ->
double {
1095 return (yValues[lIndex] * (xValues[rIndex] -
x) + yValues[rIndex] * (
x - xValues[lIndex])) /
1096 (xValues[rIndex] - xValues[lIndex]);
1101 if (xmin > xValues[iRight - 1]) {
1102 if (xmax >= xValues[iRight]) {
1103 double interpY = linearInterp(xmin, iRight - 1, iRight);
1104 yToUse = 0.5 * (interpY + yValues[iRight]);
1105 sum += yToUse * (xValues[iRight] - xmin);
1106 if (returnCumulative) {
1107 resultX.push_back(xValues[iRight]);
1108 resultY.push_back(sum);
1112 double interpY1 = linearInterp(xmin, iRight - 1, iRight);
1113 double interpY2 = linearInterp(xmax, iRight - 1, iRight);
1114 yToUse = 0.5 * (interpY1 + interpY2);
1115 sum += yToUse * (xmax - xmin);
1116 if (returnCumulative) {
1117 resultX.push_back(xmax);
1118 resultY.push_back(sum);
1125 for (; iRight < xValues.size() && xValues[iRight] <= xmax; iRight++) {
1126 yToUse = 0.5 * (yValues[iRight - 1] + yValues[iRight]);
1127 double xLeft = xValues[iRight - 1];
1128 double xRight = xValues[iRight];
1129 sum += yToUse * (xRight - xLeft);
1130 if (returnCumulative) {
1131 if (xRight > std::nextafter(xLeft, DBL_MAX)) {
1132 resultX.emplace_back(xRight);
1133 resultY.emplace_back(sum);
1139 if ((xmax > xValues[iRight - 1]) && (xmin <= xValues[iRight - 1])) {
1140 double interpY = linearInterp(xmax, iRight - 1, iRight);
1141 yToUse = 0.5 * (yValues[iRight - 1] + interpY);
1142 sum += yToUse * (xmax - xValues[iRight - 1]);
1143 if (returnCumulative) {
1144 resultX.emplace_back(xmax);
1145 resultY.emplace_back(sum);
1148 if (!returnCumulative) {
1149 resultX.emplace_back(xmax);
1150 resultY.emplace_back(sum);
1162 integrateAlgorithm->initialize();
1163 integrateAlgorithm->setProperty(
"InputWorkspace", ws);
1164 integrateAlgorithm->setProperty(
"OutputWorkspace",
"_");
1165 integrateAlgorithm->execute();
1167 for (
size_t i = 0; i < wsIntegrals->getNumberHistograms(); i++)
1168 wsIntegrals->setPoints(i, std::vector<double>{0.});
1183 bool specialSingleScatterCalc) {
1184 double scatteringXSection, absorbXsection;
1185 if (specialSingleScatterCalc) {
1188 const double wavelength = 2 * M_PI / k;
1197 const auto sig_total = scatteringXSection + absorbXsection;
1198 return {sig_total, scatteringXSection};
1208std::tuple<double, int>
1220 const auto &histx = histToInterpolate.
X;
1221 const auto &histy = histToInterpolate.
Y;
1222 assert(histToInterpolate.
X.size() == histToInterpolate.
Y.size());
1223 if (
x > histx.back()) {
1224 return histy.back();
1226 if (
x < histx.front()) {
1227 return histy.front();
1229 const auto iter = std::upper_bound(histx.cbegin(), histx.cend(),
x);
1230 const auto idx =
static_cast<size_t>(std::distance(histx.cbegin(), iter) - 1);
1231 const double x0 = histx[idx];
1232 const double x1 = histx[idx + 1];
1233 const double asq = (pow(histy[idx + 1], 2) - pow(histy[idx], 2)) / (x1 - x0);
1235 throw std::runtime_error(
"Cannot perform square root interpolation on supplied distribution");
1237 const double b = x0 - pow(histy[idx], 2) / asq;
1238 return sqrt(asq * (
x - b));
1248 auto &xHisto = histToInterpolate.
X;
1249 auto &yHisto = histToInterpolate.
Y;
1250 if (
x > xHisto.back()) {
1251 return yHisto.back();
1253 if (
x < xHisto.front()) {
1254 return yHisto.front();
1258 auto iter = std::upper_bound(xHisto.cbegin(), xHisto.cend(),
x);
1259 auto idx =
static_cast<size_t>(std::distance(xHisto.cbegin(), iter) - 1);
1273 assert(histToInterpolate.
X.size() == histToInterpolate.
Y.size());
1274 if (
x > histToInterpolate.
X.back()) {
1275 return exp(histToInterpolate.
Y.back());
1277 if (
x < histToInterpolate.
X.front()) {
1278 return exp(histToInterpolate.
Y.front());
1281 auto deltax = histToInterpolate.
X[1] - histToInterpolate.
X[0];
1283 auto iter = std::upper_bound(histToInterpolate.
X.cbegin(), histToInterpolate.
X.cend(),
x);
1284 auto idx =
static_cast<size_t>(std::distance(histToInterpolate.
X.cbegin(), iter) - 1);
1288 auto ny = histToInterpolate.
Y.size();
1290 throw std::runtime_error(
"Need at least 3 y values to perform quadratic interpolation");
1297 const auto U = (
x - histToInterpolate.
X[idx]) / deltax;
1298 const auto &
y = histToInterpolate.
Y;
1299 const auto A = (
y[idx] - 2 *
y[idx + 1] +
y[idx + 2]) / 2;
1300 const auto B = (-3 *
y[idx] + 4 *
y[idx + 1] -
y[idx + 2]) / 2;
1301 const auto C =
y[idx];
1302 return exp(A * U * U + B * U + C);
1318 auto &wValues = SQWSMapping.
SQ->getSpecAxisValues();
1319 if (wValues.size() == 1) {
1321 if (w == (wValues)[0])
1327 }
catch (std::out_of_range &) {
1364 bool specialSingleScatterCalc, const
Mantid::Geometry::
DetectorInfo &detectorInfo, const
size_t &histogramIndex) {
1366 std::vector<int> countZeroWeights(wValues.size(), 0);
1367 std::vector<double> sumOfWeights(wValues.size(), 0.);
1368 std::vector<double> weightsMeans(wValues.size(), 0.), deltas(wValues.size(), 0.), weightsM2(wValues.size(), 0.),
1369 weightsErrors(wValues.size(), 0.);
1371 for (
int ie = 0; ie < nPaths; ie++) {
1372 auto [success, weights] = scatter(nScatters, rng, componentWorkspaces, kinc, wValues, specialSingleScatterCalc,
1373 detectorInfo, histogramIndex);
1375 std::transform(weights.begin(), weights.end(), sumOfWeights.begin(), sumOfWeights.begin(), std::plus<double>());
1376 std::transform(weights.begin(), weights.end(), countZeroWeights.begin(), countZeroWeights.begin(),
1377 [](
double d,
int count) { return d > 0. ? count : count + 1; });
1380 for (
size_t i = 0; i < wValues.size(); i++) {
1381 deltas[i] = weights[i] - weightsMeans[i];
1382 weightsMeans[i] += deltas[i] /
static_cast<double>(ie + 1);
1383 weightsM2[i] += deltas[i] * (weights[i] - weightsMeans[i]);
1386 weightsErrors[i] = sqrt(weightsM2[i] /
static_cast<double>(ie));
1392 for (
size_t i = 0; i < wValues.size(); i++) {
1393 sumOfWeights[i] = sumOfWeights[i] / nPaths;
1394 weightsErrors[i] = weightsErrors[i] / sqrt(nPaths);
1397 return {sumOfWeights, weightsErrors};
1423 bool specialSingleScatterCalc, const
Mantid::Geometry::
DetectorInfo &detectorInfo, const
size_t &histogramIndex) {
1427 auto track = start_point(rng);
1428 auto shapeObjectWithScatter =
1429 updateWeightAndPosition(track, weight, kinc, rng, specialSingleScatterCalc, componentWorkspaces);
1430 double scatteringXSection;
1431 std::tie(std::ignore, scatteringXSection) =
1432 new_vector(shapeObjectWithScatter->material(), kinc, specialSingleScatterCalc);
1434 auto currentComponentWorkspaces = componentWorkspaces;
1436 for (
int iScat = 0; iScat < nScatters - 1; iScat++) {
1438 if (m_importanceSampling) {
1439 auto newComponentWorkspaces = componentWorkspaces;
1440 for (
auto &SQWSMapping : currentComponentWorkspaces)
1441 SQWSMapping.InvPOfQ = SQWSMapping.InvPOfQ->createCopy();
1442 prepareCumulativeProbForQ(k, newComponentWorkspaces);
1443 currentComponentWorkspaces = std::move(newComponentWorkspaces);
1446 auto trackStillAlive =
1447 q_dir(track, shapeObjectWithScatter, currentComponentWorkspaces, k, scatteringXSection, rng, weight);
1448 if (!trackStillAlive)
1449 return {
true, std::vector<double>(wValues.size(), 0.)};
1450 int nlinks = m_sampleShape->interceptSurface(track);
1452 nlinks += m_env->interceptSurfaces(track);
1453 m_callsToInterceptSurface += m_env->nelements();
1455 m_callsToInterceptSurface++;
1457 return {
false, {0.}};
1459 shapeObjectWithScatter =
1460 updateWeightAndPosition(track, weight, k, rng, specialSingleScatterCalc, componentWorkspaces);
1461 std::tie(std::ignore, scatteringXSection) =
1462 new_vector(shapeObjectWithScatter->material(), k, specialSingleScatterCalc);
1465 bool considerCollimator = getProperty(
"RadialCollimator");
1466 if (considerCollimator) {
1467 const auto &samplePos = detectorInfo.samplePosition();
1468 auto hexahedron = createCollimatorHexahedronShape(samplePos, detectorInfo, histogramIndex);
1471 if ((!hexahedron) || (!hexahedron->isValid(track.startPoint())))
1472 return {
true, std::vector<double>(wValues.size(), 0.)};
1475 const auto &detPos = detectorInfo.position(histogramIndex);
1476 Kernel::V3D directionToDetector = detPos - track.startPoint();
1479 track.reset(track.startPoint(), directionToDetector);
1480 int nlinks = m_sampleShape->interceptSurface(track);
1481 m_callsToInterceptSurface++;
1483 nlinks += m_env->interceptSurfaces(track);
1484 m_callsToInterceptSurface += m_env->nelements();
1490 return {
false, {0.}};
1492 std::vector<double> weights;
1493 auto scatteringXSectionFull = shapeObjectWithScatter->material().totalScatterXSection();
1501 for (
auto &w : wValues) {
1502 const double finalE = fromWaveVector(kinc) - w;
1504 const double kout = toWaveVector(finalE);
1505 const auto qVector = directionToDetector * kout - prevDirection * k;
1506 const double q = qVector.
norm();
1507 const double finalW = fromWaveVector(k) - finalE;
1508 auto componentWSIt = findMatchingComponent(componentWorkspaces, shapeObjectWithScatter);
1509 auto &componentWSMapping = *componentWSIt;
1510 double SQ = Interpolate2D(componentWSMapping, q, finalW);
1511 scatteringXSection = m_NormalizeSQ ? scatteringXSection / interpolateFlat(*(componentWSMapping.QSQScaleFactor), k)
1512 : scatteringXSectionFull;
1515 for (
auto it = track.cbegin(); it != track.cend(); it++) {
1517 auto &materialPassingThrough = it->object->material();
1518 std::tie(sigma_total, std::ignore) = new_vector(materialPassingThrough, kout, specialSingleScatterCalc);
1519 double numberDensity = materialPassingThrough.numberDensityEffective();
1520 double vmu = 100 * numberDensity * sigma_total;
1521 if (specialSingleScatterCalc)
1523 const double dl = it->distInsideObject;
1524 AT2 *= exp(-dl * vmu);
1526 weights.emplace_back(weight * AT2 * SQ * scatteringXSection / (4 * M_PI));
1528 weights.emplace_back(0.);
1531 return {
true, weights};
1540 const auto shape = detectorInfo.
detector(histogramIndex).
shape();
1547 }
catch (std::exception &) {
1552 if (colCorridorShape) {
1553 return colCorridorShape;
1556 const auto &detectorId = detectorInfo.
detector(histogramIndex).
getID();
1557 const auto &detectorAbsolPos = detectorInfo.
position(detectorInfo.
indexOf(detectorId));
1558 const auto cuboidGeometry = shape->shapeInfo().cuboidGeometry();
1561 const auto &detLeftFrontBottomPos = detectorAbsolPos + cuboidGeometry.leftFrontBottom;
1562 const auto &detLeftFrontTopPos = detectorAbsolPos + cuboidGeometry.leftFrontTop;
1563 const auto &detRightFrontBottomPos = detectorAbsolPos + cuboidGeometry.rightFrontBottom;
1564 const auto detRightFrontTopPos = detLeftFrontTopPos + detRightFrontBottomPos - detLeftFrontBottomPos;
1566 const auto detCentrePos =
1567 (detLeftFrontBottomPos + detLeftFrontTopPos + detRightFrontBottomPos + detRightFrontTopPos) / 4.0;
1568 const auto detTopMiddlePos = (detLeftFrontTopPos + detRightFrontTopPos) / 2.0;
1571 if ((detRightFrontTopPos == detLeftFrontTopPos) || (detTopMiddlePos == detCentrePos) || (detCentrePos == samplePos)) {
1578 V3D(-1.0 * unitVecSampleToDet.Z(), 0,
1579 -1.0 * unitVecSampleToDet.X()));
1581 const auto colOpenningLeftTopPos =
1585 const auto colOpenningRightTopPos =
1589 const auto colOpenningLeftBottomPos =
1593 const auto colOpenningRightBottomPos =
1599 if ((colOpenningLeftTopPos == detLeftFrontTopPos) || (colOpenningLeftBottomPos == detLeftFrontBottomPos) ||
1600 (colOpenningRightTopPos == detRightFrontTopPos) || (colOpenningRightBottomPos == detRightFrontBottomPos)) {
1605 const auto unitVecAlongLeftTopLeg =
Kernel::normalize(colOpenningLeftTopPos - detLeftFrontTopPos);
1606 const auto unitVecAlongLeftBottomLeg =
Kernel::normalize(colOpenningLeftBottomPos - detLeftFrontBottomPos);
1607 const auto unitVecAlongRightTopLeg =
Kernel::normalize(colOpenningRightTopPos - detRightFrontTopPos);
1608 const auto unitVecAlongRightBottomLeg =
Kernel::normalize(colOpenningRightBottomPos - detRightFrontBottomPos);
1612 double sampleCentreToDetDistance = (detCentrePos - samplePos).norm();
1613 const auto leftFrontBottomPoint = detRightFrontTopPos + unitVecAlongRightTopLeg * sampleCentreToDetDistance * 2.0;
1614 const auto hexaHedronLegsLenRatio =
1615 (leftFrontBottomPoint - detRightFrontTopPos).norm() / (colOpenningRightTopPos - detRightFrontTopPos).norm();
1616 const auto leftBackBottomPoint = detLeftFrontTopPos + unitVecAlongLeftTopLeg * hexaHedronLegsLenRatio *
1617 (colOpenningLeftTopPos - detLeftFrontTopPos).norm();
1618 const auto rightFrontBottomPoint =
1619 detRightFrontBottomPos +
1620 unitVecAlongRightBottomLeg * hexaHedronLegsLenRatio * (colOpenningRightBottomPos - detRightFrontBottomPos).norm();
1621 const auto rightBackBottomPoint =
1622 detLeftFrontBottomPos +
1623 unitVecAlongLeftBottomLeg * hexaHedronLegsLenRatio * (colOpenningLeftBottomPos - detLeftFrontBottomPos).norm();
1624 std::ostringstream xmlShapeStream;
1625 xmlShapeStream <<
"<hexahedron id=\"corridor-shape\" >"
1626 <<
"<left-back-bottom-point x=\"" << leftBackBottomPoint.X() <<
"\""
1627 <<
" y=\"" << leftBackBottomPoint.Y() <<
"\""
1628 <<
" z=\"" << leftBackBottomPoint.Z() <<
"\" />"
1629 <<
"<left-front-bottom-point x=\"" << leftFrontBottomPoint.X() <<
"\""
1630 <<
" y=\"" << leftFrontBottomPoint.Y() <<
"\""
1631 <<
" z=\"" << leftFrontBottomPoint.Z() <<
"\" />"
1632 <<
"<right-front-bottom-point x=\"" << rightFrontBottomPoint.X() <<
"\""
1633 <<
" y=\"" << rightFrontBottomPoint.Y() <<
"\""
1634 <<
" z=\"" << rightFrontBottomPoint.Z() <<
"\" />"
1635 <<
"<right-back-bottom-point x=\"" << rightBackBottomPoint.X() <<
"\""
1636 <<
" y=\"" << rightBackBottomPoint.Y() <<
"\""
1637 <<
" z=\"" << rightBackBottomPoint.Z() <<
"\" />"
1638 <<
"<left-back-top-point x=\"" << detLeftFrontTopPos.X() <<
"\""
1639 <<
" y=\"" << detLeftFrontTopPos.Y() <<
"\""
1640 <<
" z=\"" << detLeftFrontTopPos.Z() <<
"\" />"
1641 <<
"<left-front-top-point x=\"" << detRightFrontTopPos.X() <<
"\""
1642 <<
" y=\"" << detRightFrontTopPos.Y() <<
"\""
1643 <<
" z=\"" << detRightFrontTopPos.Z() <<
"\" />"
1644 <<
"<right-front-top-point x=\"" << detRightFrontBottomPos.X() <<
"\""
1645 <<
" y=\"" << detRightFrontBottomPos.Y() <<
"\""
1646 <<
" z=\"" << detRightFrontBottomPos.Z() <<
"\" />"
1647 <<
"<right-back-top-point x=\"" << detLeftFrontBottomPos.X() <<
"\""
1648 <<
" y=\"" << detLeftFrontBottomPos.Y() <<
"\""
1649 <<
" z=\"" << detLeftFrontBottomPos.Z() <<
"\" />"
1652 const auto collimatorCorridorCsgObj = shapeMaker.
createShape(xmlShapeStream.str());
1654 return collimatorCorridorCsgObj;
1657const std::shared_ptr<Geometry::CSGObject>
1662 return itCollimatorCorridor->second;
1668 const std::size_t &histogramIndex,
const std::shared_ptr<Geometry::CSGObject> &collimatorCorridorCsgObj) {
1675 const bool radialCollimator =
getProperty(
"RadialCollimator");
1676 if (radialCollimator) {
1690 throw std::runtime_error(
"Cannot find parameter:" + paramName +
" from instrument parameter file");
1692 std::vector<double> val_vec =
m_instrument->getNumberParameter(paramName,
true);
1693 if (val_vec.empty()) {
1694 throw std::runtime_error(
"No value specified for:" + paramName +
" in the instrument parameter file");
1697 return static_cast<double>(val_vec.front());
1702 throw std::runtime_error(
"Cannot find parameter:" + paramName +
" from instrument parameter file");
1705 std::string paramValStr =
m_instrument->getStringParameter(paramName)[0];
1706 std::vector<std::string> v3dStrComponent;
1707 boost::split(v3dStrComponent, paramValStr, boost::is_any_of(
","));
1708 if (v3dStrComponent.size() != 3) {
1709 throw std::runtime_error(
"Invalid number of coordinates given for parameter:" + paramName +
1710 " in instrument parameter file");
1712 std::vector<double> v3dComponents(3);
1713 std::transform(v3dStrComponent.begin(), v3dStrComponent.end(), v3dComponents.begin(),
1714 [](
const std::string &str) ->
double { return std::stod(str); });
1716 return Kernel::V3D(v3dComponents[0], v3dComponents[1], v3dComponents[2]);
1727 kf = toWaveVector(fromWaveVector(kinc) - deltaE);
1728 assert(!std::isnan(kf));
1746 const double qmin = abs(kf - ki);
1747 const double qrange = 2 * std::min(ki, kf);
1748 return {qmin, qrange};
1757std::tuple<double, double, int, double>
1775 if (wValues.size() == 1) {
1779 std::vector<double> wBinEdges;
1780 wBinEdges.reserve(wValues.size() + 1);
1783 wRange = wBinEdges.back() - wBinEdges.front();
1784 double w = wBinEdges.front() + rng.
nextValue() * wRange;
1787 double maxkf = toWaveVector(fromWaveVector(kinc) - wValues.front());
1788 double qRange = kinc + maxkf;
1790 return {q, qRange, iW, wRange};
1819 const double scatteringXSection,
1821 const double kinc = k;
1827 k =
getKf(componentWSIt->SQ->getSpecAxisValues()[iW], kinc);
1828 weight = weight * scatteringXSection;
1830 double qrange, wRange;
1831 auto &wValues = componentWSIt->SQ->getSpecAxisValues();
1832 std::tie(QQ, qrange, iW, wRange) =
sampleQWUniform(wValues, rng, kinc);
1835 if (fromWaveVector(kinc) - wValues[iW] <= 0)
1837 k =
getKf(wValues[iW], kinc);
1840 weight = weight * scatteringXSection * SQ * QQ * qrange * wRange;
1842 double integralQSQ =
getQSQIntegral(*componentWSIt->QSQScaleFactor, kinc);
1843 assert(integralQSQ != 0.);
1844 weight = weight / integralQSQ;
1849 const double cosT = (kinc * kinc + k * k - QQ * QQ) / (2 * kinc * k);
1851 if (std::abs(cosT) > 1.0)
1866 const auto B3 = sqrt(1 - cosT * cosT);
1867 const auto B2 = cosT;
1886 double UKX, UKY, UKZ;
1887 if (vz * vz < 1.0) {
1890 auto A2 = sqrt(vx * vx + vy * vy);
1891 auto UQTZ = cos(phi) * A2;
1892 auto UQTX = -cos(phi) * vz * vx / A2 + sin(phi) * vy / A2;
1893 auto UQTY = -cos(phi) * vz * vy / A2 - sin(phi) * vx / A2;
1894 UKX = B2 * vx + B3 * UQTX;
1895 UKY = B2 * vy + B3 * UQTY;
1896 UKZ = B2 * vz + B3 * UQTZ;
1900 UKX = B3 * cos(phi);
1901 UKY = B3 * sin(phi);
1925 if (
g_log.
is(Kernel::Logger::Priority::PRIO_WARNING)) {
1932 throw std::runtime_error(
1933 "DiscusMultipleScatteringCorrection::start_point() - Unable to generate entry point into sample after " +
1951 double totalMuL = 0.;
1952 auto nlinks = track.
count();
1954 boost::container::small_vector<std::tuple<const Geometry::IObject *, double, double, double>, 5> geometryObjects;
1955 geometryObjects.reserve(nlinks);
1957 for (
auto it = track.
cbegin(); it != track.
cend(); it++) {
1958 const double trackSegLength = it->distInsideObject;
1959 const auto geometryObj = it->object;
1961 std::tie(sigma_total, std::ignore) =
new_vector(geometryObj->material(), k, specialSingleScatterCalc);
1962 double vmu = 100 * geometryObj->material().numberDensityEffective() * sigma_total;
1963 double muL = trackSegLength * vmu;
1966 geometryObjects.emplace_back(geometryObj, vmu, muL, sigma_total);
1970 double b4Overall = (1.0 - exp(-totalMuL));
1971 double muL = -log(1 - rng.
nextValue() * b4Overall);
1973 double newWeight = 0.;
1974 double prevExpTerms = 1.;
1975 std::tuple<const Geometry::IObject *, double, double, double> geometryObjectDetails;
1976 for (
size_t i = 0; i < geometryObjects.size(); i++) {
1977 geometryObjectDetails = geometryObjects[i];
1978 auto muL_i = std::get<2>(geometryObjectDetails);
1979 auto vmu_i = std::get<1>(geometryObjectDetails);
1980 if (muL - muL_i > 0) {
1981 vl += muL_i / vmu_i;
1983 prevExpTerms *= exp(-muL_i);
1986 double b4 = (1.0 - exp(-muL_i)) * prevExpTerms;
1987 auto sigma_total = std::get<3>(geometryObjectDetails);
1988 newWeight = b4 / sigma_total;
1992 weight = weight * newWeight;
1997 auto geometryObject = std::get<0>(geometryObjectDetails);
1998 if (
g_log.
is(Kernel::Logger::Priority::PRIO_DEBUG)) {
2000 (*(componentIt->scatterCount))++;
2002 return geometryObject;
2016 auto ptx = neutron.startPos.X();
2017 auto pty = neutron.startPos.Y();
2020 ptOnBeamProfile[
m_refframe->pointingHorizontal()] = ptx;
2021 ptOnBeamProfile[
m_refframe->pointingUp()] = pty;
2024 toSample[
m_refframe->pointingAlongBeam()] = 1.;
2038 const auto x =
position[0] + vl * direction[0];
2039 const auto y =
position[1] + vl * direction[1];
2040 const auto z =
position[2] + vl * direction[2];
2041 const auto startPoint =
V3D(
x,
y,
z);
2055std::shared_ptr<SparseWorkspace>
2057 const size_t rows,
const size_t columns) {
2058 auto sparseWS = std::make_shared<SparseWorkspace>(modelWS, nXPoints, rows, columns);
2063 for (
auto &SQWSMapping : matWSs) {
2064 auto &QSQ = SQWSMapping.QSQ;
2065 size_t expectedMaxSize =
2066 std::accumulate(QSQ->histograms().cbegin(), QSQ->histograms().cend(),
static_cast<size_t>(0),
2067 [](
const size_t value,
const DiscusData1D &histo) { return value + histo.Y.size(); });
2068 auto ws = std::make_shared<DiscusData2D>(std::vector<DiscusData1D>(nhists),
nullptr);
2069 ws->histogram(0).X.reserve(expectedMaxSize);
2070 for (
size_t i = 0; i < nhists; i++)
2071 ws->histogram(i).Y.reserve(expectedMaxSize);
2072 SQWSMapping.InvPOfQ = ws;
2080 outputWS->setDistribution(
true);
2081 outputWS->setYUnit(
"");
2082 outputWS->setYUnitLabel(
"Scattered Weight");
2092 auto interpolationOpt = std::make_unique<InterpolationOption>();
2093 return interpolationOpt;
2100 const auto refFrame = targetWS.
getInstrument()->getReferenceFrame();
2102 for (int64_t i = 0; i < static_cast<decltype(i)>(spectrumInfo.size()); ++i) {
2104 if (spectrumInfo.hasDetectors(i) && !spectrumInfo.isMonitor(i)) {
2106 std::tie(lat, lon) = spectrumInfo.geographicalAngles(i);
2108 if (spatiallyInterpHisto.size() > 1) {
2109 auto targetHisto = targetWS.
histogram(i);
2110 interpOpt.
applyInPlace(spatiallyInterpHisto, targetHisto);
2113 targetWS.
mutableY(i) = spatiallyInterpHisto.y().front();
2128 bool noClash(
false);
2130 for (
int i = 0; !noClash; ++i) {
2131 std::string wsIndex;
2137 bool wsExists = AnalysisDataService::Instance().doesExist(wsName + wsIndex);
2153 API::AnalysisDataService::Instance().addOrReplace(wsName, ws);
2168 auto componentWSIt = std::find_if(componentWorkspaces.begin(), componentWorkspaces.end(),
2170 return SQWS.ComponentPtr.get() == shapeObjectWithScatter;
2172 assert(componentWSIt != componentWorkspaces.end());
2175 return &(*componentWSIt);
2181 m_env = &inputWS->sample().getEnvironment();
2182 }
catch (std::runtime_error &) {
#define DECLARE_ALGORITHM(classname)
double value
The value of the point.
std::map< DeltaEMode::Type, std::string > index
#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...
#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.
#define GNU_DIAG_OFF(x)
This is a collection of macros for turning compiler warnings off in a controlled manner.
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.
bool getAlwaysStoreInADS() const override
Returns true if we always store in the AnalysisDataService.
static bool isEmpty(const NumT toCheck)
checks that the value was not set by users, uses the value in empty double/int.
Stores numeric values that are assumed to be bin edge values.
This class is shared by a few Workspace types and holds information related to a particular experimen...
const SpectrumInfo & spectrumInfo() const
Return a reference to the SpectrumInfo object.
const Geometry::DetectorInfo & detectorInfo() const
Return a const reference to the DetectorInfo object.
Geometry::Instrument_const_sptr getInstrument() const
Returns the parameterized instrument.
specnum_t getSpectrumNo() const
Base MatrixWorkspace Abstract Class.
virtual ISpectrum & getSpectrum(const size_t index)=0
Return the underlying ISpectrum ptr at the given workspace index.
HistogramData::Points points(const size_t index) const
virtual std::size_t getNumberHistograms() const =0
Returns the number of histograms in the workspace.
void setHistogram(const size_t index, T &&...data) &
HistogramData::Histogram histogram(const size_t index) const
Returns the Histogram at the given workspace index.
HistogramData::HistogramY & mutableY(const size_t index) &
Class to represent a numeric axis of a workspace.
virtual const std::vector< double > & getValues() const
Return a const reference to the values.
Helper class for reporting progress from algorithms.
A property class for workspaces.
static std::unique_ptr< IBeamProfile > createBeamProfile(const Geometry::Instrument &instrument, const API::Sample &sample)
std::vector< DiscusData1D > m_data
const std::vector< double > & getSpecAxisValues()
std::shared_ptr< std::vector< double > > m_specAxis
std::unique_ptr< DiscusData2D > createCopy(bool clearY=false)
Calculates a multiple scattering correction Based on Muscat Fortran code provided by Spencer Howells.
ComponentWorkspaceMappings m_SQWSs
void setWorkspaceName(const API::MatrixWorkspace_sptr &ws, std::string wsName)
Set the name on a workspace, adjusting for potential clashes in the ADS.
void addWorkspaceToDiscus2DData(const Geometry::IObject_const_sptr &shape, const std::string_view &matName, API::MatrixWorkspace_sptr ws)
Function to convert between a Matrix workspace and the internal simplified 2D data structure.
void convertWsBothAxesToPoints(API::MatrixWorkspace_sptr &ws)
Convert x axis of a workspace to points if it's bin edges.
void convertToLogWorkspace(const std::shared_ptr< DiscusData2D > &SOfQ)
Kernel::V3D getV3DParamFromIDF(std::string paramName)
std::map< int, int > m_attemptsToGenerateInitialTrack
bool m_importanceSampling
void createInvPOfQWorkspaces(ComponentWorkspaceMappings &matWSs, size_t nhists)
std::tuple< double, double > getKinematicRange(double kf, double ki)
Get the range of q values accessible for a particular kinc and kf.
std::tuple< double, double, int, double > sampleQWUniform(const std::vector< double > &wValues, Kernel::PseudoRandomNumberGenerator &rng, const double kinc)
Sample the q and w value for a scattering event without importance sampling.
double getKf(const double deltaE, const double kinc)
void updateTrackDirection(Geometry::Track &track, const double cosT, const double phi)
Update the track's direction following a scatter event given theta and phi angles.
std::vector< std::tuple< double, int, double > > generateInputKOutputWList(const double efixed, const std::vector< double > &xPoints)
Generate a list of the k and w points where calculation results are required.
std::tuple< double, double > new_vector(const Kernel::Material &material, double k, bool specialSingleScatterCalc)
Calculate a total cross section using a k-specific scattering cross section Note - a separate tabulat...
Kernel::DeltaEMode::Type m_EMode
void prepareStructureFactors()
std::map< std::size_t, std::shared_ptr< Geometry::CSGObject > > m_collimatorCorridorCache
Geometry::Track generateInitialTrack(Kernel::PseudoRandomNumberGenerator &rng)
Generate an initial track starting at the source and entering the sample/sample environment at a rand...
std::tuple< double, int > sampleQW(const std::shared_ptr< DiscusData2D > &CumulativeProb, double x)
Use importance sampling to choose a Q and w value for the scatter.
std::map< std::string, std::string > validateInputs() override
Validate the input properties.
API::MatrixWorkspace_sptr integrateWS(const API::MatrixWorkspace_sptr &ws)
Create new workspace with y equal to integral across the bins.
const Geometry::IObject * updateWeightAndPosition(Geometry::Track &track, double &weight, const double k, Kernel::PseudoRandomNumberGenerator &rng, bool specialSingleScatterCalc, const ComponentWorkspaceMappings &componentWorkspaces)
update track start point and weight.
double getDoubleParamFromIDF(std::string paramName)
void integrateCumulative(const DiscusData1D &h, const double xmin, const double xmax, std::vector< double > &resultX, std::vector< double > &resultY, const bool returnCumulative)
Integrate a distribution between the supplied xmin and xmax values using trapezoid rule without any e...
bool m_simulateEnergiesIndependently
void calculateQSQIntegralAsFunctionOfK(ComponentWorkspaceMappings &matWSs, const std::vector< double > &specialKs)
This is a generalised version of the normalisation done in the original Discus algorithm The original...
API::MatrixWorkspace_sptr createOutputWorkspace(const API::MatrixWorkspace &inputWS) const
virtual std::unique_ptr< InterpolationOption > createInterpolateOption()
Factory method to return an instance of the required InterpolationOption class.
double interpolateSquareRoot(const DiscusData1D &histToInterpolate, double x)
Interpolate function of the form y = a * sqrt(x - b) ie inverse of a quadratic Used to lookup value i...
std::shared_mutex m_mutexCorridorCache
bool q_dir(Geometry::Track &track, const Geometry::IObject *shapePtr, const ComponentWorkspaceMappings &invPOfQs, double &k, const double scatteringXSection, Kernel::PseudoRandomNumberGenerator &rng, double &weight)
Update track direction and weight as a result of a scatter.
void writeToCollimatorCorridorCache(const std::size_t &histogramIndex, const std::shared_ptr< Geometry::CSGObject > &collimatorCorridorCsgObj)
void prepareSampleBeamGeometry(const API::MatrixWorkspace_sptr &inputWS)
virtual std::shared_ptr< SparseWorkspace > createSparseWorkspace(const API::MatrixWorkspace &modelWS, const size_t nXPoints, const size_t rows, const size_t columns)
Factory method to return an instance of the required SparseInstrument class.
const std::shared_ptr< Geometry::CSGObject > readFromCollimatorCorridorCache(const std::size_t &histogramIndex)
void exec() override
Execution code.
void correctForWorkspaceNameClash(std::string &wsName)
Adjust workspace name in case of clash in the ADS.
const std::shared_ptr< Geometry::CSGObject > createCollimatorHexahedronShape(const Kernel::V3D &samplePos, const Mantid::Geometry::DetectorInfo &detectorInfo, const size_t &histogramIndex)
const ComponentWorkspaceMapping * findMatchingComponent(const ComponentWorkspaceMappings &componentWorkspaces, const Geometry::IObject *shapeObjectWithScatter)
Lookup a sample or sample environment component in the supplied list.
double interpolateGaussian(const DiscusData1D &histToInterpolate, double x)
Interpolate a value from a spectrum containing Gaussian peaks.
long long m_callsToInterceptSurface
std::shared_ptr< const DiscusData1D > m_sigmaSS
void loadCollimatorInfo()
int m_maxScatterPtAttempts
Geometry::IObject_const_sptr m_sampleShape
double Interpolate2D(const ComponentWorkspaceMapping &SQWSMapping, double q, double w)
Interpolate value on S(Q,w) surface given a Q and w.
Geometry::Track start_point(Kernel::PseudoRandomNumberGenerator &rng)
Repeatedly attempt to generate an initial track starting at the source and entering the sample at a r...
void inc_xyz(Geometry::Track &track, double vl)
Update the x, y, z position of the neutron (or dV volume element to integrate over).
std::tuple< std::vector< double >, std::vector< double > > simulatePaths(const int nEvents, const int nScatters, Kernel::PseudoRandomNumberGenerator &rng, const ComponentWorkspaceMappings &componentWorkspaces, const double kinc, const std::vector< double > &wValues, bool specialSingleScatterCalc, const Mantid::Geometry::DetectorInfo &detectorInfo, const size_t &histogramIndex)
Simulates a set of neutron paths through the sample to a specific detector position with each path co...
long long m_IkCalculations
std::unique_ptr< CollimatorInfo > m_collimatorInfo
std::unique_ptr< IBeamProfile > m_beamProfile
void prepareCumulativeProbForQ(double kinc, const ComponentWorkspaceMappings &PInvOfQs)
Calculate a cumulative probability distribution for use in importance sampling.
boost::container::small_vector< ComponentWorkspaceMapping, 5 > ComponentWorkspaceMappings
double getQSQIntegral(const DiscusData1D &QSQScaleFactor, double k)
This is a generalised version of the normalisation done in the original Discus algorithm The original...
void interpolateFromSparse(API::MatrixWorkspace &targetWS, const SparseWorkspace &sparseWS, const Mantid::Algorithms::InterpolationOption &interpOpt)
std::shared_ptr< const Geometry::ReferenceFrame > m_refframe
void prepareQSQ(double kinc)
Prepare a profile of Q*S(Q) that will later be used to calculate a cumulative probability distributio...
const Geometry::SampleEnvironment * m_env
Geometry::BoundingBox m_activeRegion
void getXMinMax(const Mantid::API::MatrixWorkspace &ws, double &xmin, double &xmax) const
This is a variation on the function MatrixWorkspace::getXMinMax with some additional logic eg if x va...
std::tuple< std::vector< double >, std::vector< double >, std::vector< double > > integrateQSQ(const std::shared_ptr< DiscusData2D > &QSQ, double kinc, const bool returnCumulative)
Integrate QSQ over Q and w over the kinematic range accessible for a given kinc.
double interpolateFlat(const DiscusData1D &histToInterpolate, double x)
Interpolate function using flat interpolation from previous point.
Mantid::Geometry::Instrument_const_sptr m_instrument
Class to provide a consistent interface to an interpolation option on algorithms.
std::string validateInputSize(const size_t size) const
Validate the size of input histogram.
void applyInPlace(const HistogramData::Histogram &in, HistogramData::Histogram &out) const
Apply the interpolation method to the output histogram.
void set(const Value &kind, const bool calculateErrors, const bool independentErrors)
Set the interpolation option.
void applyInplace(HistogramData::Histogram &inOut, size_t stepSize) const
Apply the interpolation method to the given histogram.
Defines functions and utilities to create and deal with sparse instruments.
virtual HistogramData::Histogram bilinearInterpolateFromDetectorGrid(const double lat, const double lon) const
Spatially interpolate a single histogram from nearby detectors using bilinear interpolation method.
Concrete workspace implementation.
void grow(const BoundingBox &other)
Grow the bounding box so that it also encompasses the given box.
const IObject_sptr getShapePtr() const
const Kernel::Material & material() const override
Geometry::DetectorInfo is an intermediate step towards a DetectorInfo that is part of Instrument-2....
Kernel::V3D position(const size_t index) const
Returns the position of the detector with given index.
const Geometry::IDetector & detector(const size_t index) const
Return a const reference to the detector with given index.
size_t indexOf(const detid_t id) const
Returns the index of the detector with the given detector ID.
virtual detid_t getID() const =0
Get the detector ID.
virtual const std::shared_ptr< const IObject > shape() const =0
Returns the shape of the Object.
IObject : Interface for geometry objects.
virtual const Kernel::Material & material() const =0
const Container & getContainer() const
const IObject & getComponent(const size_t index) const
Returns the requested IObject.
Geometry::BoundingBox boundingBox() const
int interceptSurfaces(Track &track) const
Update the given track with intersections within the environment.
const IObject_const_sptr getComponentPtr(const size_t index) const
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...
Defines a track as a start point and a direction.
LType::reference front()
Returns a reference to the first link.
const Kernel::V3D & startPoint() const
Returns the starting point.
void clearIntersectionResults()
Clear the current set of intersection results.
int count() const
Returns the number of links.
const Kernel::V3D & direction() const
Returns the direction as a unit vector.
LType::const_iterator cbegin() const
Returns an interator to the start of the set of links (const version)
LType::const_iterator cend() const
Returns an interator to one-past-the-end of the set of links (const version)
void reset(const Kernel::V3D &startPoint, const Kernel::V3D &direction)
Set a starting point and direction.
EqualBinsChecker : Checks for evenly spaced bins.
virtual std::string validate() const
Perform validation of the given X array.
IPropertyManager * setProperty(const std::string &name, const T &value)
Templated method to set the value of a PropertyWithValue.
void warning(const std::string &msg)
Logs at warning level.
bool is(int level) const
Returns true if at least the given log level is set.
void information(const std::string &msg)
Logs at information level.
A material is defined as being composed of a given element, defined as a PhysicalConstants::NeutronAt...
double absorbXSection(const double lambda=PhysicalConstants::NeutronAtom::ReferenceLambda) const
Get the absorption cross section at a given wavelength in barns.
const std::string & name() const
Returns the name of the material.
double totalScatterXSection() const
Return the total scattering cross section for a given wavelength in barns.
This implements the Mersenne Twister 19937 pseudo-random number generator algorithm as a specialzatio...
void report()
Increments the loop counter by 1, then sends the progress notification on behalf of its algorithm.
void setNotifyStep(double notifyStepPct)
Override the frequency at which notifications are sent out.
Defines a 1D pseudo-random number generator, i.e.
virtual double nextValue()=0
Return the next double in the sequence.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
double normalize()
Make a normalized vector (return norm value)
double norm() const noexcept
std::unique_ptr< MatrixWorkspace > MatrixWorkspace_uptr
unique pointer to Mantid::API::MatrixWorkspace
std::shared_ptr< Workspace > Workspace_sptr
shared pointer to Mantid::API::Workspace
std::shared_ptr< MatrixWorkspace > MatrixWorkspace_sptr
shared pointer to the matrix workspace base class
std::shared_ptr< SparseWorkspace > SparseWorkspace_sptr
std::shared_ptr< const IComponent > IComponent_const_sptr
Typdef of a shared pointer to a const IComponent.
std::shared_ptr< const IObject > IObject_const_sptr
Typdef for a shared pointer to a const object.
void MANTID_KERNEL_DLL convertToBinCentre(const std::vector< double > &bin_edges, std::vector< double > &bin_centres)
Convert an array of bin boundaries to bin center values.
void MANTID_KERNEL_DLL convertToBinBoundary(const std::vector< double > &bin_centers, std::vector< double > &bin_edges)
Convert an array of bin centers to bin boundary values.
int MANTID_KERNEL_DLL indexOfValueFromCentersNoThrow(const std::vector< double > &bin_centers, const double value)
Gets the bin of a value from a vector of bin centers and returns -1 if out of range.
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.
MANTID_KERNEL_DLL V3D normalize(V3D v)
Normalizes a V3D.
A namespace containing physical constants that are required by algorithms and unit routines.
static constexpr double E_mev_toNeutronWavenumberSq
Transformation coefficient to transform neutron energy into neutron wavevector: K-neutron[m^-10] = sq...
static constexpr double h
Planck constant in J*s.
Helper class which provides the Collimation Length for SANS instruments.
constexpr int EMPTY_INT() noexcept
Returns what we consider an "empty" integer within a property.
int32_t detid_t
Typedef for a detector ID.
std::vector< double > MantidVec
typedef for the data storage used in Mantid matrix workspaces
int32_t specnum_t
Typedef for a spectrum Number.
std::string to_string(const wide_integer< Bits, Signed > &n)
std::shared_ptr< DiscusData2D > SQ
std::shared_ptr< DiscusData2D > logSQ
static std::string asString(const Type mode)
Return a string representation of the given mode.
Type
Define the available energy transfer modes It is important to assign enums proper numbers,...
@ Input
An input workspace.
@ Output
An output workspace.