40constexpr int DEFAULT_NPATHS = 1000;
41constexpr int DEFAULT_SEED = 123456789;
42constexpr int DEFAULT_NSCATTERINGS = 2;
43constexpr int DEFAULT_LATITUDINAL_DETS = 5;
44constexpr int DEFAULT_LONGITUDINAL_DETS = 10;
52inline double fromWaveVector(
double wavevector) {
56struct EFixedProvider {
57 explicit EFixedProvider(
const ExperimentInfo &expt) : m_expt(expt), m_emode(expt.getEMode()), m_EFixed(0.0) {
59 m_EFixed = m_expt.getEFixed();
67 return m_expt.getEFixed(detID);
80 auto data2DNew = std::make_unique<DiscusData2D>();
81 data2DNew->m_data.resize(
m_data.size());
82 for (
size_t i = 0; i <
m_data.size(); i++) {
83 data2DNew->m_data[i].X =
m_data[i].X;
84 data2DNew->m_data[i].Y = clearY ? std::vector<double>(
m_data[i].
Y.size(), 0.) :
m_data[i].Y;
92 throw std::runtime_error(
"DiscusData2D::getSpecAxisValues - No spec axis has been defined.");
104 auto wsValidator = std::make_shared<InstrumentValidator>();
108 "The name of the input workspace. The input workspace must have X units of Momentum (k) for elastic "
109 "calculations and units of energy transfer (DeltaE) for inelastic calculations. This is used to "
110 "supply the sample details, the detector positions and the x axis range to calculate corrections for");
113 "The name of the workspace containing S'(q) or S'(q, w). For elastic calculations, the input "
114 "workspace must contain a single spectrum and have X units of momentum transfer. A workspace group "
115 "containing one workspace per component can also be supplied if a calculation is being run on a "
116 "workspace with a sample environment specified");
118 "Name for the WorkspaceGroup that will be created. Each workspace in the "
119 "group contains a calculated weight for a particular number of "
120 "scattering events. The number of scattering events varies from 1 up to "
121 "the number supplied in the NumberOfScatterings parameter. The group "
122 "will also include an additional workspace for a calculation with a "
123 "single scattering event where the absorption post scattering has been "
125 auto wsKValidator = std::make_shared<WorkspaceUnitValidator>(
"Momentum");
128 "A workspace containing the scattering cross section as a function of k, :math:`\\sigma_s(k)`. Note "
129 "- this parameter would normally be left empty which results in the tabulated cross section data "
130 "being used instead which implies no wavelength dependence");
132 auto positiveInt = std::make_shared<Kernel::BoundedValidator<int>>();
133 positiveInt->setLower(1);
134 declareProperty(
"NumberOfSimulationPoints",
EMPTY_INT(), positiveInt,
135 "The number of points on the input workspace x axis for which a simulation is attempted");
137 declareProperty(
"NeutronPathsSingle", DEFAULT_NPATHS, positiveInt,
138 "The number of \"neutron\" paths to generate for single scattering");
139 declareProperty(
"NeutronPathsMultiple", DEFAULT_NPATHS, positiveInt,
140 "The number of \"neutron\" paths to generate for multiple scattering");
141 declareProperty(
"SeedValue", DEFAULT_SEED, positiveInt,
"Seed the random number generator with this value");
142 auto nScatteringsValidator = std::make_shared<Kernel::BoundedValidator<int>>();
143 nScatteringsValidator->setLower(1);
144 nScatteringsValidator->setUpper(5);
145 declareProperty(
"NumberScatterings", DEFAULT_NSCATTERINGS, nScatteringsValidator,
"Number of scatterings");
147 auto interpolateOpt = createInterpolateOption();
148 declareProperty(interpolateOpt->property(), interpolateOpt->propertyDoc());
149 declareProperty(
"SparseInstrument",
false,
150 "Enable simulation on special "
151 "instrument with a sparse grid of "
152 "detectors interpolating the "
153 "results to the real instrument.");
154 auto threeOrMore = std::make_shared<Kernel::BoundedValidator<int>>();
155 threeOrMore->setLower(3);
156 declareProperty(
"NumberOfDetectorRows", DEFAULT_LATITUDINAL_DETS, threeOrMore,
157 "Number of detector rows in the detector grid of the sparse instrument.");
158 setPropertySettings(
"NumberOfDetectorRows",
159 std::make_unique<EnabledWhenProperty>(
"SparseInstrument", ePropertyCriterion::IS_NOT_DEFAULT));
160 auto twoOrMore = std::make_shared<Kernel::BoundedValidator<int>>();
161 twoOrMore->setLower(2);
162 declareProperty(
"NumberOfDetectorColumns", DEFAULT_LONGITUDINAL_DETS, twoOrMore,
163 "Number of detector columns in the detector grid "
164 "of the sparse instrument.");
165 setPropertySettings(
"NumberOfDetectorColumns",
166 std::make_unique<EnabledWhenProperty>(
"SparseInstrument", ePropertyCriterion::IS_NOT_DEFAULT));
167 declareProperty(
"ImportanceSampling",
false,
168 "Enable importance sampling on the Q value chosen on multiple scatters based on Q.S(Q)");
170 declareProperty(
"MaxScatterPtAttempts", 5000, positiveInt,
171 "Maximum number of tries made to generate a scattering point "
172 "within the sample. Objects with holes in them, e.g. a thin "
173 "annulus can cause problems if this number is too low.\n"
174 "If a scattering point cannot be generated by increasing "
175 "this value then there is most likely a problem with "
176 "the sample geometry.");
177 declareProperty(
"SimulateEnergiesIndependently",
false,
178 "For inelastic calculation, whether the results for adjacent energy transfer bins are simulated "
179 "separately. Currently applies to Direct geometry only");
180 declareProperty(
"NormalizeStructureFactors",
false,
181 "Enable normalization of supplied structure factor(s). May be required when running a calculation "
182 "involving more than one material where the normalization of the default S(Q)=1 structure factor "
183 "doesn't match the normalization of a supplied non-isotropic structure factor");
191 std::map<std::string, std::string> issues;
193 if (inputWS ==
nullptr) {
196 issues[
"InputWorkspace"] =
"Input workspace must be a matrix workspace";
201 issues[
"InputWorkspace"] =
"Input workspace does not have a Sample";
203 bool atLeastOneValidShape = inputWS->sample().getShape().hasValidShape();
204 if (!atLeastOneValidShape) {
205 if (inputWS->sample().hasEnvironment()) {
206 auto env = &inputWS->sample().getEnvironment();
207 for (
size_t i = 0; i < env->nelements(); i++) {
208 if (env->getComponent(i).hasValidShape()) {
209 atLeastOneValidShape =
true;
215 if (!atLeastOneValidShape) {
216 issues[
"InputWorkspace"] =
"Either the Sample or one of the environment parts must have a valid shape.";
219 if (inputWS->sample().getShape().hasValidShape())
220 if (inputWS->sample().getMaterial().numberDensity() == 0)
221 issues[
"InputWorkspace"] =
"Sample must have a material set up with a non-zero number density";
222 if (inputWS->sample().hasEnvironment()) {
223 auto env = &inputWS->sample().getEnvironment();
224 for (
size_t i = 0; i < env->nelements(); i++)
225 if (env->getComponent(i).hasValidShape())
226 if (env->getComponent(i).material().numberDensity() == 0)
227 issues[
"InputWorkspace"] =
"Sample environment component " +
std::to_string(i) +
228 " must have a material set up with a non-zero number density ";
231 std::vector<MatrixWorkspace_sptr> SQWSs;
233 auto SQWSGroup = std::dynamic_pointer_cast<WorkspaceGroup>(SQWSBase);
235 auto groupMembers = SQWSGroup->getAllItems();
236 std::set<std::string> materialNames;
237 materialNames.insert(inputWS->sample().getMaterial().name());
238 if (inputWS->sample().hasEnvironment()) {
239 auto nEnvComponents = inputWS->sample().getEnvironment().nelements();
240 for (
size_t i = 0; i < nEnvComponents; i++)
241 materialNames.insert(inputWS->sample().getEnvironment().getComponent(i).material().name());
244 for (
auto &materialName : materialNames) {
245 auto wsIt = std::find_if(groupMembers.begin(), groupMembers.end(),
246 [materialName](
Workspace_sptr &ws) { return ws->getName() == materialName; });
247 if (wsIt == groupMembers.end()) {
248 issues[
"StructureFactorWorkspace"] =
249 "No workspace for material " + materialName +
" found in S(Q,w) workspace group";
251 SQWSs.push_back(std::dynamic_pointer_cast<MatrixWorkspace>(*wsIt));
254 SQWSs.push_back(std::dynamic_pointer_cast<MatrixWorkspace>(SQWSBase));
257 if (inputWS->getAxis(0)->unit()->unitID() !=
"Momentum")
258 issues[
"InputWorkspace"] +=
"Input workspace must have units of Momentum (k) for elastic instrument\n";
259 for (
auto &SQWS : SQWSs) {
260 if (SQWS->getNumberHistograms() != 1)
261 issues[
"StructureFactorWorkspace"] +=
"S(Q) workspace must contain a single spectrum for elastic mode\n";
263 if (SQWS->getAxis(0)->unit()->unitID() !=
"MomentumTransfer")
264 issues[
"StructureFactorWorkspace"] +=
"S(Q) workspace must have units of MomentumTransfer\n";
267 for (
auto &SQWS : SQWSs) {
268 if (inputWS->getAxis(0)->unit()->unitID() !=
"DeltaE")
269 issues[
"InputWorkspace"] =
"Input workspace must have units of DeltaE for inelastic instrument\n";
270 std::set<std::string> axisUnits;
271 axisUnits.insert(SQWS->getAxis(0)->unit()->unitID());
272 axisUnits.insert(SQWS->getAxis(1)->unit()->unitID());
273 if (axisUnits != std::set<std::string>{
"DeltaE",
"MomentumTransfer"})
274 issues[
"StructureFactorWorkspace"] +=
275 "S(Q, w) workspace must have units of Energy Transfer and MomentumTransfer\n";
277 if (SQWS->getAxis(1)->isSpectra())
278 issues[
"StructureFactorWorkspace"] +=
"S(Q, w) must have a numeric spectrum axis\n";
279 std::vector<double> wValues;
280 if (SQWS->getAxis(0)->unit()->unitID() ==
"DeltaE") {
281 if (!SQWS->isCommonBins())
282 issues[
"StructureFactorWorkspace"] +=
"S(Q,w) must have common w values at all Q";
285 auto checkEqualQBins = [&issues](
const MantidVec &qValues) {
288 issues[
"StructureFactorWorkspace"] +=
289 "S(Q,w) must have equal size bins in Q in order to support gaussian interpolation";
293 if (SQWS->getAxis(0)->unit()->unitID() ==
"MomentumTransfer") {
294 for (
size_t iHist = 0; iHist < SQWS->getNumberHistograms(); iHist++) {
295 auto qValues = SQWS->dataX(iHist);
296 checkEqualQBins(qValues);
298 }
else if (SQWS->getAxis(1)->unit()->unitID() ==
"MomentumTransfer") {
299 auto qAxis =
dynamic_cast<NumericAxis *
>(SQWS->getAxis(1));
302 checkEqualQBins(qValues);
308 for (
auto &SQWS : SQWSs) {
309 for (
size_t i = 0; i < SQWS->getNumberHistograms(); i++) {
310 auto &
y = SQWS->y(i);
311 if (std::any_of(
y.cbegin(),
y.cend(), [](
const auto yval) { return yval < 0 || std::isnan(yval); }))
312 issues[
"StructureFactorWorkspace"] +=
"S(Q) workspace must have all y >= 0";
316 const int nSimulationPoints =
getProperty(
"NumberOfSimulationPoints");
317 if (!
isEmpty(nSimulationPoints)) {
320 interpOpt.
set(interpValue,
false,
false);
322 if (!nSimPointsIssue.empty())
323 issues[
"NumberOfSimulationPoints"] = nSimPointsIssue;
326 const bool simulateEnergiesIndependently =
getProperty(
"SimulateEnergiesIndependently");
327 if (simulateEnergiesIndependently) {
329 issues[
"SimulateEnergiesIndependently"] =
330 "SimulateEnergiesIndependently is only applicable to inelastic direct geometry calculations";
332 issues[
"SimulateEnergiesIndependently"] =
333 "SimulateEnergiesIndependently is only applicable to inelastic direct geometry calculations. Different "
334 "energy transfer bins are always simulated separately for indirect geometry";
347 double &xmax)
const {
349 xmin = std::numeric_limits<double>::max();
355 for (
size_t wsIndex = 0; wsIndex < numberOfSpectra; wsIndex++) {
356 if (spectrumInfo.hasDetectors(wsIndex) && !spectrumInfo.isMonitor(wsIndex) && !spectrumInfo.isMasked(wsIndex)) {
357 const auto &dataX = ws.
points(wsIndex);
358 const double xfront = dataX.front();
359 const double xback = dataX.back();
360 if (std::isnormal(xfront) && std::isnormal(xback)) {
370 throw std::runtime_error(
"Unable to determine min and max x values for workspace");
375 auto SQWSGroup = std::dynamic_pointer_cast<WorkspaceGroup>(suppliedSQWS);
376 size_t nEnvComponents = 0;
382 auto SQWSGroupMember = std::static_pointer_cast<MatrixWorkspace>(SQWSGroup->getItem(matName));
384 if (nEnvComponents > 0) {
386 SQWSGroupMember = std::static_pointer_cast<MatrixWorkspace>(SQWSGroup->getItem(matName));
389 for (
size_t i = 1; i < nEnvComponents; i++) {
391 SQWSGroupMember = std::static_pointer_cast<MatrixWorkspace>(SQWSGroup->getItem(matName));
396 std::dynamic_pointer_cast<MatrixWorkspace>(suppliedSQWS));
398 *std::dynamic_pointer_cast<MatrixWorkspace>(suppliedSQWS),
static_cast<size_t>(1),
399 HistogramData::Histogram(HistogramData::Points{0.}, HistogramData::Frequencies{1.}));
400 if (nEnvComponents > 0) {
402 g_log.
information() <<
"Creating isotropic structure factor for " << matName << std::endl;
405 for (
size_t i = 1; i < nEnvComponents; i++) {
407 g_log.
information() <<
"Creating isotropic structure factor for " << matName << std::endl;
418 const std::string_view &matName,
423 if (SQWS->getAxis(1)->unit()->unitID() ==
"MomentumTransfer") {
425 transposeAlgorithm->initialize();
426 transposeAlgorithm->setProperty(
"InputWorkspace", SQWS);
427 transposeAlgorithm->setProperty(
"OutputWorkspace",
"_");
428 transposeAlgorithm->execute();
429 SQWS = transposeAlgorithm->getProperty(
"OutputWorkspace");
430 }
else if (SQWS->getAxis(1)->isSpectra()) {
432 auto newAxis = std::make_unique<NumericAxis>(std::vector<double>{0.});
433 newAxis->setUnit(
"DeltaE");
434 SQWS->replaceAxis(1, std::move(newAxis));
436 auto specAxis =
dynamic_cast<NumericAxis *
>(SQWS->getAxis(1));
437 std::vector<DiscusData1D> data;
438 for (
size_t i = 0; i < SQWS->getNumberHistograms(); i++) {
439 data.emplace_back(SQWS->histogram(i).dataX(), SQWS->histogram(i).dataY());
443 std::make_shared<DiscusData2D>(data, std::make_shared<std::vector<double>>(specAxis->getValues()))};
444 SQWSMapping.
logSQ = SQWSMapping.SQ->createCopy();
446 m_SQWSs.push_back(SQWSMapping);
455 if (ws->isHistogramData()) {
458 pointDataAlgorithm->initialize();
459 pointDataAlgorithm->setProperty(
"InputWorkspace", ws);
460 pointDataAlgorithm->setProperty(
"OutputWorkspace",
"_");
461 pointDataAlgorithm->execute();
462 ws = pointDataAlgorithm->getProperty(
"OutputWorkspace");
467 SQWSPoints->setSharedY(0, ws->sharedY(0));
468 SQWSPoints->setSharedE(0, ws->sharedE(0));
469 std::vector<double> newX = ws->histogram(0).dataX();
471 SQWSPoints->setSharedX(0, HistogramData::Points(newX).cowData());
475 auto binAxis =
dynamic_cast<BinEdgeAxis *
>(ws->getAxis(1));
478 std::vector<double> centres;
480 auto newAxis = std::make_unique<NumericAxis>(centres);
481 newAxis->setUnit(ws->getAxis(1)->unit()->unitID());
482 ws->replaceAxis(1, std::move(newAxis));
491 "DiscusMultipleScatteringCorrection is in the beta stage of development. Its name, properties and behaviour "
492 "may change without warning.");
494 throw std::runtime_error(
"This algorithm explicitly stores named output workspaces in the ADS so must be run with "
495 "AlwaysStoreInADS set to true");
503 m_sigmaSS = std::make_shared<DiscusData1D>(
504 DiscusData1D{sigmaSSWS->getSpectrum(0).readX(), sigmaSSWS->getSpectrum(0).readY()});
508 double qmax = std::numeric_limits<float>::max();
509 EFixedProvider
efixed(*inputWS);
523 int nSimulationPointsInt =
getProperty(
"NumberOfSimulationPoints");
524 size_t nSimulationPoints =
static_cast<size_t>(nSimulationPointsInt);
526 if (
isEmpty(nSimulationPoints)) {
527 nSimulationPoints = inputNbins;
528 }
else if (nSimulationPoints > inputNbins) {
529 g_log.
warning() <<
"The requested number of simulation points is larger "
530 "than the maximum number of simulations per spectra. "
532 << inputNbins <<
".\n ";
533 nSimulationPoints = inputNbins;
538 const bool useSparseInstrument =
getProperty(
"SparseInstrument");
540 if (useSparseInstrument) {
541 const int latitudinalDets =
getProperty(
"NumberOfDetectorRows");
542 const int longitudinalDets =
getProperty(
"NumberOfDetectorColumns");
545 const int nScatters =
getProperty(
"NumberScatterings");
547 std::vector<MatrixWorkspace_sptr> simulationWSs;
548 std::vector<MatrixWorkspace_sptr> outputWSs;
551 auto noAbsSimulationWS = useSparseInstrument ? sparseWS->clone() : noAbsOutputWS;
552 for (
int i = 0; i < nScatters; i++) {
555 simulationWSs.emplace_back(simulationWS);
556 outputWSs.emplace_back(outputWS);
558 const MatrixWorkspace &instrumentWS = useSparseInstrument ? *sparseWS : *inputWS;
559 const auto nhists = useSparseInstrument ? sparseWS->
getNumberHistograms() : inputWS->getNumberHistograms();
561 const int nSingleScatterEvents =
getProperty(
"NeutronPathsSingle");
562 const int nMultiScatterEvents =
getProperty(
"NeutronPathsMultiple");
571 Progress prog(
this, 0.0, 1.0, nhists * nSimulationPoints);
573 const std::string reportMsg =
"Computing corrections";
575 bool enableParallelFor =
true;
576 enableParallelFor = std::all_of(simulationWSs.cbegin(), simulationWSs.cend(),
584 for (int64_t i = 0; i < static_cast<int64_t>(nhists); ++i) {
592 if (spectrumInfo.hasDetectors(i) && !spectrumInfo.isMonitor(i) && !spectrumInfo.isMasked(i)) {
594 const double eFixedValue =
efixed.value(spectrumInfo.detector(i).getID());
595 auto xPoints = instrumentWS.
points(i).rawData();
599 const auto nbins = kInW.size();
601 const size_t nsteps = std::max(
static_cast<size_t>(1), nSimulationPoints - 1);
602 const size_t xStepSize = nbins == 1 ? 1 : (nbins - 1) / nsteps;
604 const auto detPos = spectrumInfo.position(i);
607 auto componentWorkspaces =
m_SQWSs;
613 std::vector<double> kValues;
614 std::transform(kInW.begin(), kInW.end(), std::back_inserter(kValues),
615 [](std::tuple<double, int, double> t) { return std::get<0>(t); });
618 for (
size_t bin = 0; bin < nbins; bin += xStepSize) {
619 const double kinc = std::get<0>(kInW[bin]);
620 if ((kinc <= 0) || std::isnan(kinc)) {
625 std::vector<double> wValues = std::get<1>(kInW[bin]) == -1 ? xPoints : std::vector{std::get<2>(kInW[bin])};
630 auto weights =
simulatePaths(nSingleScatterEvents, 1, rng, componentWorkspaces, kinc, wValues, detPos,
true);
631 if (std::get<1>(kInW[bin]) == -1) {
632 noAbsSimulationWS->getSpectrum(i).mutableY() += weights;
634 noAbsSimulationWS->getSpectrum(i).dataY()[std::get<1>(kInW[bin])] = weights[0];
637 for (
int ne = 0; ne < nScatters; ne++) {
638 int nEvents = ne == 0 ? nSingleScatterEvents : nMultiScatterEvents;
640 weights =
simulatePaths(nEvents, ne + 1, rng, componentWorkspaces, kinc, wValues, detPos,
false);
641 if (std::get<1>(kInW[bin]) == -1.0) {
642 simulationWSs[ne]->getSpectrum(i).mutableY() += weights;
644 simulationWSs[ne]->getSpectrum(i).dataY()[std::get<1>(kInW[bin])] = weights[0];
651 if (xStepSize > 1 && bin + xStepSize >= nbins && bin + 1 != nbins) {
652 bin = nbins - xStepSize - 1;
659 if (!useSparseInstrument && xStepSize > 1) {
660 auto histNoAbs = noAbsSimulationWS->histogram(i);
661 if (xStepSize < nbins) {
664 std::fill(histNoAbs.mutableY().begin() + 1, histNoAbs.mutableY().end(), histNoAbs.y()[0]);
666 noAbsOutputWS->setHistogram(i, histNoAbs);
668 for (
size_t ne = 0; ne < static_cast<size_t>(nScatters); ne++) {
669 auto histnew = simulationWSs[ne]->histogram(i);
670 if (xStepSize < nbins) {
673 std::fill(histnew.mutableY().begin() + 1, histnew.mutableY().end(), histnew.y()[0]);
675 outputWSs[ne]->setHistogram(i, histnew);
684 if (useSparseInstrument) {
685 Poco::Thread::sleep(200);
686 const std::string reportMsgSpatialInterpolation =
"Spatial Interpolation";
687 prog.
report(reportMsgSpatialInterpolation);
688 interpolateFromSparse(*noAbsOutputWS, *std::dynamic_pointer_cast<SparseWorkspace>(noAbsSimulationWS),
690 for (
size_t ne = 0; ne < static_cast<size_t>(nScatters); ne++) {
691 interpolateFromSparse(*outputWSs[ne], *std::dynamic_pointer_cast<SparseWorkspace>(simulationWSs[ne]),
697 auto wsgroup = std::make_shared<WorkspaceGroup>();
702 const std::string wsNamePrefix = outputGroupWSName +
"_Scatter_";
703 std::string wsName = wsNamePrefix +
"1_NoAbs";
705 wsgroup->addWorkspace(noAbsOutputWS);
707 for (
size_t i = 0; i < outputWSs.size(); i++) {
710 wsgroup->addWorkspace(outputWSs[i]);
712 auto integratedWorkspace =
integrateWS(outputWSs[i]);
714 wsgroup->addWorkspace(integratedWorkspace);
717 if (outputWSs.size() > 1) {
720 for (
size_t i = 1; i < outputWSs.size(); i++) {
721 summedMScatOutput = summedMScatOutput + outputWSs[i];
723 wsName = wsNamePrefix +
"2_" +
std::to_string(outputWSs.size()) +
"_Summed";
725 wsgroup->addWorkspace(summedMScatOutput);
728 summedAllScatOutput = summedMScatOutput + outputWSs[0];
729 wsName = wsNamePrefix +
"1_" +
std::to_string(outputWSs.size()) +
"_Summed";
731 wsgroup->addWorkspace(summedAllScatOutput);
734 ratioOutput = outputWSs[0] / summedAllScatOutput;
735 wsName = outputGroupWSName +
"_Ratio_Single_To_All";
737 wsgroup->addWorkspace(ratioOutput);
741 auto invRatioOutput = 1 / ratioOutput;
743 replaceNans->setChild(
true);
744 replaceNans->initialize();
745 replaceNans->setProperty(
"InputWorkspace", invRatioOutput);
746 replaceNans->setProperty(
"OutputWorkspace", invRatioOutput);
747 replaceNans->setProperty(
"NaNValue", 0.0);
748 replaceNans->setProperty(
"InfinityValue", 0.0);
749 replaceNans->execute();
750 wsName = outputGroupWSName +
"_Ratio_All_To_Single";
752 wsgroup->addWorkspace(invRatioOutput);
759 if (
g_log.
is(Kernel::Logger::Priority::PRIO_INFORMATION)) {
760 g_log.
information() <<
"Total simulation points=" << nhists * nSimulationPoints <<
"\n";
762 g_log.
information() <<
"Generating initial track required " << kv.first <<
" attempts on " << kv.second
766 <<
static_cast<double>(
m_IkCalculations) /
static_cast<double>(nhists * nSimulationPoints)
768 if (
g_log.
is(Kernel::Logger::Priority::PRIO_DEBUG))
769 for (
size_t i = 0; i <
m_SQWSs.size(); i++)
782std::vector<std::tuple<double, int, double>>
784 std::vector<std::tuple<double, int, double>> kInW;
785 const double kFixed = toWaveVector(
efixed);
788 std::transform(xPoints.begin(), xPoints.end(), std::back_inserter(kInW), [&
index](
double d) {
789 auto t = std::make_tuple(d, index, 0.);
795 kInW.emplace_back(std::make_tuple(kFixed, -1, 0.));
797 for (
int i = 0; i < static_cast<int>(xPoints.size()); i++) {
799 kInW.emplace_back(std::make_tuple(kFixed, i, xPoints[i]));
801 const double initialE =
efixed + xPoints[i];
803 const double kin = toWaveVector(initialE);
804 kInW.emplace_back(std::make_tuple(kin, i, xPoints[i]));
807 kInW.emplace_back(std::make_tuple(-1.0, i, xPoints[i]));
822 for (
auto &SQWSMapping :
m_SQWSs) {
823 auto &SQWS = SQWSMapping.SQ;
824 std::shared_ptr<DiscusData2D> outputWS = SQWS->createCopy(
true);
825 std::vector<double> IOfQYFull;
827 for (
size_t iW = 0; iW < SQWS->getNumberHistograms(); iW++) {
828 std::vector<double> qValues = SQWS->histogram(iW).X;
829 std::vector<double> SQValues = SQWS->histogram(iW).Y;
831 if (qValues.front() > 0.) {
832 qValues.insert(qValues.begin(), 0.);
833 SQValues.insert(SQValues.begin(), SQValues.front());
835 if (qValues.back() < qmax) {
836 qValues.push_back(qmax);
837 SQValues.push_back(SQValues.back());
840 for (
size_t i = 1; i < qValues.size(); i++) {
841 if (std::abs(SQValues[i] - SQValues[i - 1]) >
842 std::numeric_limits<double>::epsilon() * std::min(SQValues[i - 1], SQValues[i])) {
843 qValues.insert(qValues.begin() + i, std::nextafter(qValues[i], -DBL_MAX));
844 SQValues.insert(SQValues.begin() + i, SQValues[i - 1]);
849 std::vector<double> QSQValues;
850 std::transform(SQValues.begin(), SQValues.end(), qValues.begin(), std::back_inserter(QSQValues),
851 std::multiplies<double>());
853 outputWS->histogram(iW).X.resize(qValues.size());
854 outputWS->histogram(iW).X = qValues;
855 outputWS->histogram(iW).Y.resize(QSQValues.size());
856 outputWS->histogram(iW).Y = QSQValues;
858 SQWSMapping.QSQ = outputWS;
872std::tuple<std::vector<double>, std::vector<double>, std::vector<double>>
874 const bool returnCumulative) {
875 std::vector<double> IOfQYFull, qValuesFull, wIndices;
876 double IOfQMaxPreviousRow = 0.;
878 auto &wValues = QSQ->getSpecAxisValues();
879 std::vector<double> wWidths;
880 if (wValues.size() == 1) {
883 wWidths.push_back(1.);
885 std::vector<double> wBinEdges;
886 wBinEdges.reserve(wValues.size() + 1);
888 std::adjacent_difference(wBinEdges.begin(), wBinEdges.end(), std::back_inserter(wWidths));
889 wWidths.erase(wWidths.begin());
892 double wMax = fromWaveVector(kinc);
893 auto it = std::lower_bound(wValues.begin(), wValues.end(), wMax);
894 size_t iFirstInaccessibleW = std::distance(wValues.begin(), it);
895 auto nAccessibleWPoints = iFirstInaccessibleW;
898 std::vector<double> IOfQX, IOfQY;
900 IOfQYFull.reserve(nAccessibleWPoints);
901 qValuesFull.reserve(nAccessibleWPoints);
902 wIndices.reserve(nAccessibleWPoints);
904 for (
size_t iW = 0; iW < nAccessibleWPoints; iW++) {
905 auto kf =
getKf((wValues)[iW], kinc);
909 integrateCumulative(QSQ->histogram(iW), qmin, qmin + qrange, IOfQX, IOfQY, returnCumulative);
911 double wBinWidth = wWidths[iW];
912 std::transform(IOfQY.begin(), IOfQY.end(), IOfQY.begin(),
913 [IOfQMaxPreviousRow, wBinWidth](
double d) ->
double { return d * wBinWidth + IOfQMaxPreviousRow; });
914 IOfQMaxPreviousRow = IOfQY.back();
915 IOfQYFull.insert(IOfQYFull.end(), IOfQY.begin(), IOfQY.end());
916 qValuesFull.insert(qValuesFull.end(), IOfQX.begin(), IOfQX.end());
917 wIndices.insert(wIndices.end(), IOfQX.size(),
static_cast<double>(iW));
920 return {IOfQYFull, qValuesFull, wIndices};
932 for (
size_t iMat = 0; iMat < materialWorkspaces.size(); iMat++) {
933 auto QSQ = materialWorkspaces[iMat].QSQ;
934 auto [IOfQYFull, qValuesFull, wIndices] =
integrateQSQ(QSQ, kinc,
true);
935 auto IOfQYAtQMax = IOfQYFull.empty() ? 0. : IOfQYFull.back();
936 if (IOfQYAtQMax == 0.)
937 throw std::runtime_error(
"Integral of Q * S(Q) is zero so can't generate probability distribution");
939 std::vector<double> IOfQYNorm;
940 std::transform(IOfQYFull.begin(), IOfQYFull.end(), std::back_inserter(IOfQYNorm),
941 [IOfQYAtQMax](
double d) ->
double { return d / IOfQYAtQMax; });
944 auto &InvPOfQ = materialWorkspaces[iMat].InvPOfQ;
945 for (
size_t i = 0; i < InvPOfQ->getNumberHistograms(); i++) {
946 InvPOfQ->histogram(i).X.resize(IOfQYNorm.size());
947 InvPOfQ->histogram(i).X = IOfQYNorm;
949 InvPOfQ->histogram(0).Y.resize(qValuesFull.size());
950 InvPOfQ->histogram(0).Y = qValuesFull;
951 InvPOfQ->histogram(1).Y.resize(wIndices.size());
952 InvPOfQ->histogram(1).Y = wIndices;
959 for (
size_t i = 0; i < SOfQ->getNumberHistograms(); i++) {
960 auto &ySQ = SOfQ->histogram(i).Y;
962 std::transform(ySQ.begin(), ySQ.end(), ySQ.begin(), [](
double d) ->
double {
963 const double exp_that_gives_close_to_zero = -20.0;
965 return exp_that_gives_close_to_zero;
981 const std::vector<double> &specialKs) {
982 for (
auto &SQWSMapping : matWSs) {
983 std::set<double> kValues(specialKs.begin(), specialKs.end());
986 const std::vector<double> qValues = SQWSMapping.SQ->histogram(0).X;
987 for (
auto q : qValues) {
989 kValues.insert(q / 2);
995 double maxSuppliedQ = qValues.back();
996 if (maxSuppliedQ > 0.) {
997 kValues.insert(maxSuppliedQ);
998 kValues.insert(2 * maxSuppliedQ);
1002 std::vector<double> finalkValues, QSQIntegrals;
1003 for (
auto k : kValues) {
1004 std::vector<double> IOfQYFull;
1005 std::tie(IOfQYFull, std::ignore, std::ignore) =
integrateQSQ(SQWSMapping.QSQ, k,
false);
1006 auto IOfQYAtQMax = IOfQYFull.empty() ? 0. : IOfQYFull.back();
1009 if (IOfQYAtQMax > 0) {
1010 double normalisedIntegral = IOfQYAtQMax / (2 * k * k);
1011 finalkValues.push_back(k);
1012 QSQIntegrals.push_back(normalisedIntegral);
1015 auto QSQScaleFactor = std::make_shared<DiscusData1D>(
DiscusData1D{finalkValues, QSQIntegrals});
1016 SQWSMapping.QSQScaleFactor = QSQScaleFactor;
1035 const double xmax, std::vector<double> &resultX,
1036 std::vector<double> &resultY,
1037 const bool returnCumulative) {
1038 const std::vector<double> &xValues =
h.X;
1039 const std::vector<double> &yValues =
h.Y;
1040 bool isPoints = xValues.size() == yValues.size();
1043 if (returnCumulative) {
1044 resultX.emplace_back(xmin);
1045 resultY.emplace_back(0.);
1050 if (xValues.front() > xmin)
1051 throw std::runtime_error(
"Distribution doesn't extend as far as lower integration limit, x=" +
1054 if (xValues.back() < xmax)
1055 throw std::runtime_error(
"Distribution doesn't extend as far as upper integration limit, x=" +
1058 auto iter = std::upper_bound(xValues.cbegin(), xValues.cend(), xmin);
1059 auto iRight =
static_cast<size_t>(std::distance(xValues.cbegin(), iter));
1061 auto linearInterp = [&xValues, &yValues](
const double x,
const size_t lIndex,
const size_t rIndex) ->
double {
1062 return (yValues[lIndex] * (xValues[rIndex] -
x) + yValues[rIndex] * (
x - xValues[lIndex])) /
1063 (xValues[rIndex] - xValues[lIndex]);
1068 if (xmin > xValues[iRight - 1]) {
1069 if (xmax >= xValues[iRight]) {
1071 double interpY = linearInterp(xmin, iRight - 1, iRight);
1072 yToUse = 0.5 * (interpY + yValues[iRight]);
1074 yToUse = yValues[iRight - 1];
1075 sum += yToUse * (xValues[iRight] - xmin);
1076 if (returnCumulative) {
1077 resultX.push_back(xValues[iRight]);
1078 resultY.push_back(sum);
1083 double interpY1 = linearInterp(xmin, iRight - 1, iRight);
1084 double interpY2 = linearInterp(xmax, iRight - 1, iRight);
1085 yToUse = 0.5 * (interpY1 + interpY2);
1087 yToUse = yValues[iRight - 1];
1088 sum += yToUse * (xmax - xmin);
1089 if (returnCumulative) {
1090 resultX.push_back(xmax);
1091 resultY.push_back(sum);
1098 for (; iRight < xValues.size() && xValues[iRight] <= xmax; iRight++) {
1100 yToUse = 0.5 * (yValues[iRight - 1] + yValues[iRight]);
1102 yToUse = yValues[iRight - 1];
1103 double xLeft = xValues[iRight - 1];
1104 double xRight = xValues[iRight];
1105 sum += yToUse * (xRight - xLeft);
1106 if (returnCumulative) {
1107 if (xRight > std::nextafter(xLeft, DBL_MAX)) {
1108 resultX.emplace_back(xRight);
1109 resultY.emplace_back(sum);
1115 if ((xmax > xValues[iRight - 1]) && (xmin <= xValues[iRight - 1])) {
1117 double interpY = linearInterp(xmax, iRight - 1, iRight);
1118 yToUse = 0.5 * (yValues[iRight - 1] + interpY);
1120 yToUse = yValues[iRight - 1];
1121 sum += yToUse * (xmax - xValues[iRight - 1]);
1122 if (returnCumulative) {
1123 resultX.emplace_back(xmax);
1124 resultY.emplace_back(sum);
1127 if (!returnCumulative) {
1128 resultX.emplace_back(xmax);
1129 resultY.emplace_back(sum);
1134 auto wsIntegrals = DataObjects::create<Workspace2D>(*ws, HistogramData::Points{0.});
1135 for (
size_t i = 0; i < ws->getNumberHistograms(); i++) {
1136 std::vector<double> IOfQX, IOfQY;
1138 ws->x(i).back(), IOfQX, IOfQY,
false);
1139 wsIntegrals->mutableY(i) = IOfQY.back();
1155 bool specialSingleScatterCalc) {
1156 double scatteringXSection, absorbXsection;
1157 if (specialSingleScatterCalc) {
1160 const double wavelength = 2 * M_PI / k;
1169 const auto sig_total = scatteringXSection + absorbXsection;
1170 return {sig_total, scatteringXSection};
1180std::tuple<double, int>
1192 const auto &histx = histToInterpolate.
X;
1193 const auto &histy = histToInterpolate.
Y;
1194 assert(histToInterpolate.
X.size() == histToInterpolate.
Y.size());
1195 if (
x > histx.back()) {
1196 return histy.back();
1198 if (
x < histx.front()) {
1199 return histy.front();
1201 const auto iter = std::upper_bound(histx.cbegin(), histx.cend(),
x);
1202 const auto idx =
static_cast<size_t>(std::distance(histx.cbegin(), iter) - 1);
1203 const double x0 = histx[idx];
1204 const double x1 = histx[idx + 1];
1205 const double asq = (pow(histy[idx + 1], 2) - pow(histy[idx], 2)) / (x1 - x0);
1207 throw std::runtime_error(
"Cannot perform square root interpolation on supplied distribution");
1209 const double b = x0 - pow(histy[idx], 2) / asq;
1210 return sqrt(asq * (
x - b));
1220 auto &xHisto = histToInterpolate.
X;
1221 auto &yHisto = histToInterpolate.
Y;
1222 if (
x > xHisto.back()) {
1223 return yHisto.back();
1225 if (
x < xHisto.front()) {
1226 return yHisto.front();
1230 auto iter = std::upper_bound(xHisto.cbegin(), xHisto.cend(),
x);
1231 auto idx =
static_cast<size_t>(std::distance(xHisto.cbegin(), iter) - 1);
1245 assert(histToInterpolate.
X.size() == histToInterpolate.
Y.size());
1246 if (
x > histToInterpolate.
X.back()) {
1247 return exp(histToInterpolate.
Y.back());
1249 if (
x < histToInterpolate.
X.front()) {
1250 return exp(histToInterpolate.
Y.front());
1253 auto deltax = histToInterpolate.
X[1] - histToInterpolate.
X[0];
1255 auto iter = std::upper_bound(histToInterpolate.
X.cbegin(), histToInterpolate.
X.cend(),
x);
1256 auto idx =
static_cast<size_t>(std::distance(histToInterpolate.
X.cbegin(), iter) - 1);
1260 auto ny = histToInterpolate.
Y.size();
1262 throw std::runtime_error(
"Need at least 3 y values to perform quadratic interpolation");
1269 const auto U = (
x - histToInterpolate.
X[idx]) / deltax;
1270 const auto &
y = histToInterpolate.
Y;
1271 const auto A = (
y[idx] - 2 *
y[idx + 1] +
y[idx + 2]) / 2;
1272 const auto B = (-3 *
y[idx] + 4 *
y[idx + 1] -
y[idx + 2]) / 2;
1273 const auto C =
y[idx];
1274 return exp(A * U * U + B * U + C);
1290 auto &wValues = SQWSMapping.
SQ->getSpecAxisValues();
1291 if (wValues.size() == 1) {
1293 if (w == (wValues)[0])
1299 }
catch (std::out_of_range &) {
1333 const Kernel::V3D &detPos,
bool specialSingleScatterCalc) {
1334 std::vector<double> sumOfWeights(wValues.size(), 0.);
1335 std::vector<int> countZeroWeights(wValues.size(),
1338 for (
int ie = 0; ie < nPaths; ie++) {
1339 auto [success, weights] =
1340 scatter(nScatters, rng, componentWorkspaces, kinc, wValues, detPos, specialSingleScatterCalc);
1342 std::transform(weights.begin(), weights.end(), sumOfWeights.begin(), sumOfWeights.begin(), std::plus<double>());
1343 std::transform(weights.begin(), weights.end(), countZeroWeights.begin(), countZeroWeights.begin(),
1344 [](
double d,
int count) { return d > 0. ? count : count + 1; });
1348 for (
size_t i = 0; i < wValues.size(); i++) {
1349 sumOfWeights[i] = sumOfWeights[i] / nPaths;
1352 return sumOfWeights;
1373std::tuple<bool, std::vector<double>>
1376 const std::vector<double> &wValues,
const Kernel::V3D &detPos,
1377 bool specialSingleScatterCalc) {
1381 auto shapeObjectWithScatter =
1383 double scatteringXSection;
1384 std::tie(std::ignore, scatteringXSection) =
1385 new_vector(shapeObjectWithScatter->material(), kinc, specialSingleScatterCalc);
1387 auto currentComponentWorkspaces = componentWorkspaces;
1389 for (
int iScat = 0; iScat < nScatters - 1; iScat++) {
1392 auto newComponentWorkspaces = componentWorkspaces;
1393 for (
auto &SQWSMapping : currentComponentWorkspaces)
1394 SQWSMapping.InvPOfQ = SQWSMapping.InvPOfQ->createCopy();
1396 currentComponentWorkspaces = newComponentWorkspaces;
1399 auto trackStillAlive =
1400 q_dir(track, shapeObjectWithScatter, currentComponentWorkspaces, k, scatteringXSection, rng, weight);
1401 if (!trackStillAlive)
1402 return {
true, std::vector<double>(wValues.size(), 0.)};
1410 return {
false, {0.}};
1412 shapeObjectWithScatter =
1414 std::tie(std::ignore, scatteringXSection) =
1415 new_vector(shapeObjectWithScatter->material(), k, specialSingleScatterCalc);
1418 Kernel::V3D directionToDetector = detPos - track.startPoint();
1421 track.reset(track.startPoint(), directionToDetector);
1432 return {
false, {0.}};
1434 std::vector<double> weights;
1435 auto scatteringXSectionFull = shapeObjectWithScatter->material().totalScatterXSection();
1443 for (
auto &w : wValues) {
1444 const double finalE = fromWaveVector(kinc) - w;
1446 const double kout = toWaveVector(finalE);
1447 const auto qVector = directionToDetector * kout - prevDirection * k;
1448 const double q = qVector.
norm();
1449 const double finalW = fromWaveVector(k) - finalE;
1451 auto componentWSMapping = *componentWSIt;
1454 : scatteringXSectionFull;
1457 for (
auto it = track.cbegin(); it != track.cend(); it++) {
1459 auto &materialPassingThrough = it->object->material();
1460 std::tie(sigma_total, std::ignore) =
new_vector(materialPassingThrough, kout, specialSingleScatterCalc);
1461 double numberDensity = materialPassingThrough.numberDensityEffective();
1462 double vmu = 100 * numberDensity * sigma_total;
1463 if (specialSingleScatterCalc)
1465 const double dl = it->distInsideObject;
1466 AT2 *= exp(-dl * vmu);
1468 weights.emplace_back(weight * AT2 * SQ * scatteringXSection / (4 * M_PI));
1470 weights.emplace_back(0.);
1473 return {
true, weights};
1484 kf = toWaveVector(fromWaveVector(kinc) - deltaE);
1485 assert(!std::isnan(kf));
1503 const double qmin = abs(kf - ki);
1504 const double qrange = 2 * std::min(ki, kf);
1505 return {qmin, qrange};
1514std::tuple<double, double, int, double>
1532 if (wValues.size() == 1) {
1536 std::vector<double> wBinEdges;
1537 wBinEdges.reserve(wValues.size() + 1);
1540 wRange = wBinEdges.back() - wBinEdges.front();
1541 double w = wBinEdges.front() + rng.
nextValue() * wRange;
1544 double maxkf = toWaveVector(fromWaveVector(kinc) - wValues.front());
1545 double qRange = kinc + maxkf;
1547 return {q, qRange, iW, wRange};
1576 const double scatteringXSection,
1578 const double kinc = k;
1584 k =
getKf(componentWSIt->SQ->getSpecAxisValues()[iW], kinc);
1585 weight = weight * scatteringXSection;
1587 double qrange, wRange;
1588 auto &wValues = componentWSIt->SQ->getSpecAxisValues();
1589 std::tie(QQ, qrange, iW, wRange) =
sampleQWUniform(wValues, rng, kinc);
1592 if (fromWaveVector(kinc) - wValues[iW] <= 0)
1594 k =
getKf(wValues[iW], kinc);
1597 weight = weight * scatteringXSection * SQ * QQ * qrange * wRange;
1599 double integralQSQ =
getQSQIntegral(*componentWSIt->QSQScaleFactor, kinc);
1600 assert(integralQSQ != 0.);
1601 weight = weight / integralQSQ;
1606 const double cosT = (kinc * kinc + k * k - QQ * QQ) / (2 * kinc * k);
1608 if (std::abs(cosT) > 1.0)
1623 const auto B3 = sqrt(1 - cosT * cosT);
1624 const auto B2 = cosT;
1643 double UKX, UKY, UKZ;
1644 if (vz * vz < 1.0) {
1647 auto A2 = sqrt(vx * vx + vy * vy);
1648 auto UQTZ = cos(phi) * A2;
1649 auto UQTX = -cos(phi) * vz * vx / A2 + sin(phi) * vy / A2;
1650 auto UQTY = -cos(phi) * vz * vy / A2 - sin(phi) * vx / A2;
1651 UKX = B2 * vx + B3 * UQTX;
1652 UKY = B2 * vy + B3 * UQTY;
1653 UKZ = B2 * vz + B3 * UQTZ;
1657 UKX = B3 * cos(phi);
1658 UKY = B3 * sin(phi);
1682 if (
g_log.
is(Kernel::Logger::Priority::PRIO_WARNING)) {
1689 throw std::runtime_error(
1690 "DiscusMultipleScatteringCorrection::start_point() - Unable to generate entry point into sample after " +
1708 double totalMuL = 0.;
1709 auto nlinks = track.
count();
1711 boost::container::small_vector<std::tuple<const Geometry::IObject *, double, double, double>, 5> geometryObjects;
1712 geometryObjects.reserve(nlinks);
1714 for (
auto it = track.
cbegin(); it != track.
cend(); it++) {
1715 const double trackSegLength = it->distInsideObject;
1716 const auto geometryObj = it->object;
1718 std::tie(sigma_total, std::ignore) =
new_vector(geometryObj->material(), k, specialSingleScatterCalc);
1719 double vmu = 100 * geometryObj->material().numberDensityEffective() * sigma_total;
1720 double muL = trackSegLength * vmu;
1723 geometryObjects.emplace_back(geometryObj, vmu, muL, sigma_total);
1727 double b4Overall = (1.0 - exp(-totalMuL));
1728 double muL = -log(1 - rng.
nextValue() * b4Overall);
1730 double newWeight = 0.;
1731 double prevExpTerms = 1.;
1732 std::tuple<const Geometry::IObject *, double, double, double> geometryObjectDetails;
1733 for (
size_t i = 0; i < geometryObjects.size(); i++) {
1734 geometryObjectDetails = geometryObjects[i];
1735 auto muL_i = std::get<2>(geometryObjectDetails);
1736 auto vmu_i = std::get<1>(geometryObjectDetails);
1737 if (muL - muL_i > 0) {
1738 vl += muL_i / vmu_i;
1740 prevExpTerms *= exp(-muL_i);
1743 double b4 = (1.0 - exp(-muL_i)) * prevExpTerms;
1744 auto sigma_total = std::get<3>(geometryObjectDetails);
1745 newWeight = b4 / sigma_total;
1749 weight = weight * newWeight;
1754 auto geometryObject = std::get<0>(geometryObjectDetails);
1755 if (
g_log.
is(Kernel::Logger::Priority::PRIO_DEBUG)) {
1757 (*(componentIt->scatterCount))++;
1759 return geometryObject;
1773 auto ptx = neutron.startPos.X();
1774 auto pty = neutron.startPos.Y();
1777 ptOnBeamProfile[
m_refframe->pointingHorizontal()] = ptx;
1778 ptOnBeamProfile[
m_refframe->pointingUp()] = pty;
1781 toSample[
m_refframe->pointingAlongBeam()] = 1.;
1795 const auto x =
position[0] + vl * direction[0];
1796 const auto y =
position[1] + vl * direction[1];
1797 const auto z =
position[2] + vl * direction[2];
1798 const auto startPoint =
V3D(
x,
y,
z);
1812std::shared_ptr<SparseWorkspace>
1814 const size_t rows,
const size_t columns) {
1815 auto sparseWS = std::make_shared<SparseWorkspace>(modelWS, nXPoints, rows, columns);
1820 for (
auto &SQWSMapping : matWSs) {
1821 auto &QSQ = SQWSMapping.QSQ;
1822 size_t expectedMaxSize =
1823 std::accumulate(QSQ->histograms().cbegin(), QSQ->histograms().cend(),
static_cast<size_t>(0),
1824 [](
const size_t value,
const DiscusData1D &histo) { return value + histo.Y.size(); });
1825 auto ws = std::make_shared<DiscusData2D>(std::vector<DiscusData1D>(nhists),
nullptr);
1826 ws->histogram(0).X.reserve(expectedMaxSize);
1827 for (
size_t i = 0; i < nhists; i++)
1828 ws->histogram(i).Y.reserve(expectedMaxSize);
1829 SQWSMapping.InvPOfQ = ws;
1837 outputWS->setDistribution(
true);
1838 outputWS->setYUnit(
"");
1839 outputWS->setYUnitLabel(
"Scattered Weight");
1849 auto interpolationOpt = std::make_unique<InterpolationOption>();
1850 return interpolationOpt;
1857 const auto refFrame = targetWS.
getInstrument()->getReferenceFrame();
1859 for (int64_t i = 0; i < static_cast<decltype(i)>(spectrumInfo.size()); ++i) {
1861 if (spectrumInfo.hasDetectors(i) && !spectrumInfo.isMonitor(i)) {
1863 std::tie(lat, lon) = spectrumInfo.geographicalAngles(i);
1865 if (spatiallyInterpHisto.size() > 1) {
1866 auto targetHisto = targetWS.
histogram(i);
1867 interpOpt.
applyInPlace(spatiallyInterpHisto, targetHisto);
1870 targetWS.
mutableY(i) = spatiallyInterpHisto.y().front();
1885 bool noClash(
false);
1887 for (
int i = 0; !noClash; ++i) {
1888 std::string wsIndex;
1925 auto componentWSIt = std::find_if(componentWorkspaces.begin(), componentWorkspaces.end(),
1927 return SQWS.ComponentPtr.get() == shapeObjectWithScatter;
1929 assert(componentWSIt != componentWorkspaces.end());
1932 return &(*componentWSIt);
1938 m_env = &inputWS->sample().getEnvironment();
1939 }
catch (std::runtime_error &) {
1948 auto instrument = inputWS->getInstrument();
1950 m_refframe = instrument->getReferenceFrame();
#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.
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.
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)
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)
std::vector< double > simulatePaths(const int nEvents, const int nScatters, Kernel::PseudoRandomNumberGenerator &rng, ComponentWorkspaceMappings &componentWorkspaces, const double kinc, const std::vector< double > &wValues, const Kernel::V3D &detPos, bool specialSingleScatterCalc)
Simulates a set of neutron paths through the sample to a specific detector position with each path co...
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...
std::tuple< bool, std::vector< double > > scatter(const int nScatters, Kernel::PseudoRandomNumberGenerator &rng, const ComponentWorkspaceMappings &componentWorkspaces, const double kinc, const std::vector< double > &wValues, const Kernel::V3D &detPos, bool specialSingleScatterCalc)
Simulates a single neutron path through the sample to a specific detector position containing the spe...
Kernel::DeltaEMode::Type m_EMode
void prepareStructureFactors()
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)
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.
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...
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 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.
void exec() override
Execution code.
void correctForWorkspaceNameClash(std::string &wsName)
Adjust workspace name in case of clash in the ADS.
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
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).
long long m_IkCalculations
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.
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
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
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 the Mersenne Twister 19937 pseudo-random number generator algorithm as a specialz...
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.
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.
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.