99 outWS = inWS->clone();
103 bool diffraction(
true);
105 if (outWS->getNumDims() > 3) {
106 if (outWS->getDimension(3)->getMDFrame().name() ==
"DeltaE") {
108 if (outWS->getExperimentInfo(0)->run().hasProperty(
"Ei")) {
109 Ei = outWS->getExperimentInfo(0)->run().getPropertyValueAsType<
double>(
"Ei");
111 throw std::runtime_error(
"Workspace contains energy transfer axis, but no Ei."
112 "The MD normalization workflow is not implemented for "
113 "indirect geometry");
121 auto convention = Kernel::ConfigService::Instance().getString(
"Q.convention");
123 double qxmin = outWS->getDimension(0)->getMinimum();
124 double qxmax = outWS->getDimension(0)->getMaximum();
125 double qymin = outWS->getDimension(1)->getMinimum();
126 double qymax = outWS->getDimension(1)->getMaximum();
127 double qzmin = outWS->getDimension(2)->getMinimum();
128 double qzmax = outWS->getDimension(2)->getMaximum();
129 double dEmin(0.0), dEmax(0.0);
133 dEmin = outWS->getDimension(3)->getMinimum();
134 dEmax = outWS->getDimension(3)->getMaximum();
136 std::vector<double> otherDimsMin, otherDimsMax;
137 std::vector<std::string> otherDimsNames;
138 for (
size_t i = nqedims; i < outWS->getNumDims(); i++) {
139 otherDimsMin.emplace_back(outWS->getDimension(i)->getMinimum());
140 otherDimsMax.emplace_back(outWS->getDimension(i)->getMaximum());
141 otherDimsNames.emplace_back(outWS->getDimension(i)->getName());
145 size_t nExperimentInfos = outWS->getNumExperimentInfo();
146 if (nExperimentInfos > 1) {
147 g_log.
warning(
"More than one experiment info. On merged workspaces, the "
148 "limits recalculations might be wrong");
151 for (
size_t iExpInfo = 0; iExpInfo < nExperimentInfos; iExpInfo++) {
152 auto ¤tExptInfo = *(outWS->getExperimentInfo(
static_cast<uint16_t
>(iExpInfo)));
154 const auto &spectrumInfo = currentExptInfo.spectrumInfo();
155 const auto nspectra =
static_cast<int64_t
>(spectrumInfo.size());
156 std::vector<double> lowValues, highValues;
158 auto *lowValuesLog =
dynamic_cast<VectorDoubleProperty *
>(currentExptInfo.getLog(
"MDNorm_low"));
159 lowValues = (*lowValuesLog)();
161 auto *highValuesLog =
dynamic_cast<VectorDoubleProperty *
>(currentExptInfo.getLog(
"MDNorm_high"));
162 highValues = (*highValuesLog)();
165 bool zeroWeights(
false);
166 for (
size_t iOtherDims = 0; iOtherDims < otherDimsNames.size(); iOtherDims++) {
170 currentExptInfo.run().getProperty(otherDimsNames[iOtherDims]));
171 if ((otherDimsLog->firstValue() < otherDimsMin[iOtherDims]) ||
172 (otherDimsLog->firstValue() > otherDimsMax[iOtherDims])) {
174 g_log.
warning() <<
"In experimentInfo " << iExpInfo <<
", log " << otherDimsNames[iOtherDims]
175 <<
" is outside limits\n";
180 highValues = lowValues;
182 auto source = currentExptInfo.getInstrument()->getSource()->getPos();
183 auto sample = currentExptInfo.getInstrument()->getSample()->getPos();
184 auto beamDir = sample - source;
186 auto gon = currentExptInfo.run().getGoniometerMatrix();
190 for (int64_t i = 0; i < nspectra; i++) {
191 if (!spectrumInfo.hasDetectors(i) || spectrumInfo.isMonitor(i) || spectrumInfo.isMasked(i)) {
192 highValues[i] = lowValues[i];
195 double theta = spectrumInfo.twoTheta(i, sample, beamDir);
196 double phi = spectrumInfo.phi(i);
197 V3D qout(sin(theta) * cos(phi), sin(theta) * sin(phi), cos(theta)), qin(0., 0., 1.), qLabLow, qLabHigh,
198 qSampleLow, qSampleHigh;
200 if (convention ==
"Crystallography") {
206 kfmin = lowValues[i];
207 kfmax = highValues[i];
208 qLabLow = (qin - qout) * kfmin;
209 qLabHigh = (qin - qout) * kfmax;
211 if (dEmin > lowValues[i]) {
212 lowValues[i] = dEmin;
214 if (dEmax < highValues[i]) {
215 highValues[i] = dEmax;
217 if (lowValues[i] > Ei) {
220 if (highValues[i] > Ei) {
224 double ki = std::sqrt(energyToK * Ei);
225 kfmin = std::sqrt(energyToK * (Ei - lowValues[i]));
226 kfmax = std::sqrt(energyToK * (Ei - highValues[i]));
227 qLabLow = qin * ki - qout * kfmin;
228 qLabHigh = qin * ki - qout * kfmax;
230 qSampleLow = gon * qLabLow;
231 qSampleHigh = gon * qLabHigh;
234 if (((qSampleLow.
X() < qxmin) && (qSampleHigh.
X() < qxmin)) ||
235 ((qSampleLow.
X() > qxmax) && (qSampleHigh.
X() > qxmax)) ||
236 ((qSampleLow.
Y() < qymin) && (qSampleHigh.
Y() < qymin)) ||
237 ((qSampleLow.
Y() > qymax) && (qSampleHigh.
Y() > qymax)) ||
238 ((qSampleLow.
Z() < qzmin) && (qSampleHigh.
Z() < qzmin)) ||
239 ((qSampleLow.
Z() > qzmax) && (qSampleHigh.
Z() > qzmax))) {
240 highValues[i] = lowValues[i];
244 if ((qxmin - qSampleLow.
X()) * (qxmin - qSampleHigh.
X()) < 0) {
245 double kfIntersection =
246 (qxmin - qSampleLow.
X()) * (kfmax - kfmin) / (qSampleHigh.
X() - qSampleLow.
X()) + kfmin;
248 kfIntersection = Ei - kfIntersection * kfIntersection / energyToK;
250 if ((qSampleLow.
X() < qxmin) && (lowValues[i] < kfIntersection)) {
251 lowValues[i] = kfIntersection;
253 if ((qSampleHigh.
X() < qxmin) && (highValues[i] > kfIntersection)) {
254 highValues[i] = kfIntersection;
258 if ((qxmax - qSampleLow.
X()) * (qxmax - qSampleHigh.
X()) < 0) {
259 double kfIntersection =
260 (qxmax - qSampleLow.
X()) * (kfmax - kfmin) / (qSampleHigh.
X() - qSampleLow.
X()) + kfmin;
262 kfIntersection = Ei - kfIntersection * kfIntersection / energyToK;
264 if ((qSampleLow.
X() > qxmax) && (lowValues[i] < kfIntersection)) {
265 lowValues[i] = kfIntersection;
267 if ((qSampleHigh.
X() > qxmax) && (highValues[i] > kfIntersection)) {
268 highValues[i] = kfIntersection;
272 if ((qymin - qSampleLow.
Y()) * (qymin - qSampleHigh.
Y()) < 0) {
273 double kfIntersection =
274 (qymin - qSampleLow.
Y()) * (kfmax - kfmin) / (qSampleHigh.
Y() - qSampleLow.
Y()) + kfmin;
276 kfIntersection = Ei - kfIntersection * kfIntersection / energyToK;
278 if ((qSampleLow.
Y() < qymin) && (lowValues[i] < kfIntersection)) {
279 lowValues[i] = kfIntersection;
281 if ((qSampleHigh.
Y() < qymin) && (highValues[i] > kfIntersection)) {
282 highValues[i] = kfIntersection;
286 if ((qymax - qSampleLow.
Y()) * (qymax - qSampleHigh.
Y()) < 0) {
287 double kfIntersection =
288 (qymax - qSampleLow.
Y()) * (kfmax - kfmin) / (qSampleHigh.
Y() - qSampleLow.
Y()) + kfmin;
290 kfIntersection = Ei - kfIntersection * kfIntersection / energyToK;
292 if ((qSampleLow.
Y() > qymax) && (lowValues[i] < kfIntersection)) {
293 lowValues[i] = kfIntersection;
295 if ((qSampleHigh.
Y() > qymax) && (highValues[i] > kfIntersection)) {
296 highValues[i] = kfIntersection;
300 if ((qzmin - qSampleLow.
Z()) * (qzmin - qSampleHigh.
Z()) < 0) {
301 double kfIntersection =
302 (qzmin - qSampleLow.
Z()) * (kfmax - kfmin) / (qSampleHigh.
Z() - qSampleLow.
Z()) + kfmin;
304 kfIntersection = Ei - kfIntersection * kfIntersection / energyToK;
306 if ((qSampleLow.
Z() < qzmin) && (lowValues[i] < kfIntersection)) {
307 lowValues[i] = kfIntersection;
309 if ((qSampleHigh.
Z() < qzmin) && (highValues[i] > kfIntersection)) {
310 highValues[i] = kfIntersection;
314 if ((qzmax - qSampleLow.
Z()) * (qzmax - qSampleHigh.
Z()) < 0) {
315 double kfIntersection =
316 (qzmax - qSampleLow.
Z()) * (kfmax - kfmin) / (qSampleHigh.
Z() - qSampleLow.
Z()) + kfmin;
318 kfIntersection = Ei - kfIntersection * kfIntersection / energyToK;
320 if ((qSampleLow.
Z() > qzmax) && (lowValues[i] < kfIntersection)) {
321 lowValues[i] = kfIntersection;
323 if ((qSampleHigh.
Z() > qzmax) && (highValues[i] > kfIntersection)) {
324 highValues[i] = kfIntersection;
331 currentExptInfo.mutableRun().addProperty(
"MDNorm_low", lowValues,
true);
332 currentExptInfo.mutableRun().addProperty(
"MDNorm_high", highValues,
true);