/** * Takes a single valued histogram workspace and assesses which histograms are * within the limits. * Those that are not are masked on the input workspace. * @param countsWS :: Input/Output Integrated workspace to diagnose. * @param medianvec The median value calculated from the current counts. * @param indexmap Index map. * @param maskWS :: A mask workspace to apply. * @return The number of detectors that failed the tests, not including those * skipped. */ int MedianDetectorTest::doDetectorTests( const API::MatrixWorkspace_sptr countsWS, const std::vector<double> medianvec, std::vector<std::vector<size_t>> indexmap, API::MatrixWorkspace_sptr maskWS) { g_log.debug("Applying the criteria to find failing detectors"); // A spectra can't fail if the statistics show its value is consistent with // the mean value, // check the error and how many errorbars we are away const double minSigma = getProperty("SignificanceTest"); // prepare to report progress const int numSpec(m_maxWsIndex - m_minWsIndex); const int progStep = static_cast<int>(ceil(numSpec / 30.0)); int steps(0); const double deadValue(1.0); int numFailed(0); bool checkForMask = false; Geometry::Instrument_const_sptr instrument = countsWS->getInstrument(); if (instrument != nullptr) { checkForMask = ((instrument->getSource() != nullptr) && (instrument->getSample() != nullptr)); } PARALLEL_FOR_IF(Kernel::threadSafe(*countsWS, *maskWS)) for (int j = 0; j < static_cast<int>(indexmap.size()); ++j) { std::vector<size_t> hists = indexmap.at(j); double median = medianvec.at(j); const size_t nhist = hists.size(); g_log.debug() << "new component with " << nhist << " spectra.\n"; for (size_t i = 0; i < nhist; ++i) { g_log.debug() << "Counts workspace index=" << i << ", Mask workspace index=" << hists.at(i) << '\n'; PARALLEL_START_INTERUPT_REGION ++steps; // update the progressbar information if (steps % progStep == 0) { progress(advanceProgress(progStep * static_cast<double>(RTMarkDetects) / numSpec)); } if (checkForMask) { const auto &detids = countsWS->getSpectrum(hists.at(i)).getDetectorIDs(); if (instrument->isDetectorMasked(detids)) { maskWS->mutableY(hists.at(i))[0] = deadValue; continue; } if (instrument->isMonitor(detids)) { // Don't include in calculation but don't mask it continue; } } const double signal = countsWS->y(hists.at(i))[0]; // Mask out NaN and infinite if (!std::isfinite(signal)) { maskWS->mutableY(hists.at(i))[0] = deadValue; PARALLEL_ATOMIC ++numFailed; continue; } const double error = minSigma * countsWS->e(hists.at(i))[0]; if ((signal < median * m_loFrac && (signal - median < -error)) || (signal > median * m_hiFrac && (signal - median > error))) { maskWS->mutableY(hists.at(i))[0] = deadValue; PARALLEL_ATOMIC ++numFailed; } PARALLEL_END_INTERUPT_REGION } PARALLEL_CHECK_INTERUPT_REGION // Log finds g_log.information() << numFailed << " spectra failed the median tests.\n"; } return numFailed; }
/** Forms the quadrature phase signal (squashogram) * @param ws :: [input] workspace containing the measured spectra * @param phase :: [input] table workspace containing the detector phases * @param n0 :: [input] vector containing the normalization constants * @return :: workspace containing the quadrature phase signal */ API::MatrixWorkspace_sptr PhaseQuadMuon::squash(const API::MatrixWorkspace_sptr &ws, const API::ITableWorkspace_sptr &phase, const std::vector<double> &n0) { // Poisson limit: below this number we consider we don't have enough // statistics // to apply sqrt(N). This is an arbitrary number used in the original code // provided by scientists const double poissonLimit = 30.; // Muon life time in microseconds const double muLife = PhysicalConstants::MuonLifetime * 1e6; const size_t nspec = ws->getNumberHistograms(); if (n0.size() != nspec) { throw std::invalid_argument("Invalid normalization constants"); } auto names = phase->getColumnNames(); for (auto &name : names) { std::transform(name.begin(), name.end(), name.begin(), ::tolower); } auto phaseIndex = findName(phaseNames, names); auto asymmetryIndex = findName(asymmNames, names); // Get the maximum asymmetry double maxAsym = 0.; for (size_t h = 0; h < nspec; h++) { if (phase->Double(h, asymmetryIndex) > maxAsym && phase->Double(h, asymmetryIndex) != ASYMM_ERROR) { maxAsym = phase->Double(h, asymmetryIndex); } } if (maxAsym == 0.0) { throw std::invalid_argument("Invalid detector asymmetries"); } std::vector<bool> emptySpectrum; emptySpectrum.reserve(nspec); std::vector<double> aj, bj; { // Calculate coefficients aj, bj double sxx = 0.; double syy = 0.; double sxy = 0.; for (size_t h = 0; h < nspec; h++) { emptySpectrum.push_back( std::all_of(ws->y(h).begin(), ws->y(h).end(), [](double value) { return value == 0.; })); if (!emptySpectrum[h]) { const double asym = phase->Double(h, asymmetryIndex) / maxAsym; const double phi = phase->Double(h, phaseIndex); const double X = n0[h] * asym * cos(phi); const double Y = n0[h] * asym * sin(phi); sxx += X * X; syy += Y * Y; sxy += X * Y; } } const double lam1 = 2 * syy / (sxx * syy - sxy * sxy); const double mu1 = 2 * sxy / (sxy * sxy - sxx * syy); const double lam2 = 2 * sxy / (sxy * sxy - sxx * syy); const double mu2 = 2 * sxx / (sxx * syy - sxy * sxy); for (size_t h = 0; h < nspec; h++) { if (emptySpectrum[h]) { aj.push_back(0.0); bj.push_back(0.0); } else { const double asym = phase->Double(h, asymmetryIndex) / maxAsym; const double phi = phase->Double(h, phaseIndex); const double X = n0[h] * asym * cos(phi); const double Y = n0[h] * asym * sin(phi); aj.push_back((lam1 * X + mu1 * Y) * 0.5); bj.push_back((lam2 * X + mu2 * Y) * 0.5); } } } const size_t npoints = ws->blocksize(); // Create and populate output workspace API::MatrixWorkspace_sptr ows = API::WorkspaceFactory::Instance().create(ws, 2, npoints + 1, npoints); // X ows->setSharedX(0, ws->sharedX(0)); ows->setSharedX(1, ws->sharedX(0)); // Phase quadrature auto &realY = ows->mutableY(0); auto &imagY = ows->mutableY(1); auto &realE = ows->mutableE(0); auto &imagE = ows->mutableE(1); const auto xPointData = ws->histogram(0).points(); // First X value const double X0 = xPointData.front(); // calculate exponential decay outside of the loop std::vector<double> expDecay = xPointData.rawData(); std::transform(expDecay.begin(), expDecay.end(), expDecay.begin(), [X0, muLife](double x) { return exp(-(x - X0) / muLife); }); for (size_t i = 0; i < npoints; i++) { for (size_t h = 0; h < nspec; h++) { if (!emptySpectrum[h]) { // (X,Y,E) with exponential decay removed const double X = ws->x(h)[i]; const double exponential = n0[h] * exp(-(X - X0) / muLife); const double Y = ws->y(h)[i] - exponential; const double E = (ws->y(h)[i] > poissonLimit) ? ws->e(h)[i] : sqrt(exponential); realY[i] += aj[h] * Y; imagY[i] += bj[h] * Y; realE[i] += aj[h] * aj[h] * E * E; imagE[i] += bj[h] * bj[h] * E * E; } } realE[i] = sqrt(realE[i]); imagE[i] = sqrt(imagE[i]); // Regain exponential decay realY[i] /= expDecay[i]; imagY[i] /= expDecay[i]; realE[i] /= expDecay[i]; imagE[i] /= expDecay[i]; } // New Y axis label ows->setYUnit("Asymmetry"); return ows; }
/** Forms the quadrature phase signal (squashogram) * @param ws :: [input] workspace containing the measured spectra * @param phase :: [input] table workspace containing the detector phases * @param n0 :: [input] vector containing the normalization constants * @return :: workspace containing the quadrature phase signal */ API::MatrixWorkspace_sptr PhaseQuadMuon::squash(const API::MatrixWorkspace_sptr &ws, const API::ITableWorkspace_sptr &phase, const std::vector<double> &n0) { // Poisson limit: below this number we consider we don't have enough // statistics // to apply sqrt(N). This is an arbitrary number used in the original code // provided by scientists double poissonLimit = 30.; size_t nspec = ws->getNumberHistograms(); size_t npoints = ws->blocksize(); // Muon life time in microseconds double muLife = PhysicalConstants::MuonLifetime * 1e6; if (n0.size() != nspec) { throw std::invalid_argument("Invalid normalization constants"); } // Get the maximum asymmetry double maxAsym = 0.; for (size_t h = 0; h < nspec; h++) { if (phase->Double(h, 1) > maxAsym) { maxAsym = phase->Double(h, 1); } } if (maxAsym == 0.0) { throw std::invalid_argument("Invalid detector asymmetries"); } std::vector<double> aj, bj; { // Calculate coefficients aj, bj double sxx = 0; double syy = 0; double sxy = 0; for (size_t h = 0; h < nspec; h++) { double asym = phase->Double(h, 1) / maxAsym; double phi = phase->Double(h, 2); double X = n0[h] * asym * cos(phi); double Y = n0[h] * asym * sin(phi); sxx += X * X; syy += Y * Y; sxy += X * Y; } double lam1 = 2 * syy / (sxx * syy - sxy * sxy); double mu1 = 2 * sxy / (sxy * sxy - sxx * syy); double lam2 = 2 * sxy / (sxy * sxy - sxx * syy); double mu2 = 2 * sxx / (sxx * syy - sxy * sxy); for (size_t h = 0; h < nspec; h++) { double asym = phase->Double(h, 1) / maxAsym; double phi = phase->Double(h, 2); double X = n0[h] * asym * cos(phi); double Y = n0[h] * asym * sin(phi); aj.push_back((lam1 * X + mu1 * Y) * 0.5); bj.push_back((lam2 * X + mu2 * Y) * 0.5); } } // First X value double X0 = ws->x(0).front(); // Create and populate output workspace API::MatrixWorkspace_sptr ows = API::WorkspaceFactory::Instance().create( "Workspace2D", 2, npoints + 1, npoints); // X ows->setSharedX(0, ws->sharedX(0)); ows->setSharedX(1, ws->sharedX(0)); // Phase quadrature auto &realY = ows->mutableY(0); auto &imagY = ows->mutableY(1); auto &realE = ows->mutableE(0); auto &imagE = ows->mutableE(1); for (size_t i = 0; i < npoints; i++) { for (size_t h = 0; h < nspec; h++) { // (X,Y,E) with exponential decay removed const double X = ws->x(h)[i]; const double Y = ws->y(h)[i] - n0[h] * exp(-(X - X0) / muLife); const double E = (ws->y(h)[i] > poissonLimit) ? ws->e(h)[i] : sqrt(n0[h] * exp(-(X - X0) / muLife)); realY[i] += aj[h] * Y; imagY[i] += bj[h] * Y; realE[i] += aj[h] * aj[h] * E * E; imagE[i] += bj[h] * bj[h] * E * E; } realE[i] = sqrt(realE[i]); imagE[i] = sqrt(imagE[i]); // Regain exponential decay const double X = ws->getSpectrum(0).x()[i]; const double e = exp(-(X - X0) / muLife); realY[i] /= e; imagY[i] /= e; realE[i] /= e; imagE[i] /= e; } return ows; }
/** Carries out the bin-by-bin normalization * @param inputWorkspace The input workspace * @param outputWorkspace The result workspace */ void NormaliseToMonitor::normaliseBinByBin( const API::MatrixWorkspace_sptr &inputWorkspace, API::MatrixWorkspace_sptr &outputWorkspace) { EventWorkspace_sptr inputEvent = boost::dynamic_pointer_cast<EventWorkspace>(inputWorkspace); // Only create output workspace if different to input one if (outputWorkspace != inputWorkspace) { if (inputEvent) { outputWorkspace = inputWorkspace->clone(); } else outputWorkspace = WorkspaceFactory::Instance().create(inputWorkspace); } auto outputEvent = boost::dynamic_pointer_cast<EventWorkspace>(outputWorkspace); // Get hold of the monitor spectrum const auto &monX = m_monitor->binEdges(0); auto monY = m_monitor->counts(0); auto monE = m_monitor->countStandardDeviations(0); // Calculate the overall normalization just the once if bins are all matching if (m_commonBins) this->normalisationFactor(monX, monY, monE); const size_t numHists = inputWorkspace->getNumberHistograms(); auto specLength = inputWorkspace->blocksize(); // Flag set when a division by 0 is found bool hasZeroDivision = false; Progress prog(this, 0.0, 1.0, numHists); // Loop over spectra PARALLEL_FOR_IF( Kernel::threadSafe(*inputWorkspace, *outputWorkspace, *m_monitor)) for (int64_t i = 0; i < int64_t(numHists); ++i) { PARALLEL_START_INTERUPT_REGION prog.report(); const auto &X = inputWorkspace->binEdges(i); // If not rebinning, just point to our monitor spectra, otherwise create new // vectors auto Y = (m_commonBins ? monY : Counts(specLength)); auto E = (m_commonBins ? monE : CountStandardDeviations(specLength)); if (!m_commonBins) { // ConvertUnits can give X vectors of all zeros - skip these, they cause // problems if (X.back() == 0.0 && X.front() == 0.0) continue; // Rebin the monitor spectrum to match the binning of the current data // spectrum VectorHelper::rebinHistogram( monX.rawData(), monY.mutableRawData(), monE.mutableRawData(), X.rawData(), Y.mutableRawData(), E.mutableRawData(), false); // Recalculate the overall normalization factor this->normalisationFactor(X, Y, E); } if (inputEvent) { // ----------------------------------- EventWorkspace // --------------------------------------- EventList &outEL = outputEvent->getSpectrum(i); outEL.divide(X.rawData(), Y.mutableRawData(), E.mutableRawData()); } else { // ----------------------------------- Workspace2D // --------------------------------------- auto &YOut = outputWorkspace->mutableY(i); auto &EOut = outputWorkspace->mutableE(i); const auto &inY = inputWorkspace->y(i); const auto &inE = inputWorkspace->e(i); outputWorkspace->mutableX(i) = inputWorkspace->x(i); // The code below comes more or less straight out of Divide.cpp for (size_t k = 0; k < specLength; ++k) { // Get the input Y's const double leftY = inY[k]; const double rightY = Y[k]; if (rightY == 0.0) { hasZeroDivision = true; } // Calculate result and store in local variable to avoid overwriting // original data if // output workspace is same as one of the input ones const double newY = leftY / rightY; if (fabs(rightY) > 1.0e-12 && fabs(newY) > 1.0e-12) { const double lhsFactor = (inE[k] < 1.0e-12 || fabs(leftY) < 1.0e-12) ? 0.0 : pow((inE[k] / leftY), 2); const double rhsFactor = E[k] < 1.0e-12 ? 0.0 : pow((E[k] / rightY), 2); EOut[k] = std::abs(newY) * sqrt(lhsFactor + rhsFactor); } // Now store the result YOut[k] = newY; } // end Workspace2D case } // end loop over current spectrum PARALLEL_END_INTERUPT_REGION } // end loop over spectra PARALLEL_CHECK_INTERUPT_REGION if (hasZeroDivision) { g_log.warning() << "Division by zero in some of the bins.\n"; } }